diff --git a/.github/ISSUE_TEMPLATE/build.md b/.github/ISSUE_TEMPLATE/build.md index 6c565b95d6..71048873dd 100644 --- a/.github/ISSUE_TEMPLATE/build.md +++ b/.github/ISSUE_TEMPLATE/build.md @@ -9,6 +9,9 @@ They're important. --> ### What platform, compiler, and build tools? +### What compile flags or command did you run specifically? + + ### What happens (don't forget logs)? diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 981f938ebc..f94d19d932 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,10 @@ -# libretro CI +# DESCRIPTION: GitLab CI/CD for libRetro (NOT FOR GitLab-proper) + +############################################################################## +################################# BOILERPLATE ################################ +############################################################################## + +# Core definitions variables: GIT_SUBMODULE_STRATEGY: normal @@ -7,69 +13,107 @@ variables: CORENAME: ppsspp CORE_ARGS: -DLIBRETRO=ON -.linux-defs: +.cmake-defs: variables: EXTRA_PATH: lib -.windows-defs: +.make-defs: variables: MAKEFILE_PATH: libretro +# Inclusion templates, required for the build to work include: - - project: 'libretro-infrastructure/ci-templates' - file: '/android-cmake.yml' - - project: 'libretro-infrastructure/ci-templates' - file: '/linux-cmake.yml' + ################################## DESKTOPS ################################ + # Windows 64bit - project: 'libretro-infrastructure/ci-templates' file: '/windows-x64-msvc19-msys2.yml' + + # Windows 32bit - project: 'libretro-infrastructure/ci-templates' file: '/windows-i686-msvc19-msys2.yml' + # Linux + - project: 'libretro-infrastructure/ci-templates' + file: '/linux-cmake.yml' + + # MacOS 64-bit + - project: 'libretro-infrastructure/ci-templates' + file: '/osx-x64.yml' + + ################################## CELLULAR ################################ + # Android + - project: 'libretro-infrastructure/ci-templates' + file: '/android-cmake.yml' + + ################################## CONSOLES ################################ + + #################################### MISC ################################## + +# Stages for building stages: - build-prepare - build-shared -# Desktop -libretro-build-linux-x64: - extends: - - .libretro-linux-cmake-x86_64 - - .core-defs - - .linux-defs - -libretro-build-linux-i686: - extends: - - .libretro-linux-cmake-x86 - - .core-defs - - .linux-defs - +############################################################################## +#################################### STAGES ################################## +############################################################################## +# +################################### DESKTOPS ################################# +# Windows 64-bit libretro-build-windows-x64: extends: - .libretro-windows-x64-msvc19-msys2-make-default - .core-defs - - .windows-defs + - .make-defs +# Windows 32-bit libretro-build-windows-i686: extends: - .libretro-windows-i686-msvc19-msys2-make-default - .core-defs - - .windows-defs + - .make-defs -# Android +# Linux 64-bit +libretro-build-linux-x64: + extends: + - .libretro-linux-cmake-x86_64 + - .core-defs + - .cmake-defs + +# Linux 32-bit +libretro-build-linux-i686: + extends: + - .libretro-linux-cmake-x86 + - .core-defs + - .cmake-defs + +# MacOS 64-bit +libretro-build-osx-x64: + extends: + - .libretro-osx-x64-make-default + - .core-defs + - .make-defs + +################################### CELLULAR ################################# +# Android ARMv7a libretro-build-android-armeabi-v7a: extends: - .libretro-android-cmake-armeabi-v7a - .core-defs +# Android ARMv8a libretro-build-android-arm64-v8a: extends: - .libretro-android-cmake-arm64-v8a - .core-defs +# Android 64-bit x86 libretro-build-android-x86_64: extends: - .libretro-android-cmake-x86_64 - .core-defs +# Android 32-bit x86 libretro-build-android-x86: extends: - .libretro-android-cmake-x86 diff --git a/.gitmodules b/.gitmodules index 5d8992a220..49aabf6745 100644 --- a/.gitmodules +++ b/.gitmodules @@ -34,3 +34,6 @@ [submodule "ext/miniupnp"] path = ext/miniupnp url = https://github.com/hrydgard/miniupnp.git +[submodule "zstd"] + path = ext/zstd + url = https://github.com/facebook/zstd.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 01cc70d475..a3f94cf692 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -400,7 +400,7 @@ set(CommonARM Common/ArmCPUDetect.cpp Common/ArmEmitter.h Common/ArmEmitter.cpp - Common/ColorConvNEON.cpp + Common/Data/Convert/ColorConvNEON.cpp ) source_group(ARM FILES ${CommonARM}) @@ -420,14 +420,6 @@ set(CommonMIPS ) source_group(MIPS FILES ${CommonMIPS}) -if(NOT (X86 OR ARM OR MIPS)) - set(CommonFake - Common/FakeCPUDetect.cpp - Common/FakeEmitter.h - ) - source_group(Fake FILES ${CommonFake}) -endif() - if(WIN32) set(CommonD3D Common/GPU/D3D9/D3D9ShaderCompiler.cpp @@ -447,7 +439,6 @@ add_library(Common STATIC ${CommonARM} ${CommonARM64} ${CommonMIPS} - ${CommonFake} ${CommonD3D} Common/Serialize/Serializer.cpp Common/Serialize/Serializer.h @@ -469,6 +460,8 @@ add_library(Common STATIC Common/Data/Collections/ThreadSafeList.h Common/Data/Color/RGBAUtil.cpp Common/Data/Color/RGBAUtil.h + Common/Data/Convert/ColorConv.cpp + Common/Data/Convert/ColorConv.h Common/Data/Convert/SmallDataConvert.cpp Common/Data/Convert/SmallDataConvert.h Common/Data/Encoding/Base64.cpp @@ -508,6 +501,8 @@ add_library(Common STATIC Common/File/VFS/AssetReader.h Common/File/DiskFree.h Common/File/DiskFree.cpp + Common/File/Path.h + Common/File/Path.cpp Common/File/PathBrowser.h Common/File/PathBrowser.cpp Common/File/FileUtil.cpp @@ -582,6 +577,8 @@ add_library(Common STATIC Common/Net/HTTPHeaders.h Common/Net/HTTPServer.cpp Common/Net/HTTPServer.h + Common/Net/NetBuffer.cpp + Common/Net/NetBuffer.h Common/Net/Resolve.cpp Common/Net/Resolve.h Common/Net/Sinks.cpp @@ -606,8 +603,6 @@ add_library(Common STATIC Common/Render/Text/draw_text_uwp.h Common/System/Display.cpp Common/System/Display.h - Common/Thread/Executor.cpp - Common/Thread/Executor.h Common/Thread/PrioritizedWorkQueue.cpp Common/Thread/PrioritizedWorkQueue.h Common/Thread/ThreadUtil.cpp @@ -635,8 +630,6 @@ add_library(Common STATIC Common/Buffer.h Common/Buffer.cpp Common/CodeBlock.h - Common/ColorConv.cpp - Common/ColorConv.h Common/Common.h Common/CommonFuncs.h Common/CommonTypes.h @@ -644,6 +637,7 @@ add_library(Common STATIC Common/ConsoleListener.h Common/DbgNew.h Common/FakeEmitter.h + Common/FakeCPUDetect.cpp Common/ExceptionHandlerSetup.cpp Common/ExceptionHandlerSetup.h Common/Log.h @@ -792,62 +786,135 @@ if(LIBZIP_FOUND AND USE_SYSTEM_LIBZIP) add_definitions(-DSHARED_LIBZIP) else() add_library(libzip STATIC - ext/libzip/zip.h - ext/libzip/mkstemp.c ext/libzip/zip_add.c ext/libzip/zip_add_dir.c + ext/libzip/zip_add_entry.c + ext/libzip/zip_algorithm_deflate.c + ext/libzip/zip_buffer.c ext/libzip/zip_close.c ext/libzip/zip_delete.c + ext/libzip/zip_dir_add.c ext/libzip/zip_dirent.c - ext/libzip/zip_entry_free.c - ext/libzip/zip_entry_new.c - ext/libzip/zip_err_str.c + ext/libzip/zip_discard.c + ext/libzip/zip_entry.c ext/libzip/zip_error.c ext/libzip/zip_error_clear.c ext/libzip/zip_error_get.c ext/libzip/zip_error_get_sys_type.c ext/libzip/zip_error_strerror.c ext/libzip/zip_error_to_str.c + ext/libzip/zip_extra_field.c + ext/libzip/zip_extra_field_api.c ext/libzip/zip_fclose.c + ext/libzip/zip_fdopen.c + ext/libzip/zip_file_add.c ext/libzip/zip_file_error_clear.c ext/libzip/zip_file_error_get.c + ext/libzip/zip_file_get_comment.c + ext/libzip/zip_file_get_external_attributes.c ext/libzip/zip_file_get_offset.c + ext/libzip/zip_file_rename.c + ext/libzip/zip_file_replace.c + ext/libzip/zip_file_set_comment.c + ext/libzip/zip_file_set_encryption.c + ext/libzip/zip_file_set_external_attributes.c + ext/libzip/zip_file_set_mtime.c ext/libzip/zip_file_strerror.c - ext/libzip/zip_filerange_crc.c ext/libzip/zip_fopen.c + ext/libzip/zip_fopen_encrypted.c ext/libzip/zip_fopen_index.c + ext/libzip/zip_fopen_index_encrypted.c ext/libzip/zip_fread.c - ext/libzip/zip_free.c + ext/libzip/zip_fseek.c + ext/libzip/zip_ftell.c ext/libzip/zip_get_archive_comment.c ext/libzip/zip_get_archive_flag.c + ext/libzip/zip_get_encryption_implementation.c ext/libzip/zip_get_file_comment.c ext/libzip/zip_get_name.c + ext/libzip/zip_get_num_entries.c ext/libzip/zip_get_num_files.c + ext/libzip/zip_hash.c + ext/libzip/zip_io_util.c + ext/libzip/zip_libzip_version.c ext/libzip/zip_memdup.c ext/libzip/zip_name_locate.c ext/libzip/zip_new.c ext/libzip/zip_open.c + ext/libzip/zip_pkware.c + ext/libzip/zip_progress.c ext/libzip/zip_rename.c ext/libzip/zip_replace.c ext/libzip/zip_set_archive_comment.c ext/libzip/zip_set_archive_flag.c + ext/libzip/zip_set_default_password.c ext/libzip/zip_set_file_comment.c + ext/libzip/zip_set_file_compression.c ext/libzip/zip_set_name.c + ext/libzip/zip_source_accept_empty.c + ext/libzip/zip_source_begin_write.c + ext/libzip/zip_source_begin_write_cloning.c ext/libzip/zip_source_buffer.c - ext/libzip/zip_source_file.c - ext/libzip/zip_source_filep.c + ext/libzip/zip_source_call.c + ext/libzip/zip_source_close.c + ext/libzip/zip_source_commit_write.c + ext/libzip/zip_source_compress.c + ext/libzip/zip_source_crc.c + ext/libzip/zip_source_error.c + ext/libzip/zip_source_file_common.c + ext/libzip/zip_source_file_stdio.c ext/libzip/zip_source_free.c ext/libzip/zip_source_function.c + ext/libzip/zip_source_get_file_attributes.c + ext/libzip/zip_source_is_deleted.c + ext/libzip/zip_source_layered.c + ext/libzip/zip_source_open.c + ext/libzip/zip_source_pkware_decode.c + ext/libzip/zip_source_pkware_encode.c + ext/libzip/zip_source_read.c + ext/libzip/zip_source_remove.c + ext/libzip/zip_source_rollback_write.c + ext/libzip/zip_source_seek.c + ext/libzip/zip_source_seek_write.c + ext/libzip/zip_source_stat.c + ext/libzip/zip_source_supports.c + ext/libzip/zip_source_tell.c + ext/libzip/zip_source_tell_write.c + ext/libzip/zip_source_window.c + ext/libzip/zip_source_write.c ext/libzip/zip_source_zip.c + ext/libzip/zip_source_zip_new.c ext/libzip/zip_stat.c ext/libzip/zip_stat_index.c ext/libzip/zip_stat_init.c ext/libzip/zip_strerror.c + ext/libzip/zip_string.c ext/libzip/zip_unchange.c ext/libzip/zip_unchange_all.c ext/libzip/zip_unchange_archive.c ext/libzip/zip_unchange_data.c + ext/libzip/zip_utf-8.c + ext/libzip/zip_err_str.c ) + if(WIN32) + target_sources(libzip PRIVATE + ext/libzip/zip_source_file_win32.c + ext/libzip/zip_source_file_win32_named.c + ext/libzip/zip_source_file_win32_utf16.c + ext/libzip/zip_source_file_win32_utf8.c + ) + if(CMAKE_SYSTEM_NAME MATCHES WindowsPhone OR CMAKE_SYSTEM_NAME MATCHES WindowsStore) + target_sources(libzip PRIVATE ext/libzip/zip_random_uwp.c) + else() + target_sources(libzip PRIVATE ext/libzip/zip_source_file_win32_ansi.c ext/libzip/zip_random_win32.c) + endif() + else() + target_sources(libzip PRIVATE + ext/libzip/zip_mkstempm.c + ext/libzip/zip_source_file_stdio_named.c + ext/libzip/zip_random_unix.c + ) + endif() target_link_libraries(libzip) include_directories(ext/libzip) set(LIBZIP_LIBRARY libzip) @@ -1251,7 +1318,6 @@ list(APPEND CoreExtra list(APPEND CoreExtra Core/MIPS/MIPS/MipsJit.cpp Core/MIPS/MIPS/MipsJit.h - GPU/Common/VertexDecoderFake.cpp ) if(NOT MOBILE_DEVICE) @@ -1510,6 +1576,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/Debugger/WebSocket/InputSubscriber.h Core/Debugger/WebSocket/LogBroadcaster.cpp Core/Debugger/WebSocket/LogBroadcaster.h + Core/Debugger/WebSocket/MemoryInfoSubscriber.cpp + Core/Debugger/WebSocket/MemoryInfoSubscriber.h Core/Debugger/WebSocket/MemorySubscriber.cpp Core/Debugger/WebSocket/MemorySubscriber.h Core/Debugger/WebSocket/SteppingBroadcaster.cpp @@ -1824,7 +1892,8 @@ if(ANDROID) endif() endif() -set(CoreExtraLibs ${CoreExtraLibs} armips) +set(CoreExtraLibs ${CoreExtraLibs} armips libzstd_static) +include_directories(ext/zstd/lib) # needed for VK_USE_PLATFORM_XCB_KHR only #if(VULKAN AND NOT WIN32) @@ -1892,6 +1961,7 @@ if(USE_MINIUPNPC) configure_file(${MINIUPNP_DIR}/miniupnpcstrings.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/miniupnpcstrings.h) # by default miniupnp repo doesn't contains miniupnpcstrings.h and need to be generated set(MINIUPNPC_SOURCES # the needed bits of miniupnpc (no python module, no tests, no cli) + ${MINIUPNP_DIR}/addr_is_reserved.c ${MINIUPNP_DIR}/connecthostport.c ${MINIUPNP_DIR}/igd_desc_parse.c ${MINIUPNP_DIR}/minisoap.c diff --git a/Common/ArmCPUDetect.cpp b/Common/ArmCPUDetect.cpp index 0dd85591cc..3e59843e2b 100644 --- a/Common/ArmCPUDetect.cpp +++ b/Common/ArmCPUDetect.cpp @@ -74,10 +74,12 @@ const char procfile[] = "/proc/cpuinfo"; const char syscpupresentfile[] = "/sys/devices/system/cpu/present"; std::string GetCPUString() { + std::string procdata; + bool readSuccess = File::ReadFileToString(true, Path(procfile), procdata); + std::istringstream file(procdata); std::string cpu_string; - std::fstream file; - if (File::OpenCPPFile(file, procfile, std::ios::in)) { + if (readSuccess) { std::string line, marker = "Hardware\t: "; while (std::getline(file, line)) { if (line.find(marker) != std::string::npos) { @@ -95,10 +97,12 @@ std::string GetCPUString() { } std::string GetCPUBrandString() { + std::string procdata; + bool readSuccess = File::ReadFileToString(true, Path(procfile), procdata); + std::istringstream file(procdata); std::string brand_string; - std::fstream file; - if (File::OpenCPPFile(file, procfile, std::ios::in)) { + if (readSuccess) { std::string line, marker = "Processor\t: "; while (std::getline(file, line)) { if (line.find(marker) != std::string::npos) { @@ -122,10 +126,11 @@ unsigned char GetCPUImplementer() { std::string line, marker = "CPU implementer\t: "; unsigned char implementer = 0; - std::fstream file; - if (!File::OpenCPPFile(file, procfile, std::ios::in)) + std::string procdata; + if (!File::ReadFileToString(true, Path(procfile), procdata)) return 0; + std::istringstream file(procdata); while (std::getline(file, line)) { @@ -144,10 +149,11 @@ unsigned short GetCPUPart() { std::string line, marker = "CPU part\t: "; unsigned short part = 0; - std::fstream file; - if (!File::OpenCPPFile(file, procfile, std::ios::in)) + std::string procdata; + if (!File::ReadFileToString(true, Path(procfile), procdata)) return 0; + std::istringstream file(procdata); while (std::getline(file, line)) { @@ -165,11 +171,11 @@ unsigned short GetCPUPart() bool CheckCPUFeature(const std::string& feature) { std::string line, marker = "Features\t: "; - std::fstream file; - - if (!File::OpenCPPFile(file, procfile, std::ios::in)) - return 0; + std::string procdata; + if (!File::ReadFileToString(true, Path(procfile), procdata)) + return false; + std::istringstream file(procdata); while (std::getline(file, line)) { if (line.find(marker) != std::string::npos) @@ -191,12 +197,14 @@ int GetCoreCount() { std::string line, marker = "processor\t: "; int cores = 1; - std::fstream file; - if (File::OpenCPPFile(file, syscpupresentfile, std::ios::in)) - { + std::string presentData; + bool presentSuccess = File::ReadFileToString(true, Path(syscpupresentfile), presentData); + std::istringstream presentFile(presentData); + + if (presentSuccess) { int low, high, found; - std::getline(file, line); + std::getline(presentFile, line); found = sscanf(line.c_str(), "%d-%d", &low, &high); if (found == 1) return 1; @@ -204,8 +212,10 @@ int GetCoreCount() return high - low + 1; } - if (!File::OpenCPPFile(file, procfile, std::ios::in)) + std::string procdata; + if (!File::ReadFileToString(true, Path(procfile), procdata)) return 1; + std::istringstream file(procdata); while (std::getline(file, line)) { diff --git a/Common/Buffer.cpp b/Common/Buffer.cpp index 68c7e7682e..6dc6b74c5a 100644 --- a/Common/Buffer.cpp +++ b/Common/Buffer.cpp @@ -1,25 +1,10 @@ +#include +#include +#include -#include -#include -#include - -#ifdef _WIN32 -#include -#undef min -#undef max -#else -#include -#include -#endif - -#ifndef MSG_NOSIGNAL -// Default value to 0x00 (do nothing) in systems where it's not supported. -#define MSG_NOSIGNAL 0x00 -#endif - -#include "Common/File/FileDescriptor.h" -#include "Common/TimeUtil.h" #include "Common/Buffer.h" +#include "Common/File/FileUtil.h" +#include "Common/File/Path.h" #include "Common/Log.h" Buffer::Buffer() { } @@ -36,14 +21,14 @@ char *Buffer::Append(size_t length) { } void Buffer::Append(const std::string &str) { - char *ptr = Append(str.size()); - memcpy(ptr, str.data(), str.size()); + char *ptr = Append(str.size()); + memcpy(ptr, str.data(), str.size()); } void Buffer::Append(const char *str) { - size_t len = strlen(str); - char *dest = Append(len); - memcpy(dest, str, len); + size_t len = strlen(str); + char *dest = Append(len); + memcpy(dest, str, len); } void Buffer::Append(const Buffer &other) { @@ -55,10 +40,10 @@ void Buffer::Append(const Buffer &other) { } void Buffer::AppendValue(int value) { - char buf[16]; - // This is slow. - sprintf(buf, "%i", value); - Append(buf); + char buf[16]; + // This is slow. + sprintf(buf, "%i", value); + Append(buf); } void Buffer::Take(size_t length, std::string *dest) { @@ -78,14 +63,14 @@ void Buffer::Take(size_t length, char *dest) { } int Buffer::TakeLineCRLF(std::string *dest) { - int after_next_line = OffsetToAfterNextCRLF(); - if (after_next_line < 0) - return after_next_line; - else { - Take(after_next_line - 2, dest); - Skip(2); // Skip the CRLF - return after_next_line - 2; - } + int after_next_line = OffsetToAfterNextCRLF(); + if (after_next_line < 0) { + return after_next_line; + } else { + Take(after_next_line - 2, dest); + Skip(2); // Skip the CRLF + return after_next_line - 2; + } } void Buffer::Skip(size_t length) { @@ -97,52 +82,43 @@ void Buffer::Skip(size_t length) { } int Buffer::SkipLineCRLF() { - int after_next_line = OffsetToAfterNextCRLF(); - if (after_next_line < 0) - return after_next_line; - else { - Skip(after_next_line); - return after_next_line - 2; - } + int after_next_line = OffsetToAfterNextCRLF(); + if (after_next_line < 0) { + return after_next_line; + } else { + Skip(after_next_line); + return after_next_line - 2; + } } int Buffer::OffsetToAfterNextCRLF() { - for (int i = 0; i < (int)data_.size() - 1; i++) { - if (data_[i] == '\r' && data_[i + 1] == '\n') { - return i + 2; - } - } - return -1; + for (int i = 0; i < (int)data_.size() - 1; i++) { + if (data_[i] == '\r' && data_[i + 1] == '\n') { + return i + 2; + } + } + return -1; } void Buffer::Printf(const char *fmt, ...) { - char buffer[2048]; - va_list vl; - va_start(vl, fmt); - size_t retval = vsnprintf(buffer, sizeof(buffer), fmt, vl); - if ((int)retval >= (int)sizeof(buffer)) { - // Output was truncated. TODO: Do something. - ERROR_LOG(IO, "Buffer::Printf truncated output"); - } - if (retval < 0) { - ERROR_LOG(IO, "Buffer::Printf failed"); - } - va_end(vl); - char *ptr = Append(retval); - memcpy(ptr, buffer, retval); + char buffer[2048]; + va_list vl; + va_start(vl, fmt); + size_t retval = vsnprintf(buffer, sizeof(buffer), fmt, vl); + if ((int)retval >= (int)sizeof(buffer)) { + // Output was truncated. TODO: Do something. + ERROR_LOG(IO, "Buffer::Printf truncated output"); + } + if (retval < 0) { + ERROR_LOG(IO, "Buffer::Printf failed"); + } + va_end(vl); + char *ptr = Append(retval); + memcpy(ptr, buffer, retval); } -bool Buffer::Flush(int fd) { - // Look into using send() directly. - bool success = data_.size() == fd_util::WriteLine(fd, &data_[0], data_.size()); - if (success) { - data_.resize(0); - } - return success; -} - -bool Buffer::FlushToFile(const char *filename) { - FILE *f = fopen(filename, "wb"); +bool Buffer::FlushToFile(const Path &filename) { + FILE *f = File::OpenCFile(filename, "wb"); if (!f) return false; if (data_.size()) { @@ -152,116 +128,6 @@ bool Buffer::FlushToFile(const char *filename) { return true; } -bool Buffer::FlushSocket(uintptr_t sock, double timeout, bool *cancelled) { - static constexpr float CANCEL_INTERVAL = 0.25f; - for (size_t pos = 0, end = data_.size(); pos < end; ) { - bool ready = false; - double leftTimeout = timeout; - while (!ready && (leftTimeout >= 0 || cancelled)) { - if (cancelled && *cancelled) - return false; - ready = fd_util::WaitUntilReady(sock, CANCEL_INTERVAL, true); - if (!ready && leftTimeout >= 0.0) { - leftTimeout -= CANCEL_INTERVAL; - if (leftTimeout < 0) { - ERROR_LOG(IO, "FlushSocket timed out"); - return false; - } - } - } - int sent = send(sock, &data_[pos], (int)(end - pos), MSG_NOSIGNAL); - if (sent < 0) { - ERROR_LOG(IO, "FlushSocket failed"); - return false; - } - pos += sent; - - // Buffer full, don't spin. - if (sent == 0 && timeout < 0.0) { - sleep_ms(1); - } - } - data_.resize(0); - return true; -} - -bool Buffer::ReadAll(int fd, int hintSize) { - std::vector buf; - if (hintSize >= 65536 * 16) { - buf.resize(65536); - } else if (hintSize >= 1024 * 16) { - buf.resize(hintSize / 16); - } else { - buf.resize(4096); - } - - while (true) { - int retval = recv(fd, &buf[0], (int)buf.size(), MSG_NOSIGNAL); - if (retval == 0) { - break; - } else if (retval < 0) { - ERROR_LOG(IO, "Error reading from buffer: %i", retval); - return false; - } - char *p = Append((size_t)retval); - memcpy(p, &buf[0], retval); - } - return true; -} - -bool Buffer::ReadAllWithProgress(int fd, int knownSize, float *progress, bool *cancelled) { - static constexpr float CANCEL_INTERVAL = 0.25f; - std::vector buf; - if (knownSize >= 65536 * 16) { - buf.resize(65536); - } else if (knownSize >= 1024 * 16) { - buf.resize(knownSize / 16); - } else { - buf.resize(1024); - } - - int total = 0; - while (true) { - bool ready = false; - while (!ready && cancelled) { - if (*cancelled) - return false; - ready = fd_util::WaitUntilReady(fd, CANCEL_INTERVAL, false); - } - int retval = recv(fd, &buf[0], (int)buf.size(), MSG_NOSIGNAL); - if (retval == 0) { - return true; - } else if (retval < 0) { - ERROR_LOG(IO, "Error reading from buffer: %i", retval); - return false; - } - char *p = Append((size_t)retval); - memcpy(p, &buf[0], retval); - total += retval; - if (progress) - *progress = (float)total / (float)knownSize; - } - return true; -} - -int Buffer::Read(int fd, size_t sz) { - char buf[1024]; - int retval; - size_t received = 0; - while ((retval = recv(fd, buf, (int)std::min(sz, sizeof(buf)), MSG_NOSIGNAL)) > 0) { - if (retval < 0) { - return retval; - } - char *p = Append((size_t)retval); - memcpy(p, buf, retval); - sz -= retval; - received += retval; - if (sz == 0) - return 0; - } - return (int)received; -} - void Buffer::PeekAll(std::string *dest) { dest->resize(data_.size()); memcpy(&(*dest)[0], &data_[0], data_.size()); diff --git a/Common/Buffer.h b/Common/Buffer.h index 3fad61d549..4d63a8e5ad 100644 --- a/Common/Buffer.h +++ b/Common/Buffer.h @@ -5,79 +5,82 @@ #include "Common/Common.h" +class Path; + // Acts as a queue. Intended to be as fast as possible for most uses. // Does not do synchronization, must use external mutexes. class Buffer { - public: - Buffer(); - ~Buffer(); +public: + Buffer(); + Buffer(Buffer &&) = default; + ~Buffer(); - // Write max [length] bytes to the returned pointer. - // Any other operation on this Buffer invalidates the pointer. - char *Append(size_t length); + static Buffer Void() { + Buffer buf; + buf.void_ = true; + return buf; + } + + // Write max [length] bytes to the returned pointer. + // Any other operation on this Buffer invalidates the pointer. + char *Append(size_t length); // These work pretty much like you'd expect. void Append(const char *str); // str null-terminated. The null is not copied. void Append(const std::string &str); void Append(const Buffer &other); - // Various types. Useful for varz etc. Appends a string representation of the - // value, rather than a binary representation. - void AppendValue(int value); + // Various types. Useful for varz etc. Appends a string representation of the + // value, rather than a binary representation. + void AppendValue(int value); - // Parsing Helpers - - // Use for easy line skipping. If no CRLF within the buffer, returns -1. - // If parsing HTML headers, this indicates that you should probably buffer up - // more data. - int OffsetToAfterNextCRLF(); + // Parsing Helpers - // Takers + // Use for easy line skipping. If no CRLF within the buffer, returns -1. + // If parsing HTML headers, this indicates that you should probably buffer up + // more data. + int OffsetToAfterNextCRLF(); - void Take(size_t length, std::string *dest); - void Take(size_t length, char *dest); - void TakeAll(std::string *dest) { Take(size(), dest); } - // On failure, return value < 0 and *dest is unchanged. - // Strips off the actual CRLF from the result. - int TakeLineCRLF(std::string *dest); + // Takers - // Skippers - void Skip(size_t length); - // Returns -1 on failure (no CRLF within sight). - // Otherwise returns the length of the line skipped, not including CRLF. Can be 0. - int SkipLineCRLF(); + void Take(size_t length, std::string *dest); + void Take(size_t length, char *dest); + void TakeAll(std::string *dest) { Take(size(), dest); } + // On failure, return value < 0 and *dest is unchanged. + // Strips off the actual CRLF from the result. + int TakeLineCRLF(std::string *dest); - // Utility functions. - void Printf(const char *fmt, ...); + // Skippers + void Skip(size_t length); + // Returns -1 on failure (no CRLF within sight). + // Otherwise returns the length of the line skipped, not including CRLF. Can be 0. + int SkipLineCRLF(); - // Dumps the entire buffer to the string, but keeps it around. - // Only to be used for debugging, since it might not be fast at all. - void PeekAll(std::string *dest); + // Utility functions. + void Printf(const char *fmt, ...); - // Simple I/O. + // Dumps the entire buffer to the string, but keeps it around. + // Only to be used for debugging, since it might not be fast at all. + void PeekAll(std::string *dest); + + // Simple I/O. // Writes the entire buffer to the file descriptor. Also resets the // size to zero. On failure, data remains in buffer and nothing is // written. - bool Flush(int fd); - bool FlushToFile(const char *filename); - bool FlushSocket(uintptr_t sock, double timeout = -1.0, bool *cancelled = nullptr); // Windows portability + bool FlushToFile(const Path &filename); - bool ReadAll(int fd, int hintSize = 0); - bool ReadAllWithProgress(int fd, int knownSize, float *progress, bool *cancelled); + // Utilities. Try to avoid checking for size. + size_t size() const { return data_.size(); } + bool empty() const { return size() == 0; } + void clear() { data_.resize(0); } + bool IsVoid() { return void_; } - // < 0: error - // >= 0: number of bytes read - int Read(int fd, size_t sz); +protected: + // TODO: Find a better internal representation, like a cord. + std::vector data_; + bool void_ = false; - // Utilities. Try to avoid checking for size. - size_t size() const { return data_.size(); } - bool empty() const { return size() == 0; } - void clear() { data_.resize(0); } - - private: - // TODO: Find a better internal representation, like a cord. - std::vector data_; - - DISALLOW_COPY_AND_ASSIGN(Buffer); +private: + DISALLOW_COPY_AND_ASSIGN(Buffer); }; diff --git a/Common/CPUDetect.cpp b/Common/CPUDetect.cpp index 96c9b140e8..01a4d06cf4 100644 --- a/Common/CPUDetect.cpp +++ b/Common/CPUDetect.cpp @@ -26,6 +26,7 @@ #include #endif +#include #include #include #include @@ -119,7 +120,7 @@ static std::vector ParseCPUList(const std::string &filename) { std::string data; std::vector results; - if (readFileToString(true, filename.c_str(), data)) { + if (File::ReadFileToString(true, Path(filename), data)) { std::vector ranges; SplitString(data, ',', ranges); for (auto range : ranges) { @@ -369,9 +370,9 @@ void CPUInfo::Detect() { } // This seems to be the count per core. Hopefully all cores are the same, but we counted each above. - logical_cpu_count /= num_cores; + logical_cpu_count /= std::max(num_cores, 1); #elif PPSSPP_PLATFORM(LINUX) - if (File::Exists("/sys/devices/system/cpu/present")) { + if (File::Exists(Path("/sys/devices/system/cpu/present"))) { // This may not count unplugged cores, but at least it's a best guess. // Also, this assumes the CPU cores are heterogeneous (e.g. all cores could be active simultaneously.) num_cores = 0; @@ -396,7 +397,7 @@ void CPUInfo::Detect() { } // This seems to be the count per core. Hopefully all cores are the same, but we counted each above. - logical_cpu_count /= num_cores; + logical_cpu_count /= std::max(num_cores, 1); #elif PPSSPP_PLATFORM(MAC) int num = 0; size_t sz = sizeof(num); @@ -404,10 +405,12 @@ void CPUInfo::Detect() { num_cores = num; sz = sizeof(num); if (sysctlbyname("hw.logicalcpu_max", &num, &sz, nullptr, 0) == 0) { - logical_cpu_count = num / num_cores; + logical_cpu_count = num / std::max(num_cores, 1); } } #endif + if (logical_cpu_count <= 0) + logical_cpu_count = 1; } // Turn the cpu info into a string we can show diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index d66d2ebac2..5e7647b71a 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -136,7 +136,7 @@ _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_32=1;_M_IX86=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) Common/DbgNew.h MultiThreadedDebug - ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;.. + ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib StreamingSIMDExtensions2 Precise true @@ -161,7 +161,7 @@ Common/DbgNew.h MultiThreadedDebug _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;_M_X64=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;.. + ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib NotSet Precise false @@ -187,7 +187,7 @@ Common/DbgNew.h MultiThreadedDebug _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;.. + ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib NotSet Precise false @@ -214,7 +214,7 @@ MultiThreadedDebug _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_32=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;.. + ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib NotSet Precise false @@ -246,7 +246,7 @@ false StreamingSIMDExtensions2 Precise - ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;.. + ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib Speed true false @@ -275,7 +275,7 @@ USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;_M_X64=1;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;.. + ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib false NotSet Precise @@ -309,7 +309,7 @@ USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;.. + ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib false NotSet Precise @@ -343,7 +343,7 @@ USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_32=1;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;.. + ../ext/glew;../ext/snappy;../ext/glslang;../ext/libpng17;../ext;../ext/zlib;..;../ext/zstd/lib false NotSet Precise @@ -413,6 +413,7 @@ + @@ -452,6 +453,7 @@ + @@ -469,12 +471,12 @@ - + - + @@ -523,7 +525,6 @@ - @@ -706,7 +707,7 @@ - + true true true @@ -736,6 +737,7 @@ + @@ -776,6 +778,7 @@ + @@ -792,9 +795,29 @@ - + + + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + @@ -840,7 +863,6 @@ - diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index 3a7dbc7980..cd5146e2cf 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -32,8 +32,6 @@ - - GL\GLInterface @@ -70,7 +68,6 @@ - ext\libpng17 @@ -102,9 +99,6 @@ Thread - - Thread - Input @@ -387,11 +381,26 @@ GPU + + Data\Convert + + + Data\Convert + + + + Net + + + File + + + @@ -411,8 +420,6 @@ - - GL\GLInterface @@ -433,7 +440,6 @@ - ext\libpng17 @@ -492,9 +498,6 @@ Thread - - Thread - Input @@ -744,6 +747,19 @@ GPU + + Data\Convert + + + Data\Convert + + + + Net + + + File + diff --git a/Common/ConsoleListener.cpp b/Common/ConsoleListener.cpp index 141d92e26d..de957012a3 100644 --- a/Common/ConsoleListener.cpp +++ b/Common/ConsoleListener.cpp @@ -300,7 +300,7 @@ COORD ConsoleListener::GetCoordinates(int BytesRead, int BufferWidth) unsigned int WINAPI ConsoleListener::RunThread(void *lpParam) { - setCurrentThreadName("Console"); + SetCurrentThreadName("Console"); ConsoleListener *consoleLog = (ConsoleListener *)lpParam; consoleLog->LogWriterThread(); return 0; diff --git a/Common/Data/Collections/ConstMap.h b/Common/Data/Collections/ConstMap.h index 6b1fe0e165..422737c435 100644 --- a/Common/Data/Collections/ConstMap.h +++ b/Common/Data/Collections/ConstMap.h @@ -4,21 +4,21 @@ template class InitConstMap { private: - std::map m_map; + std::map m_map; public: - InitConstMap(const T& key, const U& val) - { - m_map[key] = val; - } + InitConstMap(const T& key, const U& val) + { + m_map[key] = val; + } - InitConstMap& operator()(const T& key, const U& val) - { - m_map[key] = val; - return *this; - } + InitConstMap& operator()(const T& key, const U& val) + { + m_map[key] = val; + return *this; + } - operator std::map() - { - return m_map; - } + operator std::map() + { + return m_map; + } }; diff --git a/Common/ColorConv.cpp b/Common/Data/Convert/ColorConv.cpp similarity index 95% rename from Common/ColorConv.cpp rename to Common/Data/Convert/ColorConv.cpp index 0e48cca0bc..5404e89bca 100644 --- a/Common/ColorConv.cpp +++ b/Common/Data/Convert/ColorConv.cpp @@ -16,12 +16,12 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "ppsspp_config.h" +#include "Common/Data/Convert/ColorConv.h" #include "Common/Data/Convert/SmallDataConvert.h" -#include "ColorConv.h" // NEON is in a separate file so that it can be compiled with a runtime check. -#include "ColorConvNEON.h" -#include "Common.h" -#include "CPUDetect.h" +#include "Common/Data/Convert/ColorConvNEON.h" +#include "Common/Common.h" +#include "Common/CPUDetect.h" #ifdef _M_SSE #include @@ -172,6 +172,15 @@ void ConvertBGRA8888ToRGBA8888(u32 *dst, const u32 *src, u32 numPixels) { } } +void ConvertBGRA8888ToRGB888(u8 *dst, const u32 *src, u32 numPixels) { + for (uint32_t x = 0; x < numPixels; ++x) { + uint32_t c = src[x]; + dst[x * 3 + 0] = (c >> 16) & 0xFF; + dst[x * 3 + 1] = (c >> 8) & 0xFF; + dst[x * 3 + 2] = (c >> 0) & 0xFF; + } +} + void ConvertRGBA8888ToRGBA5551(u16 *dst, const u32 *src, u32 numPixels) { #if _M_SSE >= 0x401 const __m128i maskAG = _mm_set1_epi32(0x8000F800); @@ -280,6 +289,12 @@ void ConvertRGBA8888ToRGBA4444(u16 *dst, const u32 *src, u32 numPixels) { } } +void ConvertRGBA8888ToRGB888(u8 *dst, const u32 *src, u32 numPixels) { + for (uint32_t x = 0; x < numPixels; ++x) { + memcpy(dst + x * 3, src + x, 3); + } +} + void ConvertRGB565ToRGBA8888(u32 *dst32, const u16 *src, u32 numPixels) { #ifdef _M_SSE const __m128i mask5 = _mm_set1_epi16(0x001f); @@ -637,6 +652,21 @@ void ConvertRGB565ToBGR565Basic(u16 *dst, const u16 *src, u32 numPixels) { } } +void ConvertBGRA5551ToABGR1555(u16 *dst, const u16 *src, u32 numPixels) { + const u32 *src32 = (const u32 *)src; + u32 *dst32 = (u32 *)dst; + for (u32 i = 0; i < numPixels / 2; i++) { + const u32 c = src32[i]; + dst32[i] = ((c >> 15) & 0x00010001) | ((c << 1) & 0xFFFEFFFE); + } + + if (numPixels & 1) { + const u32 i = numPixels - 1; + const u16 c = src[i]; + dst[i] = (c >> 15) | (c << 1); + } +} + // Reuse the logic from the header - if these aren't defined, we need externs. #ifndef ConvertRGBA4444ToABGR4444 Convert16bppTo16bppFunc ConvertRGBA4444ToABGR4444 = &ConvertRGBA4444ToABGR4444Basic; diff --git a/Common/ColorConv.h b/Common/Data/Convert/ColorConv.h similarity index 95% rename from Common/ColorConv.h rename to Common/Data/Convert/ColorConv.h index 9e66b73ea8..e695f3ce8f 100644 --- a/Common/ColorConv.h +++ b/Common/Data/Convert/ColorConv.h @@ -18,8 +18,8 @@ #pragma once #include "ppsspp_config.h" -#include "CommonTypes.h" -#include "ColorConvNEON.h" +#include "Common/CommonTypes.h" +#include "Common/Data/Convert/ColorConvNEON.h" void SetupColorConv(); @@ -110,10 +110,12 @@ typedef void (*Convert32bppTo32bppFunc)(u32 *dst, const u32 *src, u32 numPixels) void ConvertBGRA8888ToRGBA8888(u32 *dst, const u32 *src, u32 numPixels); #define ConvertRGBA8888ToBGRA8888 ConvertBGRA8888ToRGBA8888 +void ConvertBGRA8888ToRGB888(u8 *dst, const u32 *src, u32 numPixels); void ConvertRGBA8888ToRGBA5551(u16 *dst, const u32 *src, u32 numPixels); void ConvertRGBA8888ToRGB565(u16 *dst, const u32 *src, u32 numPixels); void ConvertRGBA8888ToRGBA4444(u16 *dst, const u32 *src, u32 numPixels); +void ConvertRGBA8888ToRGB888(u8 *dst, const u32 *src, u32 numPixels); void ConvertBGRA8888ToRGBA5551(u16 *dst, const u32 *src, u32 numPixels); void ConvertBGRA8888ToRGB565(u16 *dst, const u32 *src, u32 numPixels); @@ -134,6 +136,7 @@ void ConvertRGB565ToBGRA8888(u32 *dst, const u16 *src, u32 numPixels); void ConvertRGBA4444ToABGR4444Basic(u16 *dst, const u16 *src, u32 numPixels); void ConvertRGBA5551ToABGR1555Basic(u16 *dst, const u16 *src, u32 numPixels); void ConvertRGB565ToBGR565Basic(u16 *dst, const u16 *src, u32 numPixels); +void ConvertBGRA5551ToABGR1555(u16 *dst, const u16 *src, u32 numPixels); #if PPSSPP_ARCH(ARM64) #define ConvertRGBA4444ToABGR4444 ConvertRGBA4444ToABGR4444NEON diff --git a/Common/ColorConvNEON.cpp b/Common/Data/Convert/ColorConvNEON.cpp similarity index 97% rename from Common/ColorConvNEON.cpp rename to Common/Data/Convert/ColorConvNEON.cpp index 375bcd0e75..27c230448a 100644 --- a/Common/ColorConvNEON.cpp +++ b/Common/Data/Convert/ColorConvNEON.cpp @@ -23,9 +23,9 @@ #else #include #endif -#include "ColorConvNEON.h" -#include "Common.h" -#include "CPUDetect.h" +#include "Common/Data/Convert/ColorConvNEON.h" +#include "Common/Common.h" +#include "Common/CPUDetect.h" // TODO: More NEON color conversion funcs. diff --git a/Common/ColorConvNEON.h b/Common/Data/Convert/ColorConvNEON.h similarity index 95% rename from Common/ColorConvNEON.h rename to Common/Data/Convert/ColorConvNEON.h index 43002411cd..e80fbbd324 100644 --- a/Common/ColorConvNEON.h +++ b/Common/Data/Convert/ColorConvNEON.h @@ -17,7 +17,7 @@ #pragma once -#include "ColorConv.h" +#include "Common/Data/Convert/ColorConv.h" void ConvertRGBA4444ToABGR4444NEON(u16 *dst, const u16 *src, u32 numPixels); void ConvertRGBA5551ToABGR1555NEON(u16 *dst, const u16 *src, u32 numPixels); diff --git a/Common/Data/Encoding/Utf8.cpp b/Common/Data/Encoding/Utf8.cpp index f3c2b1e223..d3ddbdbc21 100644 --- a/Common/Data/Encoding/Utf8.cpp +++ b/Common/Data/Encoding/Utf8.cpp @@ -234,6 +234,32 @@ uint32_t u8_nextchar(const char *s, int *i) return ch; } +uint32_t u8_nextchar_unsafe(const char *s, int *i) { + uint32_t ch = (unsigned char)s[(*i)++]; + int sz = 1; + + if (ch >= 0xF0) { + sz++; + ch &= ~0x10; + } + if (ch >= 0xE0) { + sz++; + ch &= ~0x20; + } + if (ch >= 0xC0) { + sz++; + ch &= ~0xC0; + } + + // Just assume the bytes must be there. This is the logic used on the PSP. + for (int j = 1; j < sz; ++j) { + ch <<= 6; + ch += ((unsigned char)s[(*i)++]) & 0x3F; + } + + return ch; +} + void u8_inc(const char *s, int *i) { (void)(isutf(s[++(*i)]) || isutf(s[++(*i)]) || @@ -483,6 +509,22 @@ std::string ConvertUCS2ToUTF8(const std::u16string &wstr) { return s; } +std::string SanitizeUTF8(const std::string &utf8string) { + UTF8 utf(utf8string.c_str()); + std::string s; + // Worst case. + s.resize(utf8string.size() * 4); + + // This stops at invalid start bytes. + size_t pos = 0; + while (!utf.end() && !utf.invalid()) { + int c = utf.next_unsafe(); + pos += UTF8::encode(&s[pos], c); + } + s.resize(pos); + return s; +} + static size_t ConvertUTF8ToUCS2Internal(char16_t *dest, size_t destSize, const std::string &source) { const char16_t *const orig = dest; const char16_t *const destEnd = dest + destSize; diff --git a/Common/Data/Encoding/Utf8.h b/Common/Data/Encoding/Utf8.h index b954ef5371..29d09f67e9 100644 --- a/Common/Data/Encoding/Utf8.h +++ b/Common/Data/Encoding/Utf8.h @@ -20,6 +20,7 @@ #include uint32_t u8_nextchar(const char *s, int *i); +uint32_t u8_nextchar_unsafe(const char *s, int *i); int u8_wc_toutf8(char *dest, uint32_t ch); int u8_strlen(const char *s); void u8_inc(const char *s, int *i); @@ -31,10 +32,19 @@ public: UTF8(const char *c) : c_(c), index_(0) {} UTF8(const char *c, int index) : c_(c), index_(index) {} bool end() const { return c_[index_] == 0; } + // Returns true if the next character is outside BMP and Planes 1 - 16. + bool invalid() const { + unsigned char c = (unsigned char)c_[index_]; + return (c >= 0x80 && c <= 0xC1) || c >= 0xF5; + } uint32_t next() { return u8_nextchar(c_, &index_); } - uint32_t peek() { + // Allow invalid continuation bytes. + uint32_t next_unsafe() { + return u8_nextchar_unsafe(c_, &index_); + } + uint32_t peek() const { int tempIndex = index_; return u8_nextchar(c_, &tempIndex); } @@ -76,6 +86,10 @@ int UTF8StringNonASCIICount(const char *utf8string); bool UTF8StringHasNonASCII(const char *utf8string); +// Removes overlong encodings and similar. +std::string SanitizeUTF8(const std::string &utf8string); + + // UTF8 to Win32 UTF-16 // Should be used when calling Win32 api calls #ifdef _WIN32 diff --git a/Common/Data/Format/IniFile.cpp b/Common/Data/Format/IniFile.cpp index 8e001f802d..06ee1acd60 100644 --- a/Common/Data/Format/IniFile.cpp +++ b/Common/Data/Format/IniFile.cpp @@ -18,6 +18,7 @@ #include "Common/Data/Format/IniFile.h" #include "Common/File/VFS/VFS.h" +#include "Common/File/FileUtil.h" #include "Common/Data/Text/Parsers.h" #ifdef _WIN32 @@ -515,23 +516,19 @@ void IniFile::SortSections() std::sort(sections.begin(), sections.end()); } -bool IniFile::Load(const char* filename) +bool IniFile::Load(const Path &path) { sections.clear(); sections.push_back(Section("")); // first section consists of the comments before the first real section // Open file - std::ifstream in; -#if defined(_WIN32) && !defined(__MINGW32__) - in.open(ConvertUTF8ToWString(filename), std::ios::in); -#else - in.open(filename, std::ios::in); -#endif - if (in.fail()) return false; - - bool success = Load(in); - in.close(); + std::string data; + if (!File::ReadFileToString(true, path, data)) { + return false; + } + std::stringstream sstream(data); + bool success = Load(sstream); return success; } @@ -558,13 +555,13 @@ bool IniFile::Load(std::istream &in) { std::string line = templine; // Remove UTF-8 byte order marks. - if (line.substr(0, 3) == "\xEF\xBB\xBF") + if (line.substr(0, 3) == "\xEF\xBB\xBF") { line = line.substr(3); + } #ifndef _WIN32 // Check for CRLF eol and convert it to LF - if (!line.empty() && line.at(line.size()-1) == '\r') - { + if (!line.empty() && line.at(line.size()-1) == '\r') { line.erase(line.size()-1); } #endif @@ -595,34 +592,28 @@ bool IniFile::Load(std::istream &in) { return true; } -bool IniFile::Save(const char* filename) +bool IniFile::Save(const Path &filename) { - std::ofstream out; -#if defined(_WIN32) && !defined(__MINGW32__) - out.open(ConvertUTF8ToWString(filename), std::ios::out); -#else - out.open(filename, std::ios::out); -#endif - - if (out.fail()) - { + FILE *file = File::OpenCFile(filename, "w"); + if (!file) { return false; } // UTF-8 byte order mark. To make sure notepad doesn't go nuts. - out << "\xEF\xBB\xBF"; + // TODO: Do we still need this? It's annoying. + fprintf(file, "\xEF\xBB\xBF"); for (const Section §ion : sections) { if (!section.name().empty() && (!section.lines.empty() || !section.comment.empty())) { - out << "[" << section.name() << "]" << section.comment << std::endl; + fprintf(file, "[%s]%s\n", section.name().c_str(), section.comment.c_str()); } for (const std::string &s : section.lines) { - out << s << std::endl; + fprintf(file, "%s\n", s.c_str()); } } - out.close(); + fclose(file); return true; } diff --git a/Common/Data/Format/IniFile.h b/Common/Data/Format/IniFile.h index 5d65045033..6499562edb 100644 --- a/Common/Data/Format/IniFile.h +++ b/Common/Data/Format/IniFile.h @@ -9,8 +9,9 @@ #include #include -class Section -{ +#include "Common/File/Path.h" + +class Section { friend class IniFile; public: @@ -76,16 +77,15 @@ protected: std::string comment; }; -class IniFile -{ +class IniFile { public: - bool Load(const char* filename); - bool Load(const std::string &filename) { return Load(filename.c_str()); } + bool Load(const Path &path); + bool Load(const std::string &filename) { return Load(Path(filename)); } bool Load(std::istream &istream); bool LoadFromVFS(const std::string &filename); - bool Save(const char* filename); - bool Save(const std::string &filename) { return Save(filename.c_str()); } + bool Save(const Path &path); + bool Save(const std::string &filename) { return Save(Path(filename)); } // Returns true if key exists in section bool Exists(const char* sectionName, const char* key) const; diff --git a/Common/Data/Format/JSONReader.cpp b/Common/Data/Format/JSONReader.cpp index 4f299eab4a..331c54be64 100644 --- a/Common/Data/Format/JSONReader.cpp +++ b/Common/Data/Format/JSONReader.cpp @@ -11,7 +11,7 @@ JsonReader::JsonReader(const std::string &filename) { parse(); } else { // Okay, try to read on the local file system - buffer_ = (char *)ReadLocalFile(filename.c_str(), &buf_size); + buffer_ = (char *)File::ReadLocalFile(filename.c_str(), &buf_size); if (buffer_) { parse(); } else { diff --git a/Common/Data/Format/ZIMLoad.cpp b/Common/Data/Format/ZIMLoad.cpp index 45f23e2857..3fd759e92c 100644 --- a/Common/Data/Format/ZIMLoad.cpp +++ b/Common/Data/Format/ZIMLoad.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "zlib.h" @@ -106,6 +107,14 @@ int LoadZIMPtr(const uint8_t *zim, size_t datasize, int *width, int *height, int // Shouldn't happen if return value was Z_OK. ERROR_LOG(IO, "Wrong size data in ZIM: %i vs %i", (int)outlen, (int)total_data_size); } + } else if (*flags & ZIM_ZSTD_COMPRESSED) { + size_t outlen = ZSTD_decompress(*image, total_data_size, zim + 16, datasize - 16); + if (outlen != (size_t)total_data_size) { + ERROR_LOG(IO, "ZIM zstd format decompression failed: %lld", (long long)outlen); + free(*image); + *image = 0; + return 0; + } } else { memcpy(*image, zim + 16, datasize - 16); if (datasize - 16 != (size_t)total_data_size) { @@ -119,7 +128,7 @@ int LoadZIM(const char *filename, int *width, int *height, int *format, uint8_t size_t size; uint8_t *buffer = VFSReadFile(filename, &size); if (!buffer) { - ERROR_LOG(IO, "Couldn't read data for '%s'", buffer); + ERROR_LOG(IO, "Couldn't read data for '%s'", filename); return 0; } diff --git a/Common/Data/Format/ZIMLoad.h b/Common/Data/Format/ZIMLoad.h index 18354bc6d6..85276b08dd 100644 --- a/Common/Data/Format/ZIMLoad.h +++ b/Common/Data/Format/ZIMLoad.h @@ -32,6 +32,7 @@ enum { ZIM_ETC1_MEDIUM = 1024, ZIM_ETC1_HIGH = 0, // default ZIM_ETC1_DITHER = 2048, + ZIM_ZSTD_COMPRESSED = 4096, }; // ZIM will only ever support up to 12 levels (4096x4096 max). diff --git a/Common/Data/Format/ZIMSave.cpp b/Common/Data/Format/ZIMSave.cpp index 4f5fb230c6..b18fcf682a 100644 --- a/Common/Data/Format/ZIMSave.cpp +++ b/Common/Data/Format/ZIMSave.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "zlib.h" @@ -23,7 +24,7 @@ static unsigned int log2i(unsigned int val) { } -int ezcompress(unsigned char* pDest, long* pnDestLen, const unsigned char* pSrc, long nSrcLen) { +int ezcompress(unsigned char* pDest, long* pnDestLen, const unsigned char* pSrc, long nSrcLen, int compressLevel) { z_stream stream; int err; @@ -42,7 +43,7 @@ int ezcompress(unsigned char* pDest, long* pnDestLen, const unsigned char* pSrc, stream.zfree = (free_func)0; stream.opaque = (voidpf)0; - err = deflateInit(&stream, Z_DEFAULT_COMPRESSION); + err = deflateInit(&stream, compressLevel); if (err != Z_OK) return err; nExtraChunks = 0; do { @@ -178,7 +179,7 @@ uint8_t *DownsampleBy2(const uint8_t *image, int width, int height, int pitch) { return out; } -void SaveZIM(FILE *f, int width, int height, int pitch, int flags, const uint8_t *image_data) { +void SaveZIM(FILE *f, int width, int height, int pitch, int flags, const uint8_t *image_data, int compressLevel) { fwrite(magic, 1, 4, f); fwrite(&width, 1, 4, f); fwrite(&height, 1, 4, f); @@ -195,7 +196,17 @@ void SaveZIM(FILE *f, int width, int height, int pitch, int flags, const uint8_t if (flags & ZIM_ZLIB_COMPRESSED) { long dest_len = data_size * 2; uint8_t *dest = new uint8_t[dest_len]; - if (Z_OK == ezcompress(dest, &dest_len, data, data_size)) { + if (Z_OK == ezcompress(dest, &dest_len, data, data_size, compressLevel == 0 ? Z_DEFAULT_COMPRESSION : compressLevel)) { + fwrite(dest, 1, dest_len, f); + } else { + ERROR_LOG(IO, "Zlib compression failed.\n"); + } + delete [] dest; + } else if (flags & ZIM_ZSTD_COMPRESSED) { + size_t dest_len = ZSTD_compressBound(data_size); + uint8_t *dest = new uint8_t[dest_len]; + dest_len = ZSTD_compress(dest, dest_len, data, data_size, compressLevel == 0 ? 22 : compressLevel); + if (!ZSTD_isError(dest_len)) { fwrite(dest, 1, dest_len, f); } else { ERROR_LOG(IO, "Zlib compression failed.\n"); diff --git a/Common/Data/Format/ZIMSave.h b/Common/Data/Format/ZIMSave.h index 90158d3d55..d22fd0490c 100644 --- a/Common/Data/Format/ZIMSave.h +++ b/Common/Data/Format/ZIMSave.h @@ -11,4 +11,4 @@ // * Generate mipmaps if requested // * Convert images to the requested format // Input image is always 8888 RGBA. SaveZIM takes care of downsampling and mipmap generation. -void SaveZIM(FILE *f, int width, int height, int pitch, int format, const uint8_t *image); +void SaveZIM(FILE *f, int width, int height, int pitch, int format, const uint8_t *image, int compressLevel = 0); diff --git a/Common/Data/Hash/Hash.cpp b/Common/Data/Hash/Hash.cpp index d19e1a1e00..2b62ced02d 100644 --- a/Common/Data/Hash/Hash.cpp +++ b/Common/Data/Hash/Hash.cpp @@ -7,31 +7,31 @@ namespace hash { #define MOD_ADLER 65521 // data: Pointer to the data to be summed; len is in bytes uint32_t Adler32(const uint8_t *data, size_t len) { - uint32_t a = 1, b = 0; - while (len) { - size_t tlen = len > 5550 ? 5550 : len; - len -= tlen; - do { - a += *data++; - b += a; - } while (--tlen); + uint32_t a = 1, b = 0; + while (len) { + size_t tlen = len > 5550 ? 5550 : len; + len -= tlen; + do { + a += *data++; + b += a; + } while (--tlen); - a = (a & 0xffff) + (a >> 16) * (65536 - MOD_ADLER); - b = (b & 0xffff) + (b >> 16) * (65536 - MOD_ADLER); - } + a = (a & 0xffff) + (a >> 16) * (65536 - MOD_ADLER); + b = (b & 0xffff) + (b >> 16) * (65536 - MOD_ADLER); + } - // It can be shown that a <= 0x1013a here, so a single subtract will do. - if (a >= MOD_ADLER) { - a -= MOD_ADLER; - } + // It can be shown that a <= 0x1013a here, so a single subtract will do. + if (a >= MOD_ADLER) { + a -= MOD_ADLER; + } - // It can be shown that b can reach 0xfff87 here. - b = (b & 0xffff) + (b >> 16) * (65536 - MOD_ADLER); + // It can be shown that b can reach 0xfff87 here. + b = (b & 0xffff) + (b >> 16) * (65536 - MOD_ADLER); - if (b >= MOD_ADLER) { - b -= MOD_ADLER; - } - return (b << 16) | a; + if (b >= MOD_ADLER) { + b -= MOD_ADLER; + } + return (b << 16) | a; } } // namespace hash diff --git a/Common/Data/Text/I18n.cpp b/Common/Data/Text/I18n.cpp index 72e16a6eb6..6909f3a38a 100644 --- a/Common/Data/Text/I18n.cpp +++ b/Common/Data/Text/I18n.cpp @@ -72,7 +72,7 @@ std::string I18NRepo::GetIniPath(const std::string &languageID) const { } bool I18NRepo::IniExists(const std::string &languageID) const { - FileInfo info; + File::FileInfo info; if (!VFSGetFileInfo(GetIniPath(languageID).c_str(), &info)) return false; if (!info.exists) @@ -80,18 +80,18 @@ bool I18NRepo::IniExists(const std::string &languageID) const { return true; } -bool I18NRepo::LoadIni(const std::string &languageID, const std::string &overridePath) { +bool I18NRepo::LoadIni(const std::string &languageID, const Path &overridePath) { IniFile ini; - std::string iniPath; + Path iniPath; // INFO_LOG(SYSTEM, "Loading lang ini %s", iniPath.c_str()); if (!overridePath.empty()) { - iniPath = overridePath + languageID + ".ini"; + iniPath = overridePath / (languageID + ".ini"); } else { - iniPath = GetIniPath(languageID); + iniPath = Path(GetIniPath(languageID)); } - if (!ini.LoadFromVFS(iniPath)) + if (!ini.LoadFromVFS(iniPath.ToString())) return false; Clear(); diff --git a/Common/Data/Text/I18n.h b/Common/Data/Text/I18n.h index f8493d1b99..50334fa1f6 100644 --- a/Common/Data/Text/I18n.h +++ b/Common/Data/Text/I18n.h @@ -15,6 +15,7 @@ #include #include "Common/Common.h" +#include "Common/File/Path.h" // Reasonably thread safe. @@ -76,7 +77,7 @@ public: ~I18NRepo(); bool IniExists(const std::string &languageID) const; - bool LoadIni(const std::string &languageID, const std::string &overridePath = ""); // NOT the filename! + bool LoadIni(const std::string &languageID, const Path &overridePath = Path()); // NOT the filename! void SaveIni(const std::string &languageID); std::string LanguageID(); diff --git a/Common/ExceptionHandlerSetup.cpp b/Common/ExceptionHandlerSetup.cpp index 8232b4ba9e..df84d0b60c 100644 --- a/Common/ExceptionHandlerSetup.cpp +++ b/Common/ExceptionHandlerSetup.cpp @@ -102,7 +102,7 @@ static void CheckKR(const char* name, kern_return_t kr) { } static void ExceptionThread(mach_port_t port) { - setCurrentThreadName("Mach exception thread"); + SetCurrentThreadName("Mach exception thread"); #pragma pack(4) struct { mach_msg_header_t Head; diff --git a/Common/FakeCPUDetect.cpp b/Common/FakeCPUDetect.cpp index e110f0cdc0..9b1469c21c 100644 --- a/Common/FakeCPUDetect.cpp +++ b/Common/FakeCPUDetect.cpp @@ -15,6 +15,16 @@ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ +#include "ppsspp_config.h" +#if PPSSPP_ARCH(ARM) || PPSSPP_ARCH(ARM64) +#define REAL_CPUDETECT_AVAIL 1 +#elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) +#define REAL_CPUDETECT_AVAIL 1 +#elif PPSSPP_ARCH(MIPS) || PPSSPP_ARCH(MIPS64) +#define REAL_CPUDETECT_AVAIL 1 +#endif + +#ifndef REAL_CPUDETECT_AVAIL #include #include #include @@ -48,3 +58,4 @@ std::string CPUInfo::Summarize() sum = StringFromFormat("%s, %i core", cpu_string, num_cores); return sum; } +#endif diff --git a/Common/File/DirListing.cpp b/Common/File/DirListing.cpp index 89c30e35b7..36f05b9259 100644 --- a/Common/File/DirListing.cpp +++ b/Common/File/DirListing.cpp @@ -21,6 +21,7 @@ #include "Common/Data/Encoding/Utf8.h" #include "Common/StringUtils.h" #include "Common/File/DirListing.h" +#include "Common/File/FileUtil.h" #if !defined(__linux__) && !defined(_WIN32) && !defined(__QNX__) #define stat64 stat @@ -33,20 +34,29 @@ #define fileno #endif // HAVE_LIBNX -// Returns true if filename is a directory -bool isDirectory(const std::string & filename) { - FileInfo info; - getFileInfo(filename.c_str(), &info); - return info.isDirectory; -} +namespace File { + +bool GetFileInfo(const Path &path, FileInfo * fileInfo) { + switch (path.Type()) { + case PathType::NATIVE: + break; // OK + default: + return false; + } -bool getFileInfo(const char *path, FileInfo * fileInfo) { // TODO: Expand relative paths? fileInfo->fullName = path; #ifdef _WIN32 + auto FiletimeToStatTime = [](FILETIME ft) { + const int windowsTickResolution = 10000000; + const int64_t secToUnixEpoch = 11644473600LL; + int64_t ticks = ((uint64_t)ft.dwHighDateTime << 32) | ft.dwLowDateTime; + return (int64_t)(ticks / windowsTickResolution - secToUnixEpoch); + }; + WIN32_FILE_ATTRIBUTE_DATA attrs; - if (!GetFileAttributesExW(ConvertUTF8ToWString(path).c_str(), GetFileExInfoStandard, &attrs)) { + if (!GetFileAttributesExW(path.ToWString().c_str(), GetFileExInfoStandard, &attrs)) { fileInfo->size = 0; fileInfo->isDirectory = false; fileInfo->exists = false; @@ -56,16 +66,25 @@ bool getFileInfo(const char *path, FileInfo * fileInfo) { fileInfo->isDirectory = (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; fileInfo->isWritable = (attrs.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0; fileInfo->exists = true; + fileInfo->atime = FiletimeToStatTime(attrs.ftLastAccessTime); + fileInfo->mtime = FiletimeToStatTime(attrs.ftLastWriteTime); + fileInfo->ctime = FiletimeToStatTime(attrs.ftCreationTime); + if (attrs.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { + fileInfo->access = 0444; // Read + } else { + fileInfo->access = 0666; // Read/Write + } + if (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + fileInfo->access |= 0111; // Execute + } #else - std::string copy(path); - #if (defined __ANDROID__) && (__ANDROID_API__ < 21) struct stat file_info; - int result = stat(copy.c_str(), &file_info); + int result = stat(path.c_str(), &file_info); #else struct stat64 file_info; - int result = stat64(copy.c_str(), &file_info); + int result = stat64(path.c_str(), &file_info); #endif if (result < 0) { fileInfo->exists = false; @@ -76,6 +95,10 @@ bool getFileInfo(const char *path, FileInfo * fileInfo) { fileInfo->isWritable = false; fileInfo->size = file_info.st_size; fileInfo->exists = true; + fileInfo->atime = file_info.st_atime; + fileInfo->mtime = file_info.st_mtime; + fileInfo->ctime = file_info.st_ctime; + fileInfo->access = file_info.st_mode & 0x1ff; // HACK: approximation if (file_info.st_mode & 0200) fileInfo->isWritable = true; @@ -83,14 +106,16 @@ bool getFileInfo(const char *path, FileInfo * fileInfo) { return true; } -std::string getFileExtension(const std::string & fn) { - int pos = (int)fn.rfind("."); - if (pos < 0) return ""; - std::string ext = fn.substr(pos + 1); - for (size_t i = 0; i < ext.size(); i++) { - ext[i] = tolower(ext[i]); +bool GetModifTime(const Path &filename, tm & return_time) { + memset(&return_time, 0, sizeof(return_time)); + FileInfo info; + if (GetFileInfo(filename, &info)) { + time_t t = info.mtime; + localtime_r((time_t*)&t, &return_time); + return true; + } else { + return false; } - return ext; } bool FileInfo::operator <(const FileInfo & other) const { @@ -104,7 +129,7 @@ bool FileInfo::operator <(const FileInfo & other) const { return false; } -size_t getFilesInDir(const char *directory, std::vector * files, const char *filter, int flags) { +size_t GetFilesInDir(const Path &directory, std::vector * files, const char *filter, int flags) { size_t foundEntries = 0; std::set filters; if (filter) { @@ -112,6 +137,7 @@ size_t getFilesInDir(const char *directory, std::vector * files, const while (*filter) { if (*filter == ':') { filters.insert(std::move(tmp)); + tmp.clear(); } else { tmp.push_back(*filter); } @@ -123,7 +149,7 @@ size_t getFilesInDir(const char *directory, std::vector * files, const #ifdef _WIN32 // Find the first file in the directory. WIN32_FIND_DATA ffd; - HANDLE hFind = FindFirstFileEx((ConvertUTF8ToWString(directory) + L"\\*").c_str(), FindExInfoStandard, &ffd, FindExSearchNameMatch, NULL, 0); + HANDLE hFind = FindFirstFileEx((directory.ToWString() + L"\\*").c_str(), FindExInfoStandard, &ffd, FindExSearchNameMatch, NULL, 0); if (hFind == INVALID_HANDLE_VALUE) { return 0; } @@ -138,7 +164,7 @@ size_t getFilesInDir(const char *directory, std::vector * files, const //if (directoryWithSlash.back() != '/') // directoryWithSlash += "/"; - DIR *dirp = opendir(directory); + DIR *dirp = opendir(directory.c_str()); if (!dirp) return 0; // non windows loop @@ -164,23 +190,19 @@ size_t getFilesInDir(const char *directory, std::vector * files, const FileInfo info; info.name = virtualName; - std::string dir = directory; - // Only append a slash if there isn't one on the end. - size_t lastSlash = dir.find_last_of("/"); - if (lastSlash != (dir.length() - 1)) - dir.append("/"); - - info.fullName = dir + virtualName; - info.isDirectory = isDirectory(info.fullName); + info.fullName = directory / virtualName; + info.isDirectory = IsDirectory(info.fullName); info.exists = true; info.size = 0; info.isWritable = false; // TODO - implement some kind of check if (!info.isDirectory) { - std::string ext = getFileExtension(info.fullName); - if (filter) { - if (filters.find(ext) == filters.end()) + std::string ext = info.fullName.GetFileExtension(); + if (!ext.empty()) { + ext = ext.substr(1); // Remove the dot. + if (filter && filters.find(ext) == filters.end()) { continue; + } } } @@ -199,25 +221,25 @@ size_t getFilesInDir(const char *directory, std::vector * files, const return foundEntries; } -int64_t getDirectoryRecursiveSize(const std::string & path, const char *filter, int flags) { +int64_t GetDirectoryRecursiveSize(const Path &path, const char *filter, int flags) { std::vector fileInfo; - getFilesInDir(path.c_str(), &fileInfo, filter, flags); + GetFilesInDir(path, &fileInfo, filter, flags); int64_t sizeSum = 0; - // Note: getFileInDir does not fill in fileSize properly. + // Note: GetFilesInDir does not fill in fileSize. for (size_t i = 0; i < fileInfo.size(); i++) { FileInfo finfo; - getFileInfo(fileInfo[i].fullName.c_str(), &finfo); + GetFileInfo(fileInfo[i].fullName, &finfo); if (!finfo.isDirectory) sizeSum += finfo.size; else - sizeSum += getDirectoryRecursiveSize(finfo.fullName, filter, flags); + sizeSum += GetDirectoryRecursiveSize(finfo.fullName, filter, flags); } return sizeSum; } #ifdef _WIN32 // Returns a vector with the device names -std::vector getWindowsDrives() +std::vector GetWindowsDrives() { #if PPSSPP_PLATFORM(UWP) return std::vector(); // TODO UWP http://stackoverflow.com/questions/37404405/how-to-get-logical-drives-names-in-windows-10 @@ -244,3 +266,5 @@ std::vector getWindowsDrives() #endif } #endif + +} // namespace File diff --git a/Common/File/DirListing.h b/Common/File/DirListing.h index 965ca5d6bf..f175796a95 100644 --- a/Common/File/DirListing.h +++ b/Common/File/DirListing.h @@ -7,29 +7,38 @@ #include -// Beginnings of a directory utility system. TODO: Improve. +#include "Common/File/Path.h" + +namespace File { struct FileInfo { std::string name; - std::string fullName; - bool exists; - bool isDirectory; - bool isWritable; - uint64_t size; + Path fullName; + bool exists = false; + bool isDirectory = false; + bool isWritable = false; + uint64_t size = 0; + + uint64_t atime; + uint64_t mtime; + uint64_t ctime; + uint32_t access; // st_mode & 0x1ff bool operator <(const FileInfo &other) const; }; -std::string getFileExtension(const std::string &fn); -bool getFileInfo(const char *path, FileInfo *fileInfo); -FILE *openCFile(const std::string &filename, const char *mode); +bool GetFileInfo(const Path &path, FileInfo *fileInfo); enum { GETFILES_GETHIDDEN = 1 }; -size_t getFilesInDir(const char *directory, std::vector *files, const char *filter = nullptr, int flags = 0); -int64_t getDirectoryRecursiveSize(const std::string &path, const char *filter = nullptr, int flags = 0); + + +size_t GetFilesInDir(const Path &directory, std::vector *files, const char *filter = nullptr, int flags = 0); +int64_t GetDirectoryRecursiveSize(const Path &path, const char *filter = nullptr, int flags = 0); #ifdef _WIN32 -std::vector getWindowsDrives(); +std::vector GetWindowsDrives(); #endif + +} // namespace File diff --git a/Common/File/FileDescriptor.cpp b/Common/File/FileDescriptor.cpp index 0c40ee1da7..513664c008 100644 --- a/Common/File/FileDescriptor.cpp +++ b/Common/File/FileDescriptor.cpp @@ -1,8 +1,8 @@ #include "ppsspp_config.h" #include -#include -#include +#include +#include #ifndef _WIN32 #include #include @@ -18,8 +18,8 @@ #include #include "Common/Common.h" -#include "Common/Log.h" #include "Common/File/FileDescriptor.h" +#include "Common/Log.h" namespace fd_util { @@ -27,100 +27,99 @@ namespace fd_util { // Reads from a socket, up to an '\n'. This means that if the line ends // with '\r', the '\r' will be returned. size_t ReadLine(int fd, char *vptr, size_t buf_size) { - char *buffer = vptr; - size_t n; - for (n = 1; n < buf_size; n++) { - char c; - size_t rc; - if ((rc = read(fd, &c, 1)) == 1) { - *buffer++ = c; - if (c == '\n') - break; - } - else if (rc == 0) { - if (n == 1) - return 0; - else - break; - } - else { - if (errno == EINTR) - continue; - _assert_msg_(false, "Error in Readline()"); - } - } + char *buffer = vptr; + size_t n; + for (n = 1; n < buf_size; n++) { + char c; + size_t rc; + if ((rc = read(fd, &c, 1)) == 1) { + *buffer++ = c; + if (c == '\n') + break; + } else if (rc == 0) { + if (n == 1) + return 0; + else + break; + } else { + if (errno == EINTR) + continue; + _assert_msg_(false, "Error in Readline()"); + } + } - *buffer = 0; - return n; + *buffer = 0; + return n; } // Misnamed, it just writes raw data in a retry loop. size_t WriteLine(int fd, const char *vptr, size_t n) { - const char *buffer = vptr; - size_t nleft = n; + const char *buffer = vptr; + size_t nleft = n; - while (nleft > 0) { - int nwritten; - if ((nwritten = (int)write(fd, buffer, (unsigned int)nleft)) <= 0) { - if (errno == EINTR) - nwritten = 0; - else - _assert_msg_(false, "Error in Writeline()"); - } - nleft -= nwritten; - buffer += nwritten; - } + while (nleft > 0) { + int nwritten; + if ((nwritten = (int)write(fd, buffer, (unsigned int)nleft)) <= 0) { + if (errno == EINTR) + nwritten = 0; + else + _assert_msg_(false, "Error in Writeline()"); + } + nleft -= nwritten; + buffer += nwritten; + } - return n; + return n; } size_t WriteLine(int fd, const char *buffer) { - return WriteLine(fd, buffer, strlen(buffer)); + return WriteLine(fd, buffer, strlen(buffer)); } size_t Write(int fd, const std::string &str) { - return WriteLine(fd, str.c_str(), str.size()); + return WriteLine(fd, str.c_str(), str.size()); } bool WaitUntilReady(int fd, double timeout, bool for_write) { - struct timeval tv; - tv.tv_sec = floor(timeout); - tv.tv_usec = (timeout - floor(timeout)) * 1000000.0; + struct timeval tv; + tv.tv_sec = floor(timeout); + tv.tv_usec = (timeout - floor(timeout)) * 1000000.0; - fd_set fds; - FD_ZERO(&fds); - FD_SET(fd, &fds); - // First argument to select is the highest socket in the set + 1. - int rval; - if (for_write) { - rval = select(fd + 1, NULL, &fds, NULL, &tv); - } else { - rval = select(fd + 1, &fds, NULL, NULL, &tv); - } - if (rval < 0) { - // Error calling select. - return false; - } else if (rval == 0) { - // Timeout. - return false; - } else { - // Socket is ready. - return true; - } + fd_set fds; + FD_ZERO(&fds); + FD_SET(fd, &fds); + // First argument to select is the highest socket in the set + 1. + int rval; + if (for_write) { + rval = select(fd + 1, nullptr, &fds, nullptr, &tv); + } else { + rval = select(fd + 1, &fds, nullptr, nullptr, &tv); + } + + if (rval < 0) { + // Error calling select. + return false; + } else if (rval == 0) { + // Timeout. + return false; + } else { + // Socket is ready. + return true; + } } void SetNonBlocking(int sock, bool non_blocking) { #ifndef _WIN32 int opts = fcntl(sock, F_GETFL); - if (opts < 0) { + if (opts < 0) { perror("fcntl(F_GETFL)"); ERROR_LOG(IO, "Error getting socket status while changing nonblocking status"); } if (non_blocking) { - opts = (opts | O_NONBLOCK); - } else { - opts = (opts & ~O_NONBLOCK); - } + opts = (opts | O_NONBLOCK); + } else { + opts = (opts & ~O_NONBLOCK); + } if (fcntl(sock, F_SETFL, opts) < 0) { perror("fcntl(F_SETFL)"); diff --git a/Common/File/FileUtil.cpp b/Common/File/FileUtil.cpp index 0cb921f0e0..b528d21b38 100644 --- a/Common/File/FileUtil.cpp +++ b/Common/File/FileUtil.cpp @@ -91,28 +91,24 @@ // This namespace has various generic functions related to files and paths. // The code still needs a ton of cleanup. // REMEMBER: strdup considered harmful! -namespace File -{ +namespace File { + +FILE *OpenCFile(const Path &path, const char *mode) { + switch (path.Type()) { + case PathType::NATIVE: + break; + default: + ERROR_LOG(COMMON, "OpenCFile(%s): Not yet supported", path.c_str()); + return nullptr; + } -FILE *OpenCFile(const std::string &filename, const char *mode) -{ #if defined(_WIN32) && defined(UNICODE) - return _wfopen(ConvertUTF8ToWString(filename).c_str(), ConvertUTF8ToWString(mode).c_str()); + return _wfopen(path.ToWString().c_str(), ConvertUTF8ToWString(mode).c_str()); #else - return fopen(filename.c_str(), mode); + return fopen(path.ToString().c_str(), mode); #endif } -bool OpenCPPFile(std::fstream & stream, const std::string &filename, std::ios::openmode mode) -{ -#if defined(_WIN32) && defined(UNICODE) && !defined(__MINGW32__) - stream.open(ConvertUTF8ToWString(filename), mode); -#else - stream.open(filename.c_str(), mode); -#endif - return stream.is_open(); -} - #ifdef _WIN32 static bool ResolvePathVista(const std::wstring &path, wchar_t *buf, DWORD bufSize) { typedef DWORD(WINAPI *getFinalPathNameByHandleW_f)(HANDLE hFile, LPWSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags); @@ -150,7 +146,7 @@ std::string ResolvePath(const std::string &path) { if (startsWith(path, "http://") || startsWith(path, "https://")) { return path; } -#ifdef _WIN32 +#if PPSSPP_PLATFORM(WINDOWS) static const int BUF_SIZE = 32768; wchar_t *buf = new wchar_t[BUF_SIZE] {}; @@ -199,7 +195,7 @@ std::string ResolvePath(const std::string &path) { static void StripTailDirSlashes(std::string &fname) { if (fname.length() > 1) { size_t i = fname.length() - 1; -#ifdef _WIN32 +#if PPSSPP_PLATFORM(WINDOWS) if (i == 2 && fname[1] == ':' && fname[2] == '\\') return; #endif @@ -210,6 +206,10 @@ static void StripTailDirSlashes(std::string &fname) { } // Returns true if file filename exists. Will return true on directories. +bool ExistsInDir(const Path &path, const std::string &filename) { + return Exists(path / filename); +} + bool Exists(const std::string &filename) { std::string fn = filename; StripTailDirSlashes(fn); @@ -235,17 +235,29 @@ bool Exists(const std::string &filename) { #endif } -// Returns true if filename is a directory -bool IsDirectory(const std::string &filename) -{ - std::string fn = filename; +bool Exists(const Path &path) { + // Temporary forward. + return Exists(path.ToString()); +} + +// Returns true if filename exists and is a directory +bool IsDirectory(const Path &filename) { + switch (filename.Type()) { + case PathType::NATIVE: + break; // OK + default: + return false; + } + + std::string fn = filename.ToString(); StripTailDirSlashes(fn); #if defined(_WIN32) std::wstring copy = ConvertUTF8ToWString(fn); WIN32_FILE_ATTRIBUTE_DATA data{}; if (!GetFileAttributesEx(copy.c_str(), GetFileExInfoStandard, &data) || data.dwFileAttributes == INVALID_FILE_ATTRIBUTES) { - WARN_LOG(COMMON, "GetFileAttributes failed on %s: %08x", fn.c_str(), (uint32_t)GetLastError()); + auto err = GetLastError(); + WARN_LOG(COMMON, "GetFileAttributes failed on %s: %08x %s", fn.c_str(), (uint32_t)err, GetStringErrorMsg(err).c_str()); return false; } DWORD result = data.dwFileAttributes; @@ -264,7 +276,14 @@ bool IsDirectory(const std::string &filename) // Deletes a given filename, return true on success // Doesn't supports deleting a directory -bool Delete(const std::string &filename) { +bool Delete(const Path &filename) { + switch (filename.Type()) { + case PathType::NATIVE: + break; // OK + default: + return false; + } + INFO_LOG(COMMON, "Delete: file %s", filename.c_str()); // Return true because we care about the file no @@ -281,7 +300,7 @@ bool Delete(const std::string &filename) { } #ifdef _WIN32 - if (!DeleteFile(ConvertUTF8ToWString(filename).c_str())) { + if (!DeleteFile(filename.ToWString().c_str())) { WARN_LOG(COMMON, "Delete: DeleteFile failed on %s: %s", filename.c_str(), GetLastErrorMsg().c_str()); return false; } @@ -297,10 +316,15 @@ bool Delete(const std::string &filename) { } // Returns true if successful, or path already exists. -bool CreateDir(const std::string &path) -{ - std::string fn = path; - StripTailDirSlashes(fn); +bool CreateDir(const Path &path) { + switch (path.Type()) { + case PathType::NATIVE: + break; // OK + default: + return false; + } + + std::string fn = path.ToString(); DEBUG_LOG(COMMON, "CreateDir('%s')", fn.c_str()); #ifdef _WIN32 if (::CreateDirectory(ConvertUTF8ToWString(fn).c_str(), NULL)) @@ -311,14 +335,13 @@ bool CreateDir(const std::string &path) WARN_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: already exists", path.c_str()); return true; } - ERROR_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: %08x", path.c_str(), (uint32_t)error); + ERROR_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: %08x %s", path.c_str(), (uint32_t)error, GetStringErrorMsg(error).c_str()); return false; #else if (mkdir(fn.c_str(), 0755) == 0) return true; int err = errno; - if (err == EEXIST) { WARN_LOG(COMMON, "CreateDir: mkdir failed on %s: already exists", fn.c_str()); @@ -331,10 +354,15 @@ bool CreateDir(const std::string &path) } // Creates the full path of fullPath returns true on success -bool CreateFullPath(const std::string &path) -{ - std::string fullPath = path; - StripTailDirSlashes(fullPath); +bool CreateFullPath(const Path &path) { + switch (path.Type()) { + case PathType::NATIVE: + break; // OK + default: + return false; + } + + std::string fullPath = path.ToString(); int panicCounter = 100; VERBOSE_LOG(COMMON, "CreateFullPath: path %s", fullPath.c_str()); @@ -346,30 +374,29 @@ bool CreateFullPath(const std::string &path) size_t position = 0; #ifdef _WIN32 - // Skip the drive letter, no need to create C:\. - position = 3; + // Skip the drive letter when looking for slashes, no need to create C:\. + if (path.IsAbsolute()) { + position = 3; + } #endif - while (true) - { + while (true) { // Find next sub path position = fullPath.find_first_of(DIR_SEP_CHRS, position); - // we're done, yay! - if (position == fullPath.npos) - { + if (position == fullPath.npos) { if (!File::Exists(fullPath)) - return File::CreateDir(fullPath); + return File::CreateDir(Path(fullPath)); return true; } std::string subPath = fullPath.substr(0, position); - if (position != 0 && !File::Exists(subPath)) - File::CreateDir(subPath); + if (position != 0 && !File::Exists(subPath)) { + File::CreateDir(Path(subPath)); + } // A safety check panicCounter--; - if (panicCounter <= 0) - { + if (panicCounter <= 0) { ERROR_LOG(COMMON, "CreateFullPath: directory structure too deep"); return false; } @@ -377,62 +404,90 @@ bool CreateFullPath(const std::string &path) } } - // Deletes a directory filename, returns true on success -bool DeleteDir(const std::string &filename) -{ - INFO_LOG(COMMON, "DeleteDir: directory %s", filename.c_str()); +bool DeleteDir(const Path &path) { + switch (path.Type()) { + case PathType::NATIVE: + break; // OK + default: + return false; + } + INFO_LOG(COMMON, "DeleteDir: directory %s", path.c_str()); // check if a directory - if (!File::IsDirectory(filename)) + if (!File::IsDirectory(path)) { - ERROR_LOG(COMMON, "DeleteDir: Not a directory %s", filename.c_str()); + ERROR_LOG(COMMON, "DeleteDir: Not a directory %s", path.c_str()); return false; } #ifdef _WIN32 - if (::RemoveDirectory(ConvertUTF8ToWString(filename).c_str())) + if (::RemoveDirectory(path.ToWString().c_str())) return true; #else - if (rmdir(filename.c_str()) == 0) + if (rmdir(path.c_str()) == 0) return true; #endif - ERROR_LOG(COMMON, "DeleteDir: %s: %s", filename.c_str(), GetLastErrorMsg().c_str()); + ERROR_LOG(COMMON, "DeleteDir: %s: %s", path.c_str(), GetLastErrorMsg().c_str()); return false; } // renames file srcFilename to destFilename, returns true on success -bool Rename(const std::string &srcFilename, const std::string &destFilename) +bool Rename(const Path &srcFilename, const Path &destFilename) { - INFO_LOG(COMMON, "Rename: %s --> %s", - srcFilename.c_str(), destFilename.c_str()); + if (srcFilename.Type() != destFilename.Type()) { + // Impossible. + return false; + } + + switch (srcFilename.Type()) { + case PathType::NATIVE: + break; // OK + default: + return false; + } + + INFO_LOG(COMMON, "Rename: %s --> %s", srcFilename.c_str(), destFilename.c_str()); #if defined(_WIN32) && defined(UNICODE) - std::wstring srcw = ConvertUTF8ToWString(srcFilename); - std::wstring destw = ConvertUTF8ToWString(destFilename); + std::wstring srcw = srcFilename.ToWString(); + std::wstring destw = destFilename.ToWString(); if (_wrename(srcw.c_str(), destw.c_str()) == 0) return true; #else if (rename(srcFilename.c_str(), destFilename.c_str()) == 0) return true; #endif + ERROR_LOG(COMMON, "Rename: failed %s --> %s: %s", srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg().c_str()); return false; } // copies file srcFilename to destFilename, returns true on success -bool Copy(const std::string &srcFilename, const std::string &destFilename) +bool Copy(const Path &srcFilename, const Path &destFilename) { - INFO_LOG(COMMON, "Copy: %s --> %s", - srcFilename.c_str(), destFilename.c_str()); + switch (srcFilename.Type()) { + case PathType::NATIVE: + break; // OK + default: + return false; + } + switch (destFilename.Type()) { + case PathType::NATIVE: + break; // OK + default: + return false; + } + + INFO_LOG(COMMON, "Copy: %s --> %s", srcFilename.c_str(), destFilename.c_str()); #ifdef _WIN32 #if PPSSPP_PLATFORM(UWP) - if (CopyFile2(ConvertUTF8ToWString(srcFilename).c_str(), ConvertUTF8ToWString(destFilename).c_str(), nullptr)) + if (CopyFile2(srcFilename.ToWString().c_str(), destFilename.ToWString().c_str(), nullptr)) return true; return false; #else - if (CopyFile(ConvertUTF8ToWString(srcFilename).c_str(), ConvertUTF8ToWString(destFilename).c_str(), FALSE)) + if (CopyFile(srcFilename.ToWString().c_str(), destFilename.ToWString().c_str(), FALSE)) return true; #endif ERROR_LOG(COMMON, "Copy: failed %s --> %s: %s", @@ -441,23 +496,21 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename) #else // buffer size -#define BSIZE 1024 +#define BSIZE 4096 char buffer[BSIZE]; // Open input file - FILE *input = fopen(srcFilename.c_str(), "rb"); - if (!input) - { + FILE *input = OpenCFile(srcFilename, "rb"); + if (!input) { ERROR_LOG(COMMON, "Copy: input failed %s --> %s: %s", srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg().c_str()); return false; } // open output file - FILE *output = fopen(destFilename.c_str(), "wb"); - if (!output) - { + FILE *output = OpenCFile(destFilename, "wb"); + if (!output) { fclose(input); ERROR_LOG(COMMON, "Copy: output failed %s --> %s: %s", srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg().c_str()); @@ -465,14 +518,11 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename) } // copy loop - while (!feof(input)) - { + while (!feof(input)) { // read input int rnum = fread(buffer, sizeof(char), BSIZE, input); - if (rnum != BSIZE) - { - if (ferror(input) != 0) - { + if (rnum != BSIZE) { + if (ferror(input) != 0) { ERROR_LOG(COMMON, "Copy: failed reading from source, %s --> %s: %s", srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg().c_str()); @@ -484,8 +534,7 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename) // write output int wnum = fwrite(buffer, sizeof(char), rnum, output); - if (wnum != rnum) - { + if (wnum != rnum) { ERROR_LOG(COMMON, "Copy: failed writing to output, %s --> %s: %s", srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg().c_str()); @@ -494,112 +543,29 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename) return false; } } - // close flushs + // close flushes fclose(input); fclose(output); return true; #endif } -#ifdef _WIN32 - -static int64_t FiletimeToStatTime(FILETIME ft) { - const int windowsTickResolution = 10000000; - const int64_t secToUnixEpoch = 11644473600LL; - int64_t ticks = ((uint64_t)ft.dwHighDateTime << 32) | ft.dwLowDateTime; - return (int64_t)(ticks / windowsTickResolution - secToUnixEpoch); -} - -#endif - -// Returns file attributes. -bool GetFileDetails(const std::string &filename, FileDetails *details) { -#ifdef _WIN32 - WIN32_FILE_ATTRIBUTE_DATA attr; - if (!GetFileAttributesEx(ConvertUTF8ToWString(filename).c_str(), GetFileExInfoStandard, &attr)) - return false; - details->isDirectory = (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; - details->size = ((uint64_t)attr.nFileSizeHigh << 32) | (uint64_t)attr.nFileSizeLow; - details->atime = FiletimeToStatTime(attr.ftLastAccessTime); - details->mtime = FiletimeToStatTime(attr.ftLastWriteTime); - details->ctime = FiletimeToStatTime(attr.ftCreationTime); - if (attr.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { - details->access = 0444; // Read - } else { - details->access = 0666; // Read/Write - } - if (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - details->access |= 0111; // Execute - } - return true; -#else - if (!Exists(filename)) { - return false; - } -#if __ANDROID__ && __ANDROID_API__ < 21 - struct stat buf; - if (stat(filename.c_str(), &buf) == 0) { -#else - struct stat64 buf; - if (stat64(filename.c_str(), &buf) == 0) { -#endif - details->size = buf.st_size; - details->isDirectory = S_ISDIR(buf.st_mode); - details->atime = buf.st_atime; - details->mtime = buf.st_mtime; - details->ctime = buf.st_ctime; - details->access = buf.st_mode & 0x1ff; +bool Move(const Path &srcFilename, const Path &destFilename) { + if (Rename(srcFilename, destFilename)) { return true; + } else if (Copy(srcFilename, destFilename)) { + return Delete(srcFilename); } else { return false; } -#endif -} - -bool GetModifTime(const std::string &filename, tm &return_time) { - memset(&return_time, 0, sizeof(return_time)); - FileDetails details; - if (GetFileDetails(filename, &details)) { - time_t t = details.mtime; - localtime_r((time_t*)&t, &return_time); - return true; - } else { - return false; - } -} - -std::string GetDir(const std::string &path) { - if (path == "/") - return path; - int n = (int)path.size() - 1; - while (n >= 0 && path[n] != '\\' && path[n] != '/') - n--; - std::string cutpath = n > 0 ? path.substr(0, n) : ""; - for (size_t i = 0; i < cutpath.size(); i++) { - if (cutpath[i] == '\\') cutpath[i] = '/'; - } -#ifndef _WIN32 - if (!cutpath.size()) { - return "/"; - } -#endif - return cutpath; -} - -std::string GetFilename(std::string path) { - size_t off = GetDir(path).size() + 1; - if (off < path.size()) - return path.substr(off); - else - return path; } // Returns the size of file (64bit) // TODO: Add a way to return an error. -uint64_t GetFileSize(const std::string &filename) { +uint64_t GetFileSize(const Path &filename) { #if defined(_WIN32) && defined(UNICODE) WIN32_FILE_ATTRIBUTE_DATA attr; - if (!GetFileAttributesEx(ConvertUTF8ToWString(filename).c_str(), GetFileExInfoStandard, &attr)) + if (!GetFileAttributesEx(filename.ToWString().c_str(), GetFileExInfoStandard, &attr)) return 0; if (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) return 0; @@ -613,14 +579,14 @@ uint64_t GetFileSize(const std::string &filename) { int result = stat64(filename.c_str(), &file_info); #endif if (result != 0) { - WARN_LOG(COMMON, "GetSize: failed %s: No such file", filename.c_str()); + WARN_LOG(COMMON, "GetSize: failed %s: No such file", filename.ToVisualString().c_str()); return 0; } if (S_ISDIR(file_info.st_mode)) { - WARN_LOG(COMMON, "GetSize: failed %s: is a directory", filename.c_str()); + WARN_LOG(COMMON, "GetSize: failed %s: is a directory", filename.ToVisualString().c_str()); return 0; } - DEBUG_LOG(COMMON, "GetSize: %s: %lld", filename.c_str(), (long long)file_info.st_size); + DEBUG_LOG(COMMON, "GetSize: %s: %lld", filename.ToVisualString().c_str(), (long long)file_info.st_size); return file_info.st_size; #endif } @@ -665,10 +631,9 @@ uint64_t GetFileSize(FILE *f) } // creates an empty file filename, returns true on success -bool CreateEmptyFile(const std::string &filename) -{ - INFO_LOG(COMMON, "CreateEmptyFile: %s", filename.c_str()); +bool CreateEmptyFile(const Path &filename) { + INFO_LOG(COMMON, "CreateEmptyFile: %s", filename.c_str()); FILE *pFile = OpenCFile(filename, "wb"); if (!pFile) { ERROR_LOG(COMMON, "CreateEmptyFile: failed %s: %s", filename.c_str(), GetLastErrorMsg().c_str()); @@ -679,8 +644,7 @@ bool CreateEmptyFile(const std::string &filename) } // Deletes the given directory and anything under it. Returns true on success. -bool DeleteDirRecursively(const std::string &directory) -{ +bool DeleteDirRecursively(const Path &directory) { //Removed check, it prevents the UWP from deleting store downloads INFO_LOG(COMMON, "DeleteDirRecursively: %s", directory.c_str()); @@ -688,16 +652,13 @@ bool DeleteDirRecursively(const std::string &directory) // Find the first file in the directory. WIN32_FIND_DATA ffd; - HANDLE hFind = FindFirstFile(ConvertUTF8ToWString(directory + "\\*").c_str(), &ffd); - - if (hFind == INVALID_HANDLE_VALUE) - { + HANDLE hFind = FindFirstFile((directory.ToWString() + L"\\*").c_str(), &ffd); + if (hFind == INVALID_HANDLE_VALUE) { return false; } // windows loop - do - { + do { const std::string virtualName = ConvertWStringToUTF8(ffd.cFileName); #else struct dirent *result = NULL; @@ -706,8 +667,7 @@ bool DeleteDirRecursively(const std::string &directory) return false; // non windows loop - while ((result = readdir(dirp))) - { + while ((result = readdir(dirp))) { const std::string virtualName = result->d_name; #endif // check for "." and ".." @@ -716,11 +676,9 @@ bool DeleteDirRecursively(const std::string &directory) (virtualName[2] == '\0'))) continue; - std::string newPath = directory + DIR_SEP + virtualName; - if (IsDirectory(newPath)) - { - if (!DeleteDirRecursively(newPath)) - { + Path newPath = directory / virtualName; + if (IsDirectory(newPath)) { + if (!DeleteDirRecursively(newPath)) { #ifndef _WIN32 closedir(dirp); #else @@ -729,10 +687,8 @@ bool DeleteDirRecursively(const std::string &directory) return false; } } - else - { - if (!File::Delete(newPath)) - { + else { + if (!File::Delete(newPath)) { #ifndef _WIN32 closedir(dirp); #else @@ -752,52 +708,12 @@ bool DeleteDirRecursively(const std::string &directory) return File::DeleteDir(directory); } - -// Create directory and copy contents (does not overwrite existing files) -void CopyDir(const std::string &source_path, const std::string &dest_path) -{ -#ifndef _WIN32 - if (source_path == dest_path) return; - if (!File::Exists(source_path)) return; - if (!File::Exists(dest_path)) File::CreateFullPath(dest_path); - - struct dirent *result = NULL; - DIR *dirp = opendir(source_path.c_str()); - if (!dirp) return; - - while ((result = readdir(dirp))) - { - const std::string virtualName(result->d_name); - // check for "." and ".." - if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || - ((virtualName[0] == '.') && (virtualName[1] == '.') && - (virtualName[2] == '\0'))) - continue; - - std::string source, dest; - source = source_path + virtualName; - dest = dest_path + virtualName; - if (IsDirectory(source)) - { - source += '/'; - dest += '/'; - if (!File::Exists(dest)) File::CreateFullPath(dest); - CopyDir(source, dest); - } - else if (!File::Exists(dest)) File::Copy(source, dest); - } - closedir(dirp); -#else - ERROR_LOG(COMMON, "CopyDir not supported on this platform"); -#endif -} - -void openIniFile(const std::string& fileName) { +void OpenFileInEditor(const Path &fileName) { #if defined(_WIN32) #if PPSSPP_PLATFORM(UWP) // Do nothing. #else - ShellExecuteW(nullptr, L"open", ConvertUTF8ToWString(fileName).c_str(), nullptr, nullptr, SW_SHOW); + ShellExecuteW(nullptr, L"open", fileName.ToWString().c_str(), nullptr, nullptr, SW_SHOW); #endif #elif !defined(MOBILE_DEVICE) std::string iniFile; @@ -806,7 +722,7 @@ void openIniFile(const std::string& fileName) { #else iniFile = "xdg-open "; #endif - iniFile.append(fileName); + iniFile.append(fileName.ToString()); NOTICE_LOG(BOOT, "Launching %s", iniFile.c_str()); int retval = system(iniFile.c_str()); if (retval != 0) { @@ -815,22 +731,17 @@ void openIniFile(const std::string& fileName) { #endif } -const std::string &GetExeDirectory() -{ - static std::string ExePath; +const Path &GetExeDirectory() { + static Path ExePath; if (ExePath.empty()) { #ifdef _WIN32 -#ifdef UNICODE std::wstring program_path; -#else - std::string program_path; -#endif size_t sz; do { program_path.resize(program_path.size() + MAX_PATH); // On failure, this will return the same value as passed in, but success will always be one lower. - sz = GetModuleFileName(nullptr, &program_path[0], (DWORD)program_path.size()); + sz = GetModuleFileNameW(nullptr, &program_path[0], (DWORD)program_path.size()); } while (sz >= program_path.size()); const wchar_t *last_slash = wcsrchr(&program_path[0], '\\'); @@ -838,11 +749,7 @@ const std::string &GetExeDirectory() program_path.resize(last_slash - &program_path[0] + 1); else program_path.resize(sz); -#ifdef UNICODE - ExePath = ConvertWStringToUTF8(program_path); -#else - ExePath = program_path; -#endif + ExePath = Path(program_path); #elif (defined(__APPLE__) && !PPSSPP_PLATFORM(IOS)) || defined(__linux__) || defined(KERN_PROC_PATHNAME) char program_path[4096]; @@ -874,9 +781,9 @@ const std::string &GetExeDirectory() { program_path[sizeof(program_path) - 1] = '\0'; char *last_slash = strrchr(program_path, '/'); - if (last_slash != NULL) - *(last_slash + 1) = '\0'; - ExePath = program_path; + if (last_slash != nullptr) + *last_slash = '\0'; + ExePath = Path(program_path); } #endif } @@ -884,27 +791,33 @@ const std::string &GetExeDirectory() return ExePath; } - -IOFile::IOFile() - : m_file(NULL), m_good(true) -{} +IOFile::IOFile() {} IOFile::IOFile(std::FILE* file) - : m_file(file), m_good(true) + : m_file(file) {} -IOFile::IOFile(const std::string& filename, const char openmode[]) - : m_file(NULL), m_good(true) -{ +IOFile::IOFile(const std::string &filename, const char openmode[]) { Open(filename, openmode); } -IOFile::~IOFile() -{ +IOFile::IOFile(const Path &filename, const char openmode[]) { + Open(filename.ToString(), openmode); +} + +IOFile::~IOFile() { Close(); } bool IOFile::Open(const std::string& filename, const char openmode[]) +{ + Close(); + m_file = File::OpenCFile(Path(filename), openmode); + m_good = IsOpen(); + return m_good; +} + +bool IOFile::Open(const Path& filename, const char openmode[]) { Close(); m_file = File::OpenCFile(filename, openmode); @@ -984,24 +897,38 @@ bool IOFile::Resize(uint64_t size) return m_good; } -} // namespace - -bool readFileToString(bool text_file, const char *filename, std::string & str) -{ +bool ReadFileToString(bool text_file, const Path &filename, std::string &str) { FILE *f = File::OpenCFile(filename, text_file ? "r" : "rb"); if (!f) return false; + // Warning: some files, like in /sys/, may return a fixed size like 4096. size_t len = (size_t)File::GetFileSize(f); - char *buf = new char[len + 1]; - buf[fread(buf, 1, len, f)] = 0; - str = std::string(buf, len); + bool success; + if (len == -1) { + size_t totalSize = 1024; + size_t totalRead = 0; + do { + totalSize *= 2; + str.resize(totalSize); + totalRead += fread(&str[totalRead], 1, totalSize - totalRead, f); + } while (totalRead == totalSize); + str.resize(totalRead); + success = true; + } else { + str.resize(len); + size_t totalRead = fread(&str[0], 1, len, f); + str.resize(totalRead); + // Allow less, because some system files will report incorrect lengths. + success = totalRead <= len; + } fclose(f); - delete[] buf; - return true; + return success; } +// This is an odd one, mainly used for asset reading, so doesn't really +// need to support Path. uint8_t *ReadLocalFile(const char *filename, size_t * size) { - FILE *file = File::OpenCFile(filename, "rb"); + FILE *file = File::OpenCFile(Path(filename), "rb"); if (!file) { *size = 0; return nullptr; @@ -1027,8 +954,7 @@ uint8_t *ReadLocalFile(const char *filename, size_t * size) { return contents; } -bool writeStringToFile(bool text_file, const std::string &str, const char *filename) -{ +bool WriteStringToFile(bool text_file, const std::string &str, const Path &filename) { FILE *f = File::OpenCFile(filename, text_file ? "w" : "wb"); if (!f) return false; @@ -1042,8 +968,7 @@ bool writeStringToFile(bool text_file, const std::string &str, const char *filen return true; } -bool writeDataToFile(bool text_file, const void* data, const unsigned int size, const char *filename) -{ +bool WriteDataToFile(bool text_file, const void* data, const unsigned int size, const Path &filename) { FILE *f = File::OpenCFile(filename, text_file ? "w" : "wb"); if (!f) return false; @@ -1056,3 +981,5 @@ bool writeDataToFile(bool text_file, const void* data, const unsigned int size, fclose(f); return true; } + +} // namespace File diff --git a/Common/File/FileUtil.h b/Common/File/FileUtil.h index a191f554eb..1933f5893c 100644 --- a/Common/File/FileUtil.h +++ b/Common/File/FileUtil.h @@ -25,6 +25,7 @@ #include #include "Common/Common.h" +#include "Common/File/Path.h" #ifdef _MSC_VER inline struct tm* localtime_r(const time_t *clock, struct tm *result) { @@ -36,85 +37,70 @@ inline struct tm* localtime_r(const time_t *clock, struct tm *result) { namespace File { -struct FileDetails { - bool isDirectory; - uint64_t size; - uint64_t atime; - uint64_t mtime; - uint64_t ctime; - uint32_t access; // st_mode & 0x1ff -}; - -// Mostly to handle utf-8 filenames better on Windows. -FILE *OpenCFile(const std::string &filename, const char *mode); -bool OpenCPPFile(std::fstream & stream, const std::string &filename, std::ios::openmode mode); +// Mostly to handle UTF-8 filenames better on Windows. +FILE *OpenCFile(const Path &filename, const char *mode); // Resolves symlinks and similar. std::string ResolvePath(const std::string &path); // Returns true if file filename exists -bool Exists(const std::string &filename); +bool Exists(const Path &path); -// Returns true if filename is a directory -bool IsDirectory(const std::string &filename); +// Returns true if file filename exists in directory path. +bool ExistsInDir(const Path &path, const std::string &filename); -// Returns file attributes. -bool GetFileDetails(const std::string &filename, FileDetails *details); - -// Extracts the directory from a path. -std::string GetDir(const std::string &path); - -// Extracts the filename from a path. -std::string GetFilename(std::string path); +// Returns true if filename exists, and is a directory +// Supports Android content URIs. +bool IsDirectory(const Path &filename); // Returns struct with modification date of file -bool GetModifTime(const std::string &filename, tm &return_time); +bool GetModifTime(const Path &filename, tm &return_time); // Returns the size of filename (64bit) -uint64_t GetFileSize(const std::string &filename); +uint64_t GetFileSize(const Path &filename); // Overloaded GetSize, accepts FILE* uint64_t GetFileSize(FILE *f); // Returns true if successful, or path already exists. -bool CreateDir(const std::string &filename); +bool CreateDir(const Path &filename); // Creates the full path of fullPath returns true on success -bool CreateFullPath(const std::string &fullPath); +bool CreateFullPath(const Path &fullPath); // Deletes a given filename, return true on success // Doesn't supports deleting a directory -bool Delete(const std::string &filename); +bool Delete(const Path &filename); // Deletes a directory filename, returns true on success -bool DeleteDir(const std::string &filename); +// Directory must be empty. +bool DeleteDir(const Path &filename); -// renames file srcFilename to destFilename, returns true on success -bool Rename(const std::string &srcFilename, const std::string &destFilename); +// Deletes the given directory and anything under it. Returns true on success. +bool DeleteDirRecursively(const Path &directory); + +// Renames file srcFilename to destFilename, returns true on success +// Will usually only work with in the same partition or other unit of storage, +// so you might have to fall back to copy/delete. +bool Rename(const Path &srcFilename, const Path &destFilename); // copies file srcFilename to destFilename, returns true on success -bool Copy(const std::string &srcFilename, const std::string &destFilename); +bool Copy(const Path &srcFilename, const Path &destFilename); + +// Tries to rename srcFilename to destFilename, if that fails, +// it tries to copy and delete the src if succeeded. If that fails too, +// returns false, otherwise returns true. +bool Move(const Path &srcFilename, const Path &destFilename); // creates an empty file filename, returns true on success -bool CreateEmptyFile(const std::string &filename); - -// deletes the given directory and anything under it. Returns true on success. -bool DeleteDirRecursively(const std::string &directory); - -// Returns the current directory -std::string GetCurrentDir(); - -// Create directory and copy contents (does not overwrite existing files) -void CopyDir(const std::string &source_path, const std::string &dest_path); +bool CreateEmptyFile(const Path &filename); // Opens ini file (cheats, texture replacements etc.) -void openIniFile(const std::string& fileName); - -// Set the current directory to given directory -bool SetCurrentDir(const std::string &directory); - -const std::string &GetExeDirectory(); +// TODO: Belongs in System or something. +void OpenFileInEditor(const Path &fileName); +// TODO: Belongs in System or something. +const Path &GetExeDirectory(); // simple wrapper for cstdlib file functions to // hopefully will make error checking easier @@ -123,14 +109,14 @@ class IOFile { public: IOFile(); IOFile(FILE* file); - IOFile(const std::string& filename, const char openmode[]); + IOFile(const Path &filename, const char openmode[]); ~IOFile(); // Prevent copies. IOFile(const IOFile &) = delete; void operator=(const IOFile &) = delete; - bool Open(const std::string& filename, const char openmode[]); + bool Open(const Path &filename, const char openmode[]); bool Close(); template @@ -161,11 +147,11 @@ public: return WriteArray(reinterpret_cast(data), length); } - bool IsOpen() { return NULL != m_file; } + bool IsOpen() const { return nullptr != m_file; } // m_good is set to false when a read, write or other function fails - bool IsGood() { return m_good; } - operator void*() { return m_good ? m_file : NULL; } + bool IsGood() const { return m_good; } + operator bool() const { return IsGood() && IsOpen(); } std::FILE* ReleaseHandle(); @@ -187,18 +173,21 @@ public: } private: - std::FILE *m_file; - bool m_good; -}; + IOFile(const std::string &filename, const char openmode[]); + bool Open(const std::string &filename, const char openmode[]); -} // namespace + std::FILE *m_file = nullptr; + bool m_good = true; +}; // TODO: Refactor, this was moved from the old file_util.cpp. // Whole-file reading/writing -bool writeStringToFile(bool text_file, const std::string &str, const char *filename); -bool writeDataToFile(bool text_file, const void* data, const unsigned int size, const char *filename); +bool WriteStringToFile(bool text_file, const std::string &str, const Path &filename); +bool WriteDataToFile(bool text_file, const void* data, const unsigned int size, const Path &filename); -bool readFileToString(bool text_file, const char *filename, std::string &str); +bool ReadFileToString(bool text_file, const Path &filename, std::string &str); // Return value must be delete[]-d. uint8_t *ReadLocalFile(const char *filename, size_t *size); + +} // namespace diff --git a/Common/File/Path.cpp b/Common/File/Path.cpp new file mode 100644 index 0000000000..0f340dda06 --- /dev/null +++ b/Common/File/Path.cpp @@ -0,0 +1,216 @@ +#include "ppsspp_config.h" +#include + +#include "Common/File/Path.h" +#include "Common/StringUtils.h" +#include "Common/Log.h" +#include "Common/Data/Encoding/Utf8.h" + +Path::Path(const std::string &str) { + if (str.empty()) { + type_ = PathType::UNDEFINED; + } else if (startsWith(str, "http://") || startsWith(str, "https://")) { + type_ = PathType::HTTP; + } else { + type_ = PathType::NATIVE; + } + + Init(str); +} + +#if PPSSPP_PLATFORM(WINDOWS) +Path::Path(const std::wstring &str) { + type_ = PathType::NATIVE; + Init(ConvertWStringToUTF8(str)); +} +#endif + +void Path::Init(const std::string &str) { + path_ = str; + +#if PPSSPP_PLATFORM(WINDOWS) + // Flip all the slashes around. We flip them back on ToWString(). + for (size_t i = 0; i < path_.size(); i++) { + if (path_[i] == '\\') { + path_[i] = '/'; + } + } +#endif + + // Don't pop_back if it's just "/". + if (type_ == PathType::NATIVE && path_.size() > 1 && path_.back() == '/') { + path_.pop_back(); + } +} + +// We always use forward slashes internally, we convert to backslash only when +// converted to a wstring. +Path Path::operator /(const std::string &subdir) const { + if (subdir.empty()) { + return Path(path_); + } + std::string fullPath = path_; + if (subdir.front() != '/') { + fullPath += "/"; + } + fullPath += subdir; + // Prevent adding extra slashes. + if (fullPath.back() == '/') { + fullPath.pop_back(); + } + return Path(fullPath); +} + +void Path::operator /=(const std::string &subdir) { + *this = *this / subdir; +} + +Path Path::WithExtraExtension(const std::string &ext) const { + _dbg_assert_(!ext.empty() && ext[0] == '.'); + return Path(path_ + ext); +} + +Path Path::WithReplacedExtension(const std::string &oldExtension, const std::string &newExtension) const { + _dbg_assert_(!oldExtension.empty() && oldExtension[0] == '.'); + _dbg_assert_(!newExtension.empty() && newExtension[0] == '.'); + if (endsWithNoCase(path_, oldExtension)) { + std::string newPath = path_.substr(0, path_.size() - oldExtension.size()); + return Path(newPath + newExtension); + } else { + return Path(*this); + } +} + +Path Path::WithReplacedExtension(const std::string &newExtension) const { + _dbg_assert_(!newExtension.empty() && newExtension[0] == '.'); + if (path_.empty()) { + return Path(*this); + } + std::string extension = GetFileExtension(); + std::string newPath = path_.substr(0, path_.size() - extension.size()) + newExtension; + return Path(newPath); +} + +std::string Path::GetFilename() const { + size_t pos = path_.rfind('/'); + if (pos != std::string::npos) { + return path_.substr(pos + 1); + } + return path_; +} + +std::string Path::GetFileExtension() const { + size_t pos = path_.rfind("."); + if (pos == std::string::npos) { + return ""; + } + size_t slash_pos = path_.rfind("/"); + if (slash_pos != std::string::npos && slash_pos > pos) { + // Don't want to detect "df/file" from "/as.df/file" + return ""; + } + std::string ext = path_.substr(pos); + for (size_t i = 0; i < ext.size(); i++) { + ext[i] = tolower(ext[i]); + } + return ext; +} + +std::string Path::GetDirectory() const { + size_t pos = path_.rfind('/'); + if (type_ == PathType::HTTP) { + // Things are a bit different for HTTP, because we probably ended with /. + if (pos + 1 == path_.size()) { + pos = path_.rfind('/', pos - 1); + if (pos != path_.npos && pos > 8) { + return path_.substr(0, pos + 1); + } + } + } + + if (pos != std::string::npos) { + if (pos == 0) { + return "/"; // We're at the root. + } + return path_.substr(0, pos); +#if PPSSPP_PLATFORM(WINDOWS) + } else if (path_.size() == 2 && path_[1] == ':') { + // Windows fake-root. + return "/"; +#endif + } else { + // There could be a ':', too. Unlike the slash, let's include that + // in the returned directory. + size_t c_pos = path_.rfind(':'); + if (c_pos != std::string::npos) { + return path_.substr(0, c_pos + 1); + } + } + // No directory components, we're a relative path. + return path_; +} + +bool Path::FilePathContains(const std::string &needle) const { + const std::string &haystack = path_; + return haystack.find(needle) != std::string::npos; +} + +bool Path::StartsWith(const Path &other) const { + if (type_ != other.type_) { + // Bad + return false; + } + return startsWith(path_, other.path_); +} + +const std::string &Path::ToString() const { + return path_; +} + +#if PPSSPP_PLATFORM(WINDOWS) +std::wstring Path::ToWString() const { + std::wstring w = ConvertUTF8ToWString(path_); + for (size_t i = 0; i < w.size(); i++) { + if (w[i] == '/') { + w[i] = '\\'; + } + } + return w; +} +#endif + +std::string Path::ToVisualString() const { + return path_; +} + +bool Path::CanNavigateUp() const { + if (path_ == "/" || path_ == "") { + return false; + } + if (type_ == PathType::HTTP) { + size_t rootSlash = path_.find_first_of('/', strlen("https://")); + if (rootSlash == path_.npos || path_.size() < rootSlash + 1) { + // This means, "http://server" or "http://server/". Can't go up. + return false; + } + } + return true; +} + +Path Path::NavigateUp() const { + std::string dir = GetDirectory(); + return Path(dir); +} + +bool Path::IsAbsolute() const { + if (path_.empty()) + return true; + else if (path_.front() == '/') + return true; +#if PPSSPP_PLATFORM(WINDOWS) + else if (path_.size() > 3 && path_[1] == ':') + return true; // Windows path with drive letter +#endif + else + return false; +} diff --git a/Common/File/Path.h b/Common/File/Path.h new file mode 100644 index 0000000000..258287f8cb --- /dev/null +++ b/Common/File/Path.h @@ -0,0 +1,104 @@ +#pragma once + +#include "ppsspp_config.h" + +#include + +enum class PathType { + UNDEFINED = 0, + NATIVE = 1, + // RELATIVE, // (do we need this?) + // CONTENT_URI = 2, // Android only + HTTP = 3, // http://, https:// +}; + +// Windows paths are always stored with '/' slashes in a Path. +// On .ToWString(), they are flipped back to '\'. + +class Path { +private: + void Init(const std::string &str); + +public: + Path() : type_(PathType::UNDEFINED) {} + explicit Path(const std::string &str); + +#if PPSSPP_PLATFORM(WINDOWS) + explicit Path(const std::wstring &str); +#endif + + PathType Type() const { + return type_; + } + + bool Valid() const { return !path_.empty(); } + bool IsRoot() const { return path_ == "/"; } // Special value - only path that can end in a slash. + + // Some std::string emulation for simplicity. + bool empty() const { return !Valid(); } + void clear() { + type_ = PathType::UNDEFINED; + path_.clear(); + } + size_t size() const { + return path_.size(); + } + + // WARNING: Unsafe usage. + const char *c_str() const { + return path_.c_str(); + } + + bool IsAbsolute() const; + + // Returns a path extended with a subdirectory. + Path operator /(const std::string &subdir) const; + + // Navigates down into a subdir. + void operator /=(const std::string &subdir); + + // File extension manipulation. + Path WithExtraExtension(const std::string &ext) const; + Path WithReplacedExtension(const std::string &oldExtension, const std::string &newExtension) const; + Path WithReplacedExtension(const std::string &newExtension) const; + + // Removes the last component. + std::string GetFilename() const; // Really, GetLastComponent. Could be a file or directory. Includes the extension. + std::string GetFileExtension() const; // Always lowercase return. Includes the dot. + std::string GetDirectory() const; + + const std::string &ToString() const; + +#if PPSSPP_PLATFORM(WINDOWS) + std::wstring ToWString() const; +#endif + + std::string ToVisualString() const; + + bool CanNavigateUp() const; + Path NavigateUp() const; + + bool operator ==(const Path &other) const { + return path_ == other.path_ && type_ == other.type_; + } + bool operator !=(const Path &other) const { + return path_ != other.path_ || type_ != other.type_; + } + + bool FilePathContains(const std::string &needle) const; + + bool StartsWith(const Path &other) const; + + bool operator <(const Path &other) const { + return path_ < other.path_; + } + +private: + // The internal representation is currently always the plain string. + // For CPU efficiency we could keep an AndroidStorageContentURI too, + // but I don't think the encode/decode cost is significant. We simply create + // those for processing instead. + std::string path_; + + PathType type_; +}; diff --git a/Common/File/PathBrowser.cpp b/Common/File/PathBrowser.cpp index 26dce5e338..2ef8c092ec 100644 --- a/Common/File/PathBrowser.cpp +++ b/Common/File/PathBrowser.cpp @@ -8,6 +8,7 @@ #include "Common/Net/URL.h" #include "Common/File/PathBrowser.h" +#include "Common/File/FileUtil.h" #include "Common/StringUtils.h" #include "Common/TimeUtil.h" #include "Common/Log.h" @@ -15,13 +16,17 @@ #include "Core/System.h" -bool LoadRemoteFileList(const std::string &url, bool *cancel, std::vector &files) { +bool LoadRemoteFileList(const Path &url, bool *cancel, std::vector &files) { + _dbg_assert_(url.Type() == PathType::HTTP); + http::Client http; Buffer result; int code = 500; std::vector responseHeaders; - Url baseURL(url); + http.SetUserAgent(StringFromFormat("PPSSPP/%s", PPSSPP_GIT_VERSION)); + + Url baseURL(url.ToString()); if (!baseURL.Valid()) { return false; } @@ -29,7 +34,8 @@ bool LoadRemoteFileList(const std::string &url, bool *cancel, std::vector ApplyFilter(std::vector files, const char *filter) { +std::vector ApplyFilter(std::vector files, const char *filter) { std::set filters; if (filter) { std::string tmp; while (*filter) { if (*filter == ':') { - filters.insert(std::move(tmp)); + filters.insert("." + tmp); + tmp.clear(); } else { tmp.push_back(*filter); } filter++; } if (!tmp.empty()) - filters.insert(std::move(tmp)); + filters.insert("." + tmp); } - auto pred = [&](const FileInfo &info) { + auto pred = [&](const File::FileInfo &info) { if (info.isDirectory || !filter) return false; - std::string ext = getFileExtension(info.fullName); + std::string ext = info.fullName.GetFileExtension(); return filters.find(ext) == filters.end(); }; files.erase(std::remove_if(files.begin(), files.end(), pred), files.end()); @@ -129,30 +136,20 @@ PathBrowser::~PathBrowser() { } } -// Normalize slashes. -void PathBrowser::SetPath(const std::string &path) { - if (path[0] == '!') { - path_ = path; - HandlePath(); - return; - } +void PathBrowser::SetPath(const Path &path) { path_ = path; - for (size_t i = 0; i < path_.size(); i++) { - if (path_[i] == '\\') path_[i] = '/'; - } - if (!path_.size() || (path_[path_.size() - 1] != '/')) - path_ += "/"; HandlePath(); } void PathBrowser::HandlePath() { - if (!path_.empty() && path_[0] == '!') { + if (!path_.empty() && path_.ToString()[0] == '!') { if (pendingActive_) ResetPending(); ready_ = true; return; } - if (!startsWith(path_, "http://") && !startsWith(path_, "https://")) { + + if (path_.Type() != PathType::HTTP) { if (pendingActive_) ResetPending(); ready_ = true; @@ -171,18 +168,18 @@ void PathBrowser::HandlePath() { return; pendingThread_ = std::thread([&] { - setCurrentThreadName("PathBrowser"); + SetCurrentThreadName("PathBrowser"); std::unique_lock guard(pendingLock_); - std::vector results; - std::string lastPath; + std::vector results; + Path lastPath; while (!pendingStop_) { while (lastPath == pendingPath_ && !pendingCancel_) { pendingCond_.wait(guard); } lastPath = pendingPath_; bool success = false; - if (!lastPath.empty()) { + if (lastPath.Type() == PathType::HTTP) { guard.unlock(); results.clear(); success = LoadRemoteFileList(lastPath, &pendingCancel_, results); @@ -212,13 +209,9 @@ bool PathBrowser::IsListingReady() { } std::string PathBrowser::GetFriendlyPath() const { - std::string str = GetPath(); + std::string str = GetPath().ToVisualString(); // Show relative to memstick root if there. - std::string root = GetSysDirectory(DIRECTORY_MEMSTICK_ROOT); - for (size_t i = 0; i < root.size(); i++) { - if (root[i] == '\\') - root[i] = '/'; - } + std::string root = GetSysDirectory(DIRECTORY_MEMSTICK_ROOT).ToVisualString(); if (startsWith(str, root)) { return std::string("ms:/") + str.substr(root.size()); @@ -233,7 +226,7 @@ std::string PathBrowser::GetFriendlyPath() const { return str; } -bool PathBrowser::GetListing(std::vector &fileInfo, const char *filter, bool *cancel) { +bool PathBrowser::GetListing(std::vector &fileInfo, const char *filter, bool *cancel) { std::unique_lock guard(pendingLock_); while (!IsListingReady() && (!cancel || !*cancel)) { // In case cancel changes, just sleep. @@ -243,14 +236,14 @@ bool PathBrowser::GetListing(std::vector &fileInfo, const char *filter } #ifdef _WIN32 - if (path_ == "/") { + if (path_.IsRoot()) { // Special path that means root of file system. - std::vector drives = getWindowsDrives(); + std::vector drives = File::GetWindowsDrives(); for (auto drive = drives.begin(); drive != drives.end(); ++drive) { if (*drive == "A:/" || *drive == "B:/") continue; - FileInfo fake; - fake.fullName = *drive; + File::FileInfo fake; + fake.fullName = Path(*drive); fake.name = *drive; fake.isDirectory = true; fake.exists = true; @@ -261,11 +254,11 @@ bool PathBrowser::GetListing(std::vector &fileInfo, const char *filter } #endif - if (startsWith(path_, "http://") || startsWith(path_, "https://")) { + if (path_.Type() == PathType::HTTP) { fileInfo = ApplyFilter(pendingFiles_, filter); return true; } else { - getFilesInDir(path_.c_str(), &fileInfo, filter); + File::GetFilesInDir(path_, &fileInfo, filter); return true; } } @@ -279,28 +272,11 @@ bool PathBrowser::CanNavigateUp() { } #endif */ - if (path_ == "/" || path_ == "") { - return false; - } - return true; + return path_.CanNavigateUp(); } void PathBrowser::NavigateUp() { - // Upwards. - // Check for windows drives. - if (path_.size() == 3 && path_[1] == ':') { - path_ = "/"; - } else if (startsWith(path_, "http://") || startsWith(path_, "https://")) { - // You can actually pin "remote disc streaming" (which I didn't even realize until recently). - // This prevents you from getting the path browser into very weird states: - path_ = "/"; - // It's ok to just go directly to root without more checking since remote disc streaming - // does not yet support folders. - } else { - size_t slash = path_.rfind('/', path_.size() - 2); - if (slash != std::string::npos) - path_ = path_.substr(0, slash + 1); - } + path_ = path_.NavigateUp(); } // TODO: Support paths like "../../hello" @@ -310,12 +286,10 @@ void PathBrowser::Navigate(const std::string &path) { if (path == "..") { NavigateUp(); } else { - if (path.size() > 2 && path[1] == ':' && path_ == "/") - path_ = path; + if (path.size() >= 2 && path[1] == ':' && path_.IsRoot()) + path_ = Path(path); else - path_ = path_ + path; - if (path_[path_.size() - 1] != '/') - path_ += "/"; + path_ = path_ / path; } HandlePath(); } diff --git a/Common/File/PathBrowser.h b/Common/File/PathBrowser.h index 0873b2f58b..66227990b0 100644 --- a/Common/File/PathBrowser.h +++ b/Common/File/PathBrowser.h @@ -8,7 +8,9 @@ #include #include + #include "Common/File/DirListing.h" +#include "Common/File/Path.h" // Abstraction above path that lets you navigate easily. // "/" is a special path that means the root of the file system. On Windows, @@ -16,22 +18,19 @@ class PathBrowser { public: PathBrowser() {} - PathBrowser(std::string path) { SetPath(path); } + PathBrowser(const Path &path) { SetPath(path); } ~PathBrowser(); - void SetPath(const std::string &path); + void SetPath(const Path &path); bool IsListingReady(); - bool GetListing(std::vector &fileInfo, const char *filter = nullptr, bool *cancel = nullptr); + bool GetListing(std::vector &fileInfo, const char *filter = nullptr, bool *cancel = nullptr); bool CanNavigateUp(); void NavigateUp(); - void Navigate(const std::string &path); + void Navigate(const std::string &subdir); - std::string GetPath() const { - if (path_ != "/") - return path_; - else - return ""; + Path GetPath() const { + return path_; } std::string GetFriendlyPath() const; @@ -39,9 +38,9 @@ private: void HandlePath(); void ResetPending(); - std::string path_; - std::string pendingPath_; - std::vector pendingFiles_; + Path path_; + Path pendingPath_; + std::vector pendingFiles_; std::condition_variable pendingCond_; std::mutex pendingLock_; std::thread pendingThread_; diff --git a/Common/File/VFS/AssetReader.cpp b/Common/File/VFS/AssetReader.cpp index 1fec28922b..77bd76df22 100644 --- a/Common/File/VFS/AssetReader.cpp +++ b/Common/File/VFS/AssetReader.cpp @@ -42,7 +42,7 @@ ZipAssetReader::ZipAssetReader(const char *zip_file, const char *in_zip_path) { ERROR_LOG(IO, "Failed to open %s as a zip file", zip_file); } - std::vector info; + std::vector info; GetFileListing("assets", &info, 0); for (size_t i = 0; i < info.size(); i++) { if (info[i].isDirectory) { @@ -64,7 +64,7 @@ uint8_t *ZipAssetReader::ReadAsset(const char *path, size_t *size) { return ReadFromZip(zip_file_, temp_path, size); } -bool ZipAssetReader::GetFileListing(const char *orig_path, std::vector *listing, const char *filter = 0) { +bool ZipAssetReader::GetFileListing(const char *orig_path, std::vector *listing, const char *filter = 0) { char path[1024]; strcpy(path, in_zip_path_); strcat(path, orig_path); @@ -74,8 +74,8 @@ bool ZipAssetReader::GetFileListing(const char *orig_path, std::vector if (filter) { while (*filter) { if (*filter == ':') { - filters.insert(tmp); - tmp = ""; + filters.insert("." + tmp); + tmp.clear(); } else { tmp.push_back(*filter); } @@ -83,7 +83,7 @@ bool ZipAssetReader::GetFileListing(const char *orig_path, std::vector } } if (tmp.size()) - filters.insert(tmp); + filters.insert("." + tmp); // We just loop through the whole ZIP file and deduce what files are in this directory, and what subdirectories there are. std::set files; @@ -111,15 +111,11 @@ bool ZipAssetReader::GetFileListing(const char *orig_path, std::vector } for (auto diter = directories.begin(); diter != directories.end(); ++diter) { - FileInfo info; + File::FileInfo info; info.name = *diter; - info.fullName = std::string(path); - if (info.fullName[info.fullName.size() - 1] == '/') - info.fullName = info.fullName.substr(0, info.fullName.size() - 1); // Remove the "inzip" part of the fullname. - info.fullName = info.fullName.substr(strlen(in_zip_path_)); - info.fullName += "/" + *diter; + info.fullName = Path(std::string(path).substr(strlen(in_zip_path_))) / *diter; info.exists = true; info.isWritable = false; info.isDirectory = true; @@ -127,20 +123,18 @@ bool ZipAssetReader::GetFileListing(const char *orig_path, std::vector } for (auto fiter = files.begin(); fiter != files.end(); ++fiter) { - FileInfo info; + std::string fpath = path; + File::FileInfo info; info.name = *fiter; - info.fullName = std::string(path); - if (info.fullName[info.fullName.size() - 1] == '/') - info.fullName = info.fullName.substr(0, info.fullName.size() - 1); - info.fullName = info.fullName.substr(strlen(in_zip_path_)); - info.fullName += "/" + *fiter; + info.fullName = Path(std::string(path).substr(strlen(in_zip_path_))) / *fiter; info.exists = true; info.isWritable = false; info.isDirectory = false; - std::string ext = getFileExtension(info.fullName); + std::string ext = info.fullName.GetFileExtension(); if (filter) { - if (filters.find(ext) == filters.end()) + if (filters.find(ext) == filters.end()) { continue; + } } listing->push_back(info); } @@ -149,7 +143,7 @@ bool ZipAssetReader::GetFileListing(const char *orig_path, std::vector return true; } -bool ZipAssetReader::GetFileInfo(const char *path, FileInfo *info) { +bool ZipAssetReader::GetFileInfo(const char *path, File::FileInfo *info) { struct zip_stat zstat; char temp_path[1024]; strcpy(temp_path, in_zip_path_); @@ -162,7 +156,7 @@ bool ZipAssetReader::GetFileInfo(const char *path, FileInfo *info) { return false; } - info->fullName = path; + info->fullName = Path(path); info->exists = true; // TODO info->isWritable = false; info->isDirectory = false; // TODO @@ -172,60 +166,30 @@ bool ZipAssetReader::GetFileInfo(const char *path, FileInfo *info) { #endif -DirectoryAssetReader::DirectoryAssetReader(const char *path) { - strncpy(path_, path, ARRAY_SIZE(path_)); - path_[ARRAY_SIZE(path_) - 1] = '\0'; +DirectoryAssetReader::DirectoryAssetReader(const Path &path) { + path_ = path; } uint8_t *DirectoryAssetReader::ReadAsset(const char *path, size_t *size) { - char new_path[2048]; - new_path[0] = '\0'; - // Check if it already contains the path - if (strlen(path) > strlen(path_) && 0 == memcmp(path, path_, strlen(path_))) { - } - else { - strcpy(new_path, path_); - } - strcat(new_path, path); - return ReadLocalFile(new_path, size); + Path new_path = Path(path).StartsWith(path_) ? Path(path) : path_ / path; + return File::ReadLocalFile(new_path.c_str(), size); } -bool DirectoryAssetReader::GetFileListing(const char *path, std::vector *listing, const char *filter = 0) -{ - char new_path[2048]; - new_path[0] = '\0'; - // Check if it already contains the path - if (strlen(path) > strlen(path_) && 0 == memcmp(path, path_, strlen(path_))) { - } - else { - strcpy(new_path, path_); - } - strcat(new_path, path); - FileInfo info; - if (!getFileInfo(new_path, &info)) +bool DirectoryAssetReader::GetFileListing(const char *path, std::vector *listing, const char *filter = nullptr) { + Path new_path = Path(path).StartsWith(path_) ? Path(path) : path_ / path; + + File::FileInfo info; + if (!File::GetFileInfo(new_path, &info)) return false; - if (info.isDirectory) - { - getFilesInDir(new_path, listing, filter); + if (info.isDirectory) { + File::GetFilesInDir(new_path, listing, filter); return true; } - else - { - return false; - } + return false; } -bool DirectoryAssetReader::GetFileInfo(const char *path, FileInfo *info) -{ - char new_path[2048]; - new_path[0] = '\0'; - // Check if it already contains the path - if (strlen(path) > strlen(path_) && 0 == memcmp(path, path_, strlen(path_))) { - } - else { - strcpy(new_path, path_); - } - strcat(new_path, path); - return getFileInfo(new_path, info); +bool DirectoryAssetReader::GetFileInfo(const char *path, File::FileInfo *info) { + Path new_path = Path(path).StartsWith(path_) ? Path(path) : path_ / path; + return File::GetFileInfo(new_path, info); } diff --git a/Common/File/VFS/AssetReader.h b/Common/File/VFS/AssetReader.h index 5dcf98efc8..df0c4f2821 100644 --- a/Common/File/VFS/AssetReader.h +++ b/Common/File/VFS/AssetReader.h @@ -9,9 +9,8 @@ #include #include "Common/File/VFS/VFS.h" - -// Direct readers. deallocate using delete []. -uint8_t *ReadLocalFile(const char *filename, size_t *size); +#include "Common/File/FileUtil.h" +#include "Common/File/Path.h" class AssetReader { public: @@ -19,8 +18,8 @@ public: // use delete[] virtual uint8_t *ReadAsset(const char *path, size_t *size) = 0; // Filter support is optional but nice to have - virtual bool GetFileListing(const char *path, std::vector *listing, const char *filter = 0) = 0; - virtual bool GetFileInfo(const char *path, FileInfo *info) = 0; + virtual bool GetFileListing(const char *path, std::vector *listing, const char *filter = 0) = 0; + virtual bool GetFileInfo(const char *path, File::FileInfo *info) = 0; virtual std::string toString() const = 0; }; @@ -32,8 +31,8 @@ public: ~ZipAssetReader(); // use delete[] virtual uint8_t *ReadAsset(const char *path, size_t *size); - virtual bool GetFileListing(const char *path, std::vector *listing, const char *filter); - virtual bool GetFileInfo(const char *path, FileInfo *info); + virtual bool GetFileListing(const char *path, std::vector *listing, const char *filter); + virtual bool GetFileInfo(const char *path, File::FileInfo *info); virtual std::string toString() const { return in_zip_path_; } @@ -46,16 +45,16 @@ private: class DirectoryAssetReader : public AssetReader { public: - explicit DirectoryAssetReader(const char *path); + explicit DirectoryAssetReader(const Path &path); // use delete[] virtual uint8_t *ReadAsset(const char *path, size_t *size); - virtual bool GetFileListing(const char *path, std::vector *listing, const char *filter); - virtual bool GetFileInfo(const char *path, FileInfo *info); + virtual bool GetFileListing(const char *path, std::vector *listing, const char *filter); + virtual bool GetFileInfo(const char *path, File::FileInfo *info); virtual std::string toString() const { - return path_; + return path_.ToString(); } private: - char path_[512]{}; + Path path_; }; diff --git a/Common/File/VFS/VFS.cpp b/Common/File/VFS/VFS.cpp index 0353169d79..ff6dd37430 100644 --- a/Common/File/VFS/VFS.cpp +++ b/Common/File/VFS/VFS.cpp @@ -24,7 +24,8 @@ void VFSShutdown() { num_entries = 0; } -static bool IsLocalPath(const char *path) { +// TODO: Use Path more. +static bool IsLocalAbsolutePath(const char *path) { bool isUnixLocal = path[0] == '/'; #ifdef _WIN32 bool isWindowsLocal = isalpha(path[0]) && path[1] == ':'; @@ -36,10 +37,10 @@ static bool IsLocalPath(const char *path) { // The returned data should be free'd with delete[]. uint8_t *VFSReadFile(const char *filename, size_t *size) { - if (IsLocalPath(filename)) { + if (IsLocalAbsolutePath(filename)) { // Local path, not VFS. // INFO_LOG(IO, "Not a VFS path: %s . Reading local file.", filename); - return ReadLocalFile(filename, size); + return File::ReadLocalFile(filename, size); } int fn_len = (int)strlen(filename); @@ -64,11 +65,11 @@ uint8_t *VFSReadFile(const char *filename, size_t *size) { return 0; } -bool VFSGetFileListing(const char *path, std::vector *listing, const char *filter) { - if (IsLocalPath(path)) { +bool VFSGetFileListing(const char *path, std::vector *listing, const char *filter) { + if (IsLocalAbsolutePath(path)) { // Local path, not VFS. // INFO_LOG(IO, "Not a VFS path: %s . Reading local directory.", path); - getFilesInDir(path, listing, filter); + File::GetFilesInDir(Path(std::string(path)), listing, filter); return true; } @@ -91,11 +92,11 @@ bool VFSGetFileListing(const char *path, std::vector *listing, const c return false; } -bool VFSGetFileInfo(const char *path, FileInfo *info) { - if (IsLocalPath(path)) { +bool VFSGetFileInfo(const char *path, File::FileInfo *info) { + if (IsLocalAbsolutePath(path)) { // Local path, not VFS. // INFO_LOG(IO, "Not a VFS path: %s . Getting local file info.", path); - return getFileInfo(path, info); + return File::GetFileInfo(Path(std::string(path)), info); } bool fileSystemFound = false; diff --git a/Common/File/VFS/VFS.h b/Common/File/VFS/VFS.h index 693e8fbc1d..0f7e03c3fa 100644 --- a/Common/File/VFS/VFS.h +++ b/Common/File/VFS/VFS.h @@ -17,5 +17,5 @@ void VFSShutdown(); // Always allocates an extra zero byte at the end, so that it // can be used for text like shader sources. uint8_t *VFSReadFile(const char *filename, size_t *size); -bool VFSGetFileListing(const char *path, std::vector *listing, const char *filter = 0); -bool VFSGetFileInfo(const char *filename, FileInfo *fileInfo); +bool VFSGetFileListing(const char *path, std::vector *listing, const char *filter = 0); +bool VFSGetFileInfo(const char *filename, File::FileInfo *fileInfo); diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index eb808fb66d..dff6367f36 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -8,9 +8,9 @@ #endif #include "Common/System/Display.h" +#include "Common/Data/Convert/ColorConv.h" #include "Common/Data/Convert/SmallDataConvert.h" #include "Common/Data/Encoding/Utf8.h" -#include "Common/ColorConv.h" #include "Common/Log.h" #include diff --git a/Common/GPU/OpenGL/GLQueueRunner.cpp b/Common/GPU/OpenGL/GLQueueRunner.cpp index 487a335dd0..3f3dec4c4d 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.cpp +++ b/Common/GPU/OpenGL/GLQueueRunner.cpp @@ -199,7 +199,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps, bool ski // Dual source alpha glBindFragDataLocationIndexed(program->program, 0, 0, "fragColor0"); glBindFragDataLocationIndexed(program->program, 0, 1, "fragColor1"); - } else if (gl_extensions.VersionGEThan(3, 3, 0)) { + } else if (gl_extensions.VersionGEThan(3, 0, 0)) { glBindFragDataLocation(program->program, 0, "fragColor0"); } #elif !PPSSPP_PLATFORM(IOS) diff --git a/Common/GPU/OpenGL/GLSLProgram.cpp b/Common/GPU/OpenGL/GLSLProgram.cpp index bdc41644a4..0bca65c6f3 100644 --- a/Common/GPU/OpenGL/GLSLProgram.cpp +++ b/Common/GPU/OpenGL/GLSLProgram.cpp @@ -104,7 +104,7 @@ bool glsl_recompile(GLSLProgram *program, std::string *error_message) { program->vshader_mtime = vs.st_mtime; if (!program->vshader_source) { size_t sz; - vsh_src.reset((char *)ReadLocalFile(program->vshader_filename, &sz)); + vsh_src.reset((char *)File::ReadLocalFile(program->vshader_filename, &sz)); } } else { program->vshader_mtime = 0; @@ -114,7 +114,7 @@ bool glsl_recompile(GLSLProgram *program, std::string *error_message) { program->fshader_mtime = fs.st_mtime; if (!program->fshader_source) { size_t sz; - fsh_src.reset((char *)ReadLocalFile(program->fshader_filename, &sz)); + fsh_src.reset((char *)File::ReadLocalFile(program->fshader_filename, &sz)); } } else { program->fshader_mtime = 0; diff --git a/Common/GPU/OpenGL/thin3d_gl.cpp b/Common/GPU/OpenGL/thin3d_gl.cpp index 29e8283d53..454cde20f5 100644 --- a/Common/GPU/OpenGL/thin3d_gl.cpp +++ b/Common/GPU/OpenGL/thin3d_gl.cpp @@ -6,6 +6,7 @@ #include "ppsspp_config.h" +#include "Common/Data/Convert/ColorConv.h" #include "Common/Data/Convert/SmallDataConvert.h" #include "Common/Math/math_util.h" #include "Common/Math/lin/matrix4x4.h" @@ -641,8 +642,12 @@ OpenGLContext::OpenGLContext() { } else if (gl_extensions.VersionGEThan(3, 0, 0)) { shaderLanguageDesc_.shaderLanguage = ShaderLanguage::GLSL_1xx; shaderLanguageDesc_.fragColor0 = "fragColor0"; + shaderLanguageDesc_.texture = "texture"; shaderLanguageDesc_.bitwiseOps = true; shaderLanguageDesc_.texelFetch = "texelFetch"; + shaderLanguageDesc_.varying_vs = "out"; + shaderLanguageDesc_.varying_fs = "in"; + shaderLanguageDesc_.attribute = "in"; } else { // This too... shaderLanguageDesc_.shaderLanguage = ShaderLanguage::GLSL_1xx; @@ -808,15 +813,6 @@ public: GLRFramebuffer *framebuffer_ = nullptr; }; -// TODO: SSE/NEON optimize, and move to ColorConv.cpp. -void MoveABit(u16 *dest, const u16 *src, size_t count) { - for (int i = 0; i < count; i++) { - u16 data = src[i]; - data = (data >> 15) | (data << 1); - dest[i] = data; - } -} - void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback callback) { if ((width != width_ || height != height_ || depth != depth_) && level == 0) { // When switching to texStorage we need to handle this correctly. @@ -839,14 +835,14 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int if (texDataPopulated) { if (format_ == DataFormat::A1R5G5B5_UNORM_PACK16) { format_ = DataFormat::R5G5B5A1_UNORM_PACK16; - MoveABit((u16 *)texData, (const u16 *)texData, width * height * depth); + ConvertBGRA5551ToABGR1555((u16 *)texData, (const u16 *)texData, width * height * depth); } } else { // Emulate support for DataFormat::A1R5G5B5_UNORM_PACK16. if (format_ == DataFormat::A1R5G5B5_UNORM_PACK16) { format_ = DataFormat::R5G5B5A1_UNORM_PACK16; for (int y = 0; y < height; y++) { - MoveABit((u16 *)(texData + y * width * alignment), (const u16 *)(data + y * stride * alignment), width); + ConvertBGRA5551ToABGR1555((u16 *)(texData + y * width * alignment), (const u16 *)(data + y * stride * alignment), width); } } else { for (int y = 0; y < height; y++) { diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index fce371640f..523e88805f 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -392,7 +392,7 @@ VulkanRenderManager::~VulkanRenderManager() { } void VulkanRenderManager::ThreadFunc() { - setCurrentThreadName("RenderMan"); + SetCurrentThreadName("RenderMan"); int threadFrame = threadInitFrame_; bool nextFrame = false; bool firstFrame = true; diff --git a/Common/GPU/thin3d.cpp b/Common/GPU/thin3d.cpp index ce09c0356c..312bd91312 100644 --- a/Common/GPU/thin3d.cpp +++ b/Common/GPU/thin3d.cpp @@ -2,10 +2,10 @@ #include #include -#include "Common/System/Display.h" +#include "Common/Data/Convert/ColorConv.h" #include "Common/GPU/thin3d.h" #include "Common/Log.h" -#include "Common/ColorConv.h" +#include "Common/System/Display.h" namespace Draw { @@ -396,8 +396,6 @@ DrawContext::~DrawContext() { DestroyPresets(); } -// TODO: Use the functions we have in Common/ColorConv.cpp. -// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :) void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format) { // Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP. const uint32_t *src32 = (const uint32_t *)src; @@ -415,9 +413,7 @@ void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, u } } else if (format == Draw::DataFormat::R8G8B8_UNORM) { for (uint32_t y = 0; y < height; ++y) { - for (uint32_t x = 0; x < width; ++x) { - memcpy(dst + x * 3, src32 + x, 3); - } + ConvertRGBA8888ToRGB888(dst, src32, width); src32 += srcStride; dst += dstStride * 3; } @@ -455,8 +451,6 @@ void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, u } } -// TODO: Use the functions we have in Common/ColorConv.cpp. -// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :) void ConvertFromBGRA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format) { // Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP. const uint32_t *src32 = (const uint32_t *)src; @@ -481,12 +475,7 @@ void ConvertFromBGRA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, u } } else if (format == Draw::DataFormat::R8G8B8_UNORM) { for (uint32_t y = 0; y < height; ++y) { - for (uint32_t x = 0; x < width; ++x) { - uint32_t c = src32[x]; - dst[x * 3 + 0] = (c >> 16) & 0xFF; - dst[x * 3 + 1] = (c >> 8) & 0xFF; - dst[x * 3 + 2] = (c >> 0) & 0xFF; - } + ConvertBGRA8888ToRGB888(dst, src32, width); src32 += srcStride; dst += dstStride * 3; } diff --git a/Common/Input/InputState.cpp b/Common/Input/InputState.cpp index 90fc9b6a1a..66922ce77f 100644 --- a/Common/Input/InputState.cpp +++ b/Common/Input/InputState.cpp @@ -33,6 +33,7 @@ std::vector confirmKeys; std::vector cancelKeys; std::vector tabLeftKeys; std::vector tabRightKeys; +static std::unordered_map uiFlipAnalogY; static void AppendKeys(std::vector &keys, const std::vector &newKeys) { for (auto iter = newKeys.begin(); iter != newKeys.end(); ++iter) { @@ -61,3 +62,14 @@ void SetTabLeftRightKeys(const std::vector &tabLeft, const std::vector flipYByDeviceId) { + uiFlipAnalogY = flipYByDeviceId; +} + +int GetAnalogYDirection(int deviceId) { + auto configured = uiFlipAnalogY.find(deviceId); + if (configured != uiFlipAnalogY.end()) + return configured->second; + return 0; +} diff --git a/Common/Input/InputState.h b/Common/Input/InputState.h index 59a0a6e057..02377f3b24 100644 --- a/Common/Input/InputState.h +++ b/Common/Input/InputState.h @@ -4,8 +4,9 @@ // own mapping. Might later move the mapping system from PPSSPP to native. #include -#include #include +#include +#include #include "Common/Math/lin/vec3.h" #include "Common/Input/KeyCodes.h" @@ -162,3 +163,7 @@ void SetDPadKeys(const std::vector &leftKey, const std::vector & const std::vector &upKey, const std::vector &downKey); void SetConfirmCancelKeys(const std::vector &confirm, const std::vector &cancel); void SetTabLeftRightKeys(const std::vector &tabLeft, const std::vector &tabRight); + +// 0 means unknown (attempt autodetect), -1 means flip, 1 means original direction. +void SetAnalogFlipY(std::unordered_map flipYByDeviceId); +int GetAnalogYDirection(int deviceId); diff --git a/Common/Log.cpp b/Common/Log.cpp index 1d3b1ba56c..23dbcaf726 100644 --- a/Common/Log.cpp +++ b/Common/Log.cpp @@ -42,7 +42,7 @@ bool HandleAssert(const char *function, const char *file, int line, const char * va_end(args); // Secondary formatting. Wonder if this can be combined into the vsnprintf somehow. - char formatted[LOG_BUF_SIZE]; + char formatted[LOG_BUF_SIZE + 128]; snprintf(formatted, sizeof(formatted), "(%s:%s:%d) %s: [%s] %s", file, function, line, caption, expression, text); // Normal logging (will also log to Android log) diff --git a/Common/LogManager.cpp b/Common/LogManager.cpp index 113dc8200a..84f03a8aa6 100644 --- a/Common/LogManager.cpp +++ b/Common/LogManager.cpp @@ -291,12 +291,13 @@ void LogManager::RemoveListener(LogListener *listener) { } FileLogListener::FileLogListener(const char *filename) { -#if defined(_WIN32) && !defined(__MINGW32__) - m_logfile.open(ConvertUTF8ToWString(filename).c_str(), std::ios::app); -#else - m_logfile.open(filename, std::ios::app); -#endif - SetEnabled(true); + fp_ = File::OpenCFile(Path(std::string(filename)), "at"); + SetEnabled(fp_ != nullptr); +} + +FileLogListener::~FileLogListener() { + if (fp_) + fclose(fp_); } void FileLogListener::Log(const LogMessage &message) { @@ -304,7 +305,8 @@ void FileLogListener::Log(const LogMessage &message) { return; std::lock_guard lk(m_log_lock); - m_logfile << message.timestamp << " " << message.header << " " << message.msg << std::flush; + fprintf(fp_, "%s %s %s", message.timestamp, message.header, message.msg.c_str()); + fflush(fp_); } void OutputDebugStringLogListener::Log(const LogMessage &message) { diff --git a/Common/LogManager.h b/Common/LogManager.h index f5d4c61f12..805c48c5f8 100644 --- a/Common/LogManager.h +++ b/Common/LogManager.h @@ -19,10 +19,10 @@ #include "ppsspp_config.h" -#include #include #include #include +#include #include "Common/Data/Format/IniFile.h" #include "Common/CommonFuncs.h" @@ -53,18 +53,19 @@ public: class FileLogListener : public LogListener { public: FileLogListener(const char *filename); + ~FileLogListener(); void Log(const LogMessage &msg); - bool IsValid() { if (!m_logfile) return false; else return true; } + bool IsValid() { if (!fp_) return false; else return true; } bool IsEnabled() const { return m_enable; } void SetEnabled(bool enable) { m_enable = enable; } - const char* GetName() const { return "file"; } + const char *GetName() const { return "file"; } private: std::mutex m_log_lock; - std::ofstream m_logfile; + FILE *fp_ = nullptr; bool m_enable; }; diff --git a/Common/MemArenaPosix.cpp b/Common/MemArenaPosix.cpp index 7df84ab35c..3701f5c377 100644 --- a/Common/MemArenaPosix.cpp +++ b/Common/MemArenaPosix.cpp @@ -56,7 +56,7 @@ void MemArena::GrabLowMemSpace(size_t size) { // Some platforms (like Raspberry Pi) end up flushing to disk. // To avoid this, we try to use /dev/shm (tmpfs) if it exists. fd = -1; - if (File::Exists(tmpfs_location)) { + if (File::Exists(Path(tmpfs_location))) { fd = open(tmpfs_ram_temp_file.c_str(), O_RDWR | O_CREAT, mode); if (fd >= 0) { // Great, this definitely shouldn't flush to disk. diff --git a/Common/MipsCPUDetect.cpp b/Common/MipsCPUDetect.cpp index 9f2c1a1835..a7669edd51 100644 --- a/Common/MipsCPUDetect.cpp +++ b/Common/MipsCPUDetect.cpp @@ -32,13 +32,14 @@ const char procfile[] = "/proc/cpuinfo"; // https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-devices-system-cpu const char syscpupresentfile[] = "/sys/devices/system/cpu/present"; -std::string GetCPUString() -{ +std::string GetCPUString() { std::string line, marker = "Hardware\t: "; std::string cpu_string = "Unknown"; - std::fstream file; - if (!File::OpenCPPFile(file, procfile, std::ios::in)) + + std::string procdata; + if (!File::ReadFileToString(true, procfile, procdata)) return cpu_string; + std::istringstream file(procdata); while (std::getline(file, line)) { @@ -56,10 +57,11 @@ unsigned char GetCPUImplementer() { std::string line, marker = "CPU implementer\t: "; unsigned char implementer = 0; - std::fstream file; - if (!File::OpenCPPFile(file, procfile, std::ios::in)) + std::string procdata; + if (!File::ReadFileToString(true, procfile, procdata)) return 0; + std::istringstream file(procdata); while (std::getline(file, line)) { @@ -78,10 +80,11 @@ unsigned short GetCPUPart() { std::string line, marker = "CPU part\t: "; unsigned short part = 0; - std::fstream file; - if (!File::OpenCPPFile(file, procfile, std::ios::in)) + std::string procdata; + if (!File::ReadFileToString(true, procfile, procdata)) return 0; + std::istringstream file(procdata); while (std::getline(file, line)) { @@ -99,10 +102,11 @@ unsigned short GetCPUPart() bool CheckCPUASE(const std::string& ase) { std::string line, marker = "ASEs implemented\t: "; - std::fstream file; - if (!File::OpenCPPFile(file, procfile, std::ios::in)) - return 0; + std::string procdata; + if (!File::ReadFileToString(true, procfile, procdata)) + return false; + std::istringstream file(procdata); while (std::getline(file, line)) { @@ -125,12 +129,14 @@ int GetCoreCount() { std::string line, marker = "processor\t: "; int cores = 1; - std::fstream file; - if (File::OpenCPPFile(file, syscpupresentfile, std::ios::in)) - { + std::string presentData; + bool presentSuccess = File::ReadFileToString(true, syscpupresentfile, presentData); + std::istringstream presentFile(presentData); + + if (presentSuccess) { int low, high, found; - std::getline(file, line); + std::getline(presentFile, line); found = sscanf(line.c_str(), "%d-%d", &low, &high); if (found == 1) return 1; @@ -138,8 +144,10 @@ int GetCoreCount() return high - low + 1; } - if (!File::OpenCPPFile(file, procfile, std::ios::in)) + std::string procdata; + if (!File::ReadFileToString(true, procfile, procdata)) return 1; + std::istringstream file(procdata); while (std::getline(file, line)) { diff --git a/Common/Net/HTTPClient.cpp b/Common/Net/HTTPClient.cpp index 1c13d95f90..e1b25bb073 100644 --- a/Common/Net/HTTPClient.cpp +++ b/Common/Net/HTTPClient.cpp @@ -31,18 +31,17 @@ #include "Common/File/FileDescriptor.h" #include "Common/Thread/ThreadUtil.h" #include "Common/Data/Encoding/Compression.h" -#include "Common/Buffer.h" +#include "Common/Net/NetBuffer.h" #include "Common/Log.h" namespace net { -Connection::Connection() - : port_(-1), resolved_(NULL), sock_(-1) { +Connection::Connection() { } Connection::~Connection() { Disconnect(); - if (resolved_ != NULL) + if (resolved_ != nullptr) DNSResolveFree(resolved_); } @@ -148,7 +147,6 @@ bool Connection::Connect(int maxTries, double timeout, bool *cancelConnect) { // Something connected. Pick the first one that did (if multiple.) for (int sock : sockets) { if ((intptr_t)sock_ == -1 && FD_ISSET(sock, &fds)) { - fd_util::SetNonBlocking(sock, false); sock_ = sock; } else { closesocket(sock); @@ -182,11 +180,11 @@ void Connection::Disconnect() { namespace http { // TODO: do something sane here -#define USERAGENT "NATIVEAPP 1.0" +constexpr const char *DEFAULT_USERAGENT = "NATIVEAPP 1.0"; Client::Client() { httpVersion_ = "1.1"; - userAgent_ = USERAGENT; + userAgent_ = DEFAULT_USERAGENT; } Client::~Client() { @@ -245,35 +243,35 @@ void DeChunk(Buffer *inbuffer, Buffer *outbuffer, int contentLength, float *prog } } -int Client::GET(const char *resource, Buffer *output, std::vector &responseHeaders, float *progress, bool *cancelled) { +int Client::GET(const char *resource, Buffer *output, std::vector &responseHeaders, RequestProgress *progress) { const char *otherHeaders = "Accept: */*\r\n" "Accept-Encoding: gzip\r\n"; - int err = SendRequest("GET", resource, otherHeaders, progress, cancelled); + int err = SendRequest("GET", resource, otherHeaders, progress); if (err < 0) { return err; } - Buffer readbuf; - int code = ReadResponseHeaders(&readbuf, responseHeaders, progress, cancelled); + net::Buffer readbuf; + int code = ReadResponseHeaders(&readbuf, responseHeaders, progress); if (code < 0) { return code; } - err = ReadResponseEntity(&readbuf, responseHeaders, output, progress, cancelled); + err = ReadResponseEntity(&readbuf, responseHeaders, output, progress); if (err < 0) { return err; } return code; } -int Client::GET(const char *resource, Buffer *output, float *progress, bool *cancelled) { +int Client::GET(const char *resource, Buffer *output, RequestProgress *progress) { std::vector responseHeaders; - int code = GET(resource, output, responseHeaders, progress, cancelled); + int code = GET(resource, output, responseHeaders, progress); return code; } -int Client::POST(const char *resource, const std::string &data, const std::string &mime, Buffer *output, float *progress) { +int Client::POST(const char *resource, const std::string &data, const std::string &mime, Buffer *output, RequestProgress *progress) { char otherHeaders[2048]; if (mime.empty()) { snprintf(otherHeaders, sizeof(otherHeaders), "Content-Length: %lld\r\n", (long long)data.size()); @@ -285,7 +283,7 @@ int Client::POST(const char *resource, const std::string &data, const std::strin return err; } - Buffer readbuf; + net::Buffer readbuf; std::vector responseHeaders; int code = ReadResponseHeaders(&readbuf, responseHeaders, progress); if (code < 0) { @@ -299,20 +297,18 @@ int Client::POST(const char *resource, const std::string &data, const std::strin return code; } -int Client::POST(const char *resource, const std::string &data, Buffer *output, float *progress) { +int Client::POST(const char *resource, const std::string &data, Buffer *output, RequestProgress *progress) { return POST(resource, data, "", output, progress); } -int Client::SendRequest(const char *method, const char *resource, const char *otherHeaders, float *progress, bool *cancelled) { - return SendRequestWithData(method, resource, "", otherHeaders, progress, cancelled); +int Client::SendRequest(const char *method, const char *resource, const char *otherHeaders, RequestProgress *progress) { + return SendRequestWithData(method, resource, "", otherHeaders, progress); } -int Client::SendRequestWithData(const char *method, const char *resource, const std::string &data, const char *otherHeaders, float *progress, bool *cancelled) { - if (progress) { - *progress = 0.01f; - } +int Client::SendRequestWithData(const char *method, const char *resource, const std::string &data, const char *otherHeaders, RequestProgress *progress) { + progress->progress = 0.01f; - Buffer buffer; + net::Buffer buffer; const char *tpl = "%s %s HTTP/%s\r\n" "Host: %s\r\n" @@ -324,31 +320,28 @@ int Client::SendRequestWithData(const char *method, const char *resource, const buffer.Printf(tpl, method, resource, httpVersion_, host_.c_str(), - userAgent_, + userAgent_.c_str(), otherHeaders ? otherHeaders : ""); buffer.Append(data); - bool flushed = buffer.FlushSocket(sock(), dataTimeout_); + bool flushed = buffer.FlushSocket(sock(), dataTimeout_, progress->cancelled); if (!flushed) { return -1; // TODO error code. } return 0; } -int Client::ReadResponseHeaders(Buffer *readbuf, std::vector &responseHeaders, float *progress, bool *cancelled) { +int Client::ReadResponseHeaders(net::Buffer *readbuf, std::vector &responseHeaders, RequestProgress *progress) { // Snarf all the data we can into RAM. A little unsafe but hey. static constexpr float CANCEL_INTERVAL = 0.25f; bool ready = false; - double leftTimeout = dataTimeout_; + double endTimeout = time_now_d() + dataTimeout_; while (!ready) { - if (cancelled && *cancelled) + if (progress->cancelled && *progress->cancelled) return -1; ready = fd_util::WaitUntilReady(sock(), CANCEL_INTERVAL, false); - if (!ready && leftTimeout >= 0.0) { - leftTimeout -= CANCEL_INTERVAL; - if (leftTimeout < 0) { - ERROR_LOG(IO, "HTTP headers timed out"); - return -1; - } + if (!ready && time_now_d() > endTimeout) { + ERROR_LOG(IO, "HTTP headers timed out"); + return -1; } }; // Let's hope all the headers are available in a single packet... @@ -371,7 +364,7 @@ int Client::ReadResponseHeaders(Buffer *readbuf, std::vector &respo if (code_pos != line.npos) { code = atoi(&line[code_pos]); } else { - ERROR_LOG(IO, "Code not parse HTTP status code"); + ERROR_LOG(IO, "Could not parse HTTP status code: %s", line.c_str()); return -1; } @@ -390,7 +383,7 @@ int Client::ReadResponseHeaders(Buffer *readbuf, std::vector &respo return code; } -int Client::ReadResponseEntity(Buffer *readbuf, const std::vector &responseHeaders, Buffer *output, float *progress, bool *cancelled) { +int Client::ReadResponseEntity(net::Buffer *readbuf, const std::vector &responseHeaders, Buffer *output, RequestProgress *progress) { bool gzip = false; bool chunked = false; int contentLength = 0; @@ -422,51 +415,50 @@ int Client::ReadResponseEntity(Buffer *readbuf, const std::vector & contentLength = 0; } - if (!contentLength && progress) { + if (!contentLength) { // Content length is unknown. // Set progress to 1% so it looks like something is happening... - *progress = 0.1f; + progress->progress = 0.1f; } - if (!contentLength || !progress) { + if (!contentLength) { // No way to know how far along we are. Let's just not update the progress counter. - if (!readbuf->ReadAllWithProgress(sock(), contentLength, nullptr, cancelled)) + if (!readbuf->ReadAllWithProgress(sock(), contentLength, nullptr, &progress->kBps, progress->cancelled)) return -1; } else { // Let's read in chunks, updating progress between each. - if (!readbuf->ReadAllWithProgress(sock(), contentLength, progress, cancelled)) + if (!readbuf->ReadAllWithProgress(sock(), contentLength, &progress->progress, &progress->kBps, progress->cancelled)) return -1; } // output now contains the rest of the reply. Dechunk it. - if (chunked) { - DeChunk(readbuf, output, contentLength, progress); - } else { - output->Append(*readbuf); - } + if (!output->IsVoid()) { + if (chunked) { + DeChunk(readbuf, output, contentLength, &progress->progress); + } else { + output->Append(*readbuf); + } - // If it's gzipped, we decompress it and put it back in the buffer. - if (gzip) { - std::string compressed, decompressed; - output->TakeAll(&compressed); - bool result = decompress_string(compressed, &decompressed); - if (!result) { - ERROR_LOG(IO, "Error decompressing using zlib"); - if (progress) - *progress = 0.0f; - return -1; + // If it's gzipped, we decompress it and put it back in the buffer. + if (gzip) { + std::string compressed, decompressed; + output->TakeAll(&compressed); + bool result = decompress_string(compressed, &decompressed); + if (!result) { + ERROR_LOG(IO, "Error decompressing using zlib"); + progress->progress = 0.0f; + return -1; + } + output->Append(decompressed); } - output->Append(decompressed); } - if (progress) { - *progress = 1.0f; - } + progress->progress = 1.0f; return 0; } -Download::Download(const std::string &url, const std::string &outfile) - : url_(url), outfile_(outfile) { +Download::Download(const std::string &url, const Path &outfile) + : progress_(&cancelled_), url_(url), outfile_(outfile) { } Download::~Download() { @@ -487,7 +479,7 @@ void Download::Join() { void Download::SetFailed(int code) { failed_ = true; - progress_ = 1.0f; + progress_.progress = 1.0f; completed_ = true; } @@ -516,7 +508,7 @@ int Download::PerformGET(const std::string &url) { return -1; } - return client.GET(fileUrl.Resource().c_str(), &buffer_, responseHeaders_, &progress_, &cancelled_); + return client.GET(fileUrl.Resource().c_str(), &buffer_, responseHeaders_, &progress_); } std::string Download::RedirectLocation(const std::string &baseUrl) { @@ -531,7 +523,7 @@ std::string Download::RedirectLocation(const std::string &baseUrl) { } void Download::Do() { - setCurrentThreadName("Downloader::Do"); + SetCurrentThreadName("Downloader::Do"); resultCode_ = 0; std::string downloadURL = url_; @@ -560,24 +552,24 @@ void Download::Do() { } if (resultCode == 200) { - INFO_LOG(IO, "Completed downloading %s to %s", url_.c_str(), outfile_.empty() ? "memory" : outfile_.c_str()); - if (!outfile_.empty() && !buffer_.FlushToFile(outfile_.c_str())) { - ERROR_LOG(IO, "Failed writing download to %s", outfile_.c_str()); + INFO_LOG(IO, "Completed downloading %s to %s", url_.c_str(), outfile_.empty() ? "memory" : outfile_.ToVisualString().c_str()); + if (!outfile_.empty() && !buffer_.FlushToFile(outfile_)) { + ERROR_LOG(IO, "Failed writing download to %s", outfile_.ToVisualString().c_str()); } } else { - ERROR_LOG(IO, "Error downloading %s to %s: %i", url_.c_str(), outfile_.c_str(), resultCode); + ERROR_LOG(IO, "Error downloading %s to %s: %i", url_.c_str(), outfile_.ToVisualString().c_str(), resultCode); } resultCode_ = resultCode; } - progress_ = 1.0f; + progress_.progress = 1.0f; // Set this last to ensure no race conditions when checking Done. Users must always check // Done before looking at the result code. completed_ = true; } -std::shared_ptr Downloader::StartDownload(const std::string &url, const std::string &outfile) { +std::shared_ptr Downloader::StartDownload(const std::string &url, const Path &outfile) { std::shared_ptr dl(new Download(url, outfile)); downloads_.push_back(dl); dl->Start(); @@ -586,7 +578,7 @@ std::shared_ptr Downloader::StartDownload(const std::string &url, cons std::shared_ptr Downloader::StartDownloadWithCallback( const std::string &url, - const std::string &outfile, + const Path &outfile, std::function callback) { std::shared_ptr dl(new Download(url, outfile)); dl->SetCallback(callback); diff --git a/Common/Net/HTTPClient.h b/Common/Net/HTTPClient.h index d76e57d1b9..0924d4b9b7 100644 --- a/Common/Net/HTTPClient.h +++ b/Common/Net/HTTPClient.h @@ -5,10 +5,10 @@ #include #include +#include "Common/File/Path.h" +#include "Common/Net/NetBuffer.h" #include "Common/Net/Resolve.h" -#include "Common/Buffer.h" - namespace net { class Connection { @@ -29,12 +29,12 @@ protected: // Store the remote host here, so we can send it along through HTTP/1.1 requests. // TODO: Move to http::client? std::string host_; - int port_; + int port_ = -1; - addrinfo *resolved_; + addrinfo *resolved_ = nullptr; private: - uintptr_t sock_; + uintptr_t sock_ = -1; }; @@ -44,41 +44,54 @@ namespace http { bool GetHeaderValue(const std::vector &responseHeaders, const std::string &header, std::string *value); +struct RequestProgress { + RequestProgress() {} + explicit RequestProgress(bool *c) : cancelled(c) {} + + float progress = 0.0f; + float kBps = 0.0f; + bool *cancelled = nullptr; +}; + class Client : public net::Connection { public: Client(); ~Client(); // Return value is the HTTP return code. 200 means OK. < 0 means some local error. - int GET(const char *resource, Buffer *output, float *progress = nullptr, bool *cancelled = nullptr); - int GET(const char *resource, Buffer *output, std::vector &responseHeaders, float *progress = nullptr, bool *cancelled = nullptr); + int GET(const char *resource, Buffer *output, RequestProgress *progress); + int GET(const char *resource, Buffer *output, std::vector &responseHeaders, RequestProgress *progress); // Return value is the HTTP return code. - int POST(const char *resource, const std::string &data, const std::string &mime, Buffer *output, float *progress = nullptr); - int POST(const char *resource, const std::string &data, Buffer *output, float *progress = nullptr); + int POST(const char *resource, const std::string &data, const std::string &mime, Buffer *output, RequestProgress *progress); + int POST(const char *resource, const std::string &data, Buffer *output, RequestProgress *progress); // HEAD, PUT, DELETE aren't implemented yet, but can be done with SendRequest. - int SendRequest(const char *method, const char *resource, const char *otherHeaders = nullptr, float *progress = nullptr, bool *cancelled = nullptr); - int SendRequestWithData(const char *method, const char *resource, const std::string &data, const char *otherHeaders = nullptr, float *progress = nullptr, bool *cancelled = nullptr); - int ReadResponseHeaders(Buffer *readbuf, std::vector &responseHeaders, float *progress = nullptr, bool *cancelled = nullptr); + int SendRequest(const char *method, const char *resource, const char *otherHeaders, RequestProgress *progress); + int SendRequestWithData(const char *method, const char *resource, const std::string &data, const char *otherHeaders, RequestProgress *progress); + int ReadResponseHeaders(net::Buffer *readbuf, std::vector &responseHeaders, RequestProgress *progress); // If your response contains a response, you must read it. - int ReadResponseEntity(Buffer *readbuf, const std::vector &responseHeaders, Buffer *output, float *progress = nullptr, bool *cancelled = nullptr); + int ReadResponseEntity(net::Buffer *readbuf, const std::vector &responseHeaders, Buffer *output, RequestProgress *progress); void SetDataTimeout(double t) { dataTimeout_ = t; } + void SetUserAgent(const std::string &&value) { + userAgent_ = value; + } + protected: - const char *userAgent_; + std::string userAgent_; const char *httpVersion_; - double dataTimeout_ = -1.0; + double dataTimeout_ = 900.0; }; // Not particularly efficient, but hey - it's a background download, that's pretty cool :P class Download { public: - Download(const std::string &url, const std::string &outfile); + Download(const std::string &url, const Path &outfile); ~Download(); void Start(); @@ -86,7 +99,8 @@ public: void Join(); // Returns 1.0 when done. That one value can be compared exactly - or just use Done(). - float Progress() const { return progress_; } + float Progress() const { return progress_.progress; } + float SpeedKBps() const { return progress_.kBps; } bool Done() const { return completed_; } bool Failed() const { return failed_; } @@ -95,7 +109,7 @@ public: int ResultCode() const { return resultCode_; } std::string url() const { return url_; } - std::string outfile() const { return outfile_; } + const Path &outfile() const { return outfile_; } // If not downloading to a file, access this to get the result. Buffer &buffer() { return buffer_; } @@ -130,11 +144,11 @@ private: int PerformGET(const std::string &url); std::string RedirectLocation(const std::string &baseUrl); void SetFailed(int code); - float progress_ = 0.0f; + RequestProgress progress_; Buffer buffer_; std::vector responseHeaders_; std::string url_; - std::string outfile_; + Path outfile_; std::thread thread_; int resultCode_ = 0; bool completed_ = false; @@ -153,11 +167,11 @@ public: CancelAll(); } - std::shared_ptr StartDownload(const std::string &url, const std::string &outfile); + std::shared_ptr StartDownload(const std::string &url, const Path &outfile); std::shared_ptr StartDownloadWithCallback( const std::string &url, - const std::string &outfile, + const Path &outfile, std::function callback); // Drops finished downloads from the list. diff --git a/Common/Net/HTTPHeaders.cpp b/Common/Net/HTTPHeaders.cpp index ef19d3bc9d..16dc16c8e7 100644 --- a/Common/Net/HTTPHeaders.cpp +++ b/Common/Net/HTTPHeaders.cpp @@ -12,34 +12,32 @@ namespace http { -RequestHeader::RequestHeader() - : status(200), referer(0), user_agent(0), - resource(0), params(0), content_length(-1), first_header_(true) { +RequestHeader::RequestHeader() { } RequestHeader::~RequestHeader() { - delete [] referer; - delete [] user_agent; - delete [] resource; - delete [] params; + delete [] referer; + delete [] user_agent; + delete [] resource; + delete [] params; } bool RequestHeader::GetParamValue(const char *param_name, std::string *value) const { - if (!params) - return false; - std::string p(params); - std::vector v; + if (!params) + return false; + std::string p(params); + std::vector v; SplitString(p, '&', v); - for (size_t i = 0; i < v.size(); i++) { - std::vector parts; + for (size_t i = 0; i < v.size(); i++) { + std::vector parts; SplitString(v[i], '=', parts); - DEBUG_LOG(IO, "Param: %s Value: %s", parts[0].c_str(), parts[1].c_str()); - if (parts[0] == param_name) { - *value = parts[1]; - return true; - } - } - return false; + DEBUG_LOG(IO, "Param: %s Value: %s", parts[0].c_str(), parts[1].c_str()); + if (parts[0] == param_name) { + *value = parts[1]; + return true; + } + } + return false; } bool RequestHeader::GetOther(const char *name, std::string *value) const { @@ -54,87 +52,87 @@ bool RequestHeader::GetOther(const char *name, std::string *value) const { // Intended to be a mad fast parser. It's not THAT fast currently, there's still // things to optimize, but meh. int RequestHeader::ParseHttpHeader(const char *buffer) { - if (first_header_) { - // Step 1: Method - first_header_ = false; - if (!memcmp(buffer, "GET ", 4)) { - method = GET; - buffer += 4; - } else if (!memcmp(buffer, "HEAD ", 5)) { - method = HEAD; - buffer += 5; - } else if (!memcmp(buffer, "POST ", 5)) { - method = POST; - buffer += 5; - } else { - method = UNSUPPORTED; - status = 405; - return -1; - } - SkipSpace(&buffer); + if (first_header_) { + // Step 1: Method + first_header_ = false; + if (!memcmp(buffer, "GET ", 4)) { + method = GET; + buffer += 4; + } else if (!memcmp(buffer, "HEAD ", 5)) { + method = HEAD; + buffer += 5; + } else if (!memcmp(buffer, "POST ", 5)) { + method = POST; + buffer += 5; + } else { + method = UNSUPPORTED; + status = 405; + return -1; + } + SkipSpace(&buffer); - // Step 2: Resource, params (what's after the ?, if any) - const char *endptr = strchr(buffer, ' '); - const char *q_ptr = strchr(buffer, '?'); + // Step 2: Resource, params (what's after the ?, if any) + const char *endptr = strchr(buffer, ' '); + const char *q_ptr = strchr(buffer, '?'); - int resource_name_len; - if (q_ptr) - resource_name_len = q_ptr - buffer; - else - resource_name_len = endptr - buffer; - if (!resource_name_len) { - status = 400; - return -1; - } - resource = new char[resource_name_len + 1]; - memcpy(resource, buffer, resource_name_len); - resource[resource_name_len] = '\0'; - if (q_ptr) { - int param_length = endptr - q_ptr - 1; - params = new char[param_length + 1]; - memcpy(params, q_ptr + 1, param_length); - params[param_length] = '\0'; - } - if (strstr(buffer, "HTTP/")) - type = FULL; - else - type = SIMPLE; - return 0; - } + int resource_name_len; + if (q_ptr) + resource_name_len = q_ptr - buffer; + else + resource_name_len = endptr - buffer; + if (!resource_name_len) { + status = 400; + return -1; + } + resource = new char[resource_name_len + 1]; + memcpy(resource, buffer, resource_name_len); + resource[resource_name_len] = '\0'; + if (q_ptr) { + int param_length = endptr - q_ptr - 1; + params = new char[param_length + 1]; + memcpy(params, q_ptr + 1, param_length); + params[param_length] = '\0'; + } + if (strstr(buffer, "HTTP/")) + type = FULL; + else + type = SIMPLE; + return 0; + } - // We have a real header to parse. - const char *colon = strchr(buffer, ':'); - if (!colon) { - status = 400; - return -1; - } + // We have a real header to parse. + const char *colon = strchr(buffer, ':'); + if (!colon) { + status = 400; + return -1; + } - // The header is formatted as key: value. - int key_len = colon - buffer; - const char *key = buffer; + // The header is formatted as key: value. + int key_len = colon - buffer; + const char *key = buffer; - // Go to after the colon to get the value. - buffer = colon + 1; - SkipSpace(&buffer); - int value_len = (int)strlen(buffer); - - if (!strncasecmp(key, "User-Agent", key_len)) { - user_agent = new char[value_len + 1]; - memcpy(user_agent, buffer, value_len + 1); - VERBOSE_LOG(IO, "user-agent: %s", user_agent); - } else if (!strncasecmp(key, "Referer", key_len)) { - referer = new char[value_len + 1]; - memcpy(referer, buffer, value_len + 1); - } else if (!strncasecmp(key, "Content-Length", key_len)) { - content_length = atoi(buffer); - VERBOSE_LOG(IO, "Content-Length: %i", (int)content_length); - } else { - std::string key_str(key, key_len); - std::transform(key_str.begin(), key_str.end(), key_str.begin(), tolower); - other[key_str] = buffer; - } + // Go to after the colon to get the value. + buffer = colon + 1; + SkipSpace(&buffer); + int value_len = (int)strlen(buffer); - return 0; + if (!strncasecmp(key, "User-Agent", key_len)) { + user_agent = new char[value_len + 1]; + memcpy(user_agent, buffer, value_len + 1); + VERBOSE_LOG(IO, "user-agent: %s", user_agent); + } else if (!strncasecmp(key, "Referer", key_len)) { + referer = new char[value_len + 1]; + memcpy(referer, buffer, value_len + 1); + } else if (!strncasecmp(key, "Content-Length", key_len)) { + content_length = atoi(buffer); + VERBOSE_LOG(IO, "Content-Length: %i", (int)content_length); + } else { + std::string key_str(key, key_len); + std::transform(key_str.begin(), key_str.end(), key_str.begin(), tolower); + other[key_str] = buffer; + } + + return 0; } void RequestHeader::ParseHeaders(net::InputSink *sink) { diff --git a/Common/Net/HTTPHeaders.h b/Common/Net/HTTPHeaders.h index 0ee0b1b751..5900b01874 100644 --- a/Common/Net/HTTPHeaders.h +++ b/Common/Net/HTTPHeaders.h @@ -3,7 +3,7 @@ #include #include -#include "Common/Buffer.h" +#include "Common/Net/NetBuffer.h" namespace net { class InputSink; @@ -12,39 +12,39 @@ class InputSink; namespace http { class RequestHeader { - public: - RequestHeader(); - ~RequestHeader(); - // Public variables since it doesn't make sense - // to bother with accessors for all these. - int status; - // Intentional misspelling. - char *referer; - char *user_agent; - char *resource; - char *params; - int content_length; - std::unordered_map other; - enum RequestType { - SIMPLE, FULL, - }; - RequestType type; - enum Method { - GET, - HEAD, - POST, - UNSUPPORTED, - }; - Method method; - bool ok; - void ParseHeaders(net::InputSink *sink); - bool GetParamValue(const char *param_name, std::string *value) const; - bool GetOther(const char *name, std::string *value) const; - private: - int ParseHttpHeader(const char *buffer); - bool first_header_; - - DISALLOW_COPY_AND_ASSIGN(RequestHeader); +public: + RequestHeader(); + ~RequestHeader(); + // Public variables since it doesn't make sense + // to bother with accessors for all these. + int status = 100; + // Intentional misspelling. + char *referer = nullptr; + char *user_agent = nullptr; + char *resource = nullptr; + char *params = nullptr; + int content_length = -1; + std::unordered_map other; + enum RequestType { + SIMPLE, FULL, + }; + RequestType type; + enum Method { + GET, + HEAD, + POST, + UNSUPPORTED, + }; + Method method = UNSUPPORTED; + bool ok = false; + void ParseHeaders(net::InputSink *sink); + bool GetParamValue(const char *param_name, std::string *value) const; + bool GetOther(const char *name, std::string *value) const; +private: + int ParseHttpHeader(const char *buffer); + bool first_header_ = true; + + DISALLOW_COPY_AND_ASSIGN(RequestHeader); }; } // namespace http diff --git a/Common/Net/HTTPServer.cpp b/Common/Net/HTTPServer.cpp index 1deb45f72c..882d49a965 100644 --- a/Common/Net/HTTPServer.cpp +++ b/Common/Net/HTTPServer.cpp @@ -31,24 +31,33 @@ #include #include -#include -#include +#include +#include #include "Common/Net/HTTPServer.h" +#include "Common/Net/NetBuffer.h" #include "Common/Net/Sinks.h" #include "Common/File/FileDescriptor.h" -#include "Common/Thread/Executor.h" - -#include "Common/Buffer.h" #include "Common/Log.h" +void NewThreadExecutor::Run(std::function &&func) { + threads_.push_back(std::thread(func)); +} + +NewThreadExecutor::~NewThreadExecutor() { + // If Run was ever called... + for (auto &thread : threads_) + thread.join(); + threads_.clear(); +} + namespace http { // Note: charset here helps prevent XSS. const char *const DEFAULT_MIME_TYPE = "text/html; charset=utf-8"; Request::Request(int fd) - : fd_(fd) { + : fd_(fd) { in_ = new net::InputSink(fd); out_ = new net::OutputSink(fd); header_.ParseHeaders(in_); @@ -109,27 +118,27 @@ void Request::WriteHttpResponseHeader(const char *ver, int status, int64_t size, } void Request::WritePartial() const { - _assert_(fd_); - out_->Flush(); + _assert_(fd_); + out_->Flush(); } void Request::Write() { - _assert_(fd_); - WritePartial(); - Close(); + _assert_(fd_); + WritePartial(); + Close(); } void Request::Close() { - if (fd_) { - closesocket(fd_); - fd_ = 0; - } + if (fd_) { + closesocket(fd_); + fd_ = 0; + } } -Server::Server(threading::Executor *executor) - : port_(0), executor_(executor) { - RegisterHandler("/", std::bind(&Server::HandleListing, this, std::placeholders::_1)); - SetFallbackHandler(std::bind(&Server::Handle404, this, std::placeholders::_1)); +Server::Server(NewThreadExecutor *executor) + : executor_(executor) { + RegisterHandler("/", std::bind(&Server::HandleListing, this, std::placeholders::_1)); + SetFallbackHandler(std::bind(&Server::Handle404, this, std::placeholders::_1)); } Server::~Server() { @@ -137,7 +146,7 @@ Server::~Server() { } void Server::RegisterHandler(const char *url_path, UrlHandlerFunc handler) { - handlers_[std::string(url_path)] = handler; + handlers_[std::string(url_path)] = handler; } void Server::SetFallbackHandler(UrlHandlerFunc handler) { @@ -302,18 +311,18 @@ void Server::Stop() { } void Server::HandleConnection(int conn_fd) { - Request request(conn_fd); - if (!request.IsOK()) { - WARN_LOG(IO, "Bad request, ignoring."); - return; - } - HandleRequest(request); + Request request(conn_fd); + if (!request.IsOK()) { + WARN_LOG(IO, "Bad request, ignoring."); + return; + } + HandleRequest(request); - // TODO: Way to mark the content body as read, read it here if never read. - // This allows the handler to stream if need be. + // TODO: Way to mark the content body as read, read it here if never read. + // This allows the handler to stream if need be. - // TODO: Could handle keep alive here. - request.Write(); + // TODO: Could handle keep alive here. + request.Write(); } void Server::HandleRequest(const Request &request) { diff --git a/Common/Net/HTTPServer.h b/Common/Net/HTTPServer.h index 9cfdf2fc1e..a88137398b 100644 --- a/Common/Net/HTTPServer.h +++ b/Common/Net/HTTPServer.h @@ -2,10 +2,19 @@ #include #include +#include #include "Common/Net/HTTPHeaders.h" #include "Common/Net/Resolve.h" -#include "Common/Thread/Executor.h" + +class NewThreadExecutor { +public: + ~NewThreadExecutor(); + void Run(std::function &&func); + +private: + std::vector threads_; +}; namespace net { class InputSink; @@ -15,40 +24,40 @@ class OutputSink; namespace http { class Request { - public: - Request(int fd); - ~Request(); +public: + Request(int fd); + ~Request(); - const char *resource() const { - return header_.resource; - } + const char *resource() const { + return header_.resource; + } - RequestHeader::Method Method() const { - return header_.method; - } + RequestHeader::Method Method() const { + return header_.method; + } - bool GetParamValue(const char *param_name, std::string *value) const { - return header_.GetParamValue(param_name, value); - } - // Use lowercase. - bool GetHeader(const char *name, std::string *value) const { - return header_.GetOther(name, value); - } + bool GetParamValue(const char *param_name, std::string *value) const { + return header_.GetParamValue(param_name, value); + } + // Use lowercase. + bool GetHeader(const char *name, std::string *value) const { + return header_.GetOther(name, value); + } - net::InputSink *In() const { return in_; } - net::OutputSink *Out() const { return out_; } + net::InputSink *In() const { return in_; } + net::OutputSink *Out() const { return out_; } - // TODO: Remove, in favor of PartialWrite and friends. - int fd() const { return fd_; } + // TODO: Remove, in favor of PartialWrite and friends. + int fd() const { return fd_; } - void WritePartial() const; - void Write(); - void Close(); + void WritePartial() const; + void Write(); + void Close(); - bool IsOK() const { return fd_ > 0; } + bool IsOK() const { return fd_ > 0; } - // If size is negative, no Content-Length: line is written. - void WriteHttpResponseHeader(const char *ver, int status, int64_t size = -1, const char *mimeType = nullptr, const char *otherHeaders = nullptr) const; + // If size is negative, no Content-Length: line is written. + void WriteHttpResponseHeader(const char *ver, int status, int64_t size = -1, const char *mimeType = nullptr, const char *otherHeaders = nullptr) const; private: net::InputSink *in_; @@ -61,7 +70,7 @@ private: class Server { public: // Takes ownership. - Server(threading::Executor *executor); + Server(NewThreadExecutor *executor); virtual ~Server(); typedef std::function UrlHandlerFunc; @@ -103,12 +112,12 @@ private: void Handle404(const Request &request); int listener_; - int port_; + int port_ = 0; UrlHandlerMap handlers_; UrlHandlerFunc fallback_; - threading::Executor *executor_; + NewThreadExecutor *executor_; }; } // namespace http diff --git a/Common/Net/NetBuffer.cpp b/Common/Net/NetBuffer.cpp new file mode 100644 index 0000000000..331f24b55e --- /dev/null +++ b/Common/Net/NetBuffer.cpp @@ -0,0 +1,112 @@ +#include "ppsspp_config.h" +#ifdef _WIN32 +#include +#undef min +#undef max +#else +#include +#include +#endif +#include +#include + +#ifndef MSG_NOSIGNAL +// Default value to 0x00 (do nothing) in systems where it's not supported. +#define MSG_NOSIGNAL 0x00 +#endif + +#include "Common/File/FileDescriptor.h" +#include "Common/Log.h" +#include "Common/Net/NetBuffer.h" +#include "Common/TimeUtil.h" + +namespace net { + +bool Buffer::FlushSocket(uintptr_t sock, double timeout, bool *cancelled) { + static constexpr float CANCEL_INTERVAL = 0.25f; + for (size_t pos = 0, end = data_.size(); pos < end; ) { + bool ready = false; + double endTimeout = time_now_d() + timeout; + while (!ready) { + if (cancelled && *cancelled) + return false; + ready = fd_util::WaitUntilReady(sock, CANCEL_INTERVAL, true); + if (!ready && time_now_d() > endTimeout) { + ERROR_LOG(IO, "FlushSocket timed out"); + return false; + } + } + int sent = send(sock, &data_[pos], (int)(end - pos), MSG_NOSIGNAL); + if (sent < 0) { + ERROR_LOG(IO, "FlushSocket failed"); + return false; + } + pos += sent; + } + data_.resize(0); + return true; +} + +bool Buffer::ReadAllWithProgress(int fd, int knownSize, float *progress, float *kBps, bool *cancelled) { + static constexpr float CANCEL_INTERVAL = 0.25f; + std::vector buf; + // We're non-blocking and reading from an OS buffer, so try to read as much as we can at a time. + if (knownSize >= 65536 * 16) { + buf.resize(65536); + } else if (knownSize >= 1024 * 16) { + buf.resize(knownSize / 16); + } else { + buf.resize(1024); + } + + double st = time_now_d(); + int total = 0; + while (true) { + bool ready = false; + while (!ready && cancelled) { + if (*cancelled) + return false; + ready = fd_util::WaitUntilReady(fd, CANCEL_INTERVAL, false); + } + int retval = recv(fd, &buf[0], (int)buf.size(), MSG_NOSIGNAL); + if (retval == 0) { + return true; + } else if (retval < 0) { +#if PPSSPP_PLATFORM(WINDOWS) + if (WSAGetLastError() != WSAEWOULDBLOCK) +#else + if (errno != EWOULDBLOCK) +#endif + ERROR_LOG(IO, "Error reading from buffer: %i", retval); + return false; + } + char *p = Append((size_t)retval); + memcpy(p, &buf[0], retval); + total += retval; + if (progress) + *progress = (float)total / (float)knownSize; + if (kBps) + *kBps = (float)(total / (time_now_d() - st)) / 1024.0f; + } + return true; +} + +int Buffer::Read(int fd, size_t sz) { + char buf[1024]; + int retval; + size_t received = 0; + while ((retval = recv(fd, buf, (int)std::min(sz, sizeof(buf)), MSG_NOSIGNAL)) > 0) { + if (retval < 0) { + return retval; + } + char *p = Append((size_t)retval); + memcpy(p, buf, retval); + sz -= retval; + received += retval; + if (sz == 0) + return 0; + } + return (int)received; +} + +} diff --git a/Common/Net/NetBuffer.h b/Common/Net/NetBuffer.h new file mode 100644 index 0000000000..6247aca985 --- /dev/null +++ b/Common/Net/NetBuffer.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Common/Buffer.h" + +namespace net { + +class Buffer : public ::Buffer { +public: + bool FlushSocket(uintptr_t sock, double timeout, bool *cancelled = nullptr); + + bool ReadAllWithProgress(int fd, int knownSize, float *progress, float *kBps, bool *cancelled); + + // < 0: error + // >= 0: number of bytes read + int Read(int fd, size_t sz); +}; + +} diff --git a/Common/Net/Resolve.cpp b/Common/Net/Resolve.cpp index 37fde61481..f0c633de67 100644 --- a/Common/Net/Resolve.cpp +++ b/Common/Net/Resolve.cpp @@ -17,10 +17,13 @@ #undef min #undef max #else +#include +#include +#include #include #include #include -#include +#include #include #endif @@ -106,7 +109,8 @@ void DNSResolveFree(addrinfo *res) bool GetIPList(std::vector &IP4s) { char ipstr[INET6_ADDRSTRLEN]; // We use IPv6 length since it's longer than IPv4 -#if defined(getifaddrs) // On Android: Requires __ANDROID_API__ >= 24 +#if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3) || (__ANDROID_API__ >= 24) // getifaddrs first appeared in glibc 2.3, On Android officially supported since __ANDROID_API__ >= 24 + INFO_LOG(SCENET, "GetIPList from getifaddrs"); struct ifaddrs* ifAddrStruct = NULL; struct ifaddrs* ifa = NULL; @@ -134,7 +138,7 @@ bool GetIPList(std::vector &IP4s) { return true; } #elif defined(SIOCGIFCONF) // Better detection on Linux/UNIX/MacOS/some Android -#include + INFO_LOG(SCENET, "GetIPList from SIOCGIFCONF"); static struct ifreq ifreqs[32]; struct ifconf ifconf; memset(&ifconf, 0, sizeof(ifconf)); @@ -166,6 +170,7 @@ bool GetIPList(std::vector &IP4s) { close(sd); return true; #else // Fallback to POSIX/Cross-platform way but may not works well on Linux (ie. only shows 127.0.0.1) + INFO_LOG(SCENET, "GetIPList from Fallback"); struct addrinfo hints, * res, * p; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version diff --git a/Common/Net/WebsocketServer.cpp b/Common/Net/WebsocketServer.cpp index 5af6bf2bdb..3b5449b489 100644 --- a/Common/Net/WebsocketServer.cpp +++ b/Common/Net/WebsocketServer.cpp @@ -47,12 +47,12 @@ static const size_t OUT_PRESSURE = 65536; static inline std::string TrimString(const std::string &s) { auto wsfront = std::find_if_not(s.begin(), s.end(), [](int c) { // isspace() expects 0 - 255, so convert any sign-extended value. - return std::isspace(c & 0xFF); - }); - auto wsback = std::find_if_not(s.rbegin(), s.rend(), [](int c){ - return std::isspace(c & 0xFF); - }).base(); - return wsback > wsfront ? std::string(wsfront, wsback) : std::string(); + return std::isspace(c & 0xFF); + }); + auto wsback = std::find_if_not(s.rbegin(), s.rend(), [](int c){ + return std::isspace(c & 0xFF); + }).base(); + return wsback > wsfront ? std::string(wsfront, wsback) : std::string(); } static bool ListContainsNoCase(const std::string &list, const std::string value) { diff --git a/Common/Render/TextureAtlas.cpp b/Common/Render/TextureAtlas.cpp index 285f766892..c23cb33646 100644 --- a/Common/Render/TextureAtlas.cpp +++ b/Common/Render/TextureAtlas.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "Common/Render/TextureAtlas.h" @@ -22,10 +23,19 @@ public: } template - T *ReadMultipleAlloc(size_t count) { + T *ReadMultipleAlloc(size_t count, bool compressed) { T *t = new T[count]; - memcpy(t, data_ + offset_, sizeof(T) * count); - offset_ += sizeof(T) * count; + if (!compressed) { + memcpy(t, data_ + offset_, sizeof(T) * count); + offset_ += sizeof(T) * count; + } else { + uint32_t compressed_size = 0; + memcpy(&compressed_size, data_ + offset_, sizeof(uint32_t)); + offset_ += sizeof(uint32_t); + + ZSTD_decompress(t, sizeof(T) * count, data_ + offset_, compressed_size); + offset_ += compressed_size; + } return t; } @@ -45,7 +55,8 @@ bool Atlas::Load(const uint8_t *data, size_t data_size) { return false; } - images = reader.ReadMultipleAlloc(num_images); + images = reader.ReadMultipleAlloc(num_images, header.version >= 1); + fonts = new AtlasFont[num_fonts]; for (int i = 0; i < num_fonts; i++) { AtlasFontHeader font_header = reader.Read(); @@ -55,8 +66,8 @@ bool Atlas::Load(const uint8_t *data, size_t data_size) { fonts[i].distslope = font_header.distslope; fonts[i].numRanges = font_header.numRanges; fonts[i].numChars = font_header.numChars; - fonts[i].ranges = reader.ReadMultipleAlloc(font_header.numRanges); - fonts[i].charData = reader.ReadMultipleAlloc(font_header.numChars); + fonts[i].ranges = reader.ReadMultipleAlloc(font_header.numRanges, header.version >= 1); + fonts[i].charData = reader.ReadMultipleAlloc(font_header.numChars, header.version >= 1); memcpy(fonts[i].name, font_header.name, sizeof(font_header.name)); } return true; diff --git a/Common/Serialize/Serializer.cpp b/Common/Serialize/Serializer.cpp index 4a6c9d94a9..a04448cc8a 100644 --- a/Common/Serialize/Serializer.cpp +++ b/Common/Serialize/Serializer.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" @@ -249,7 +250,7 @@ CChunkFileReader::Error CChunkFileReader::LoadFileHeader(File::IOFile &pFile, SC return ERROR_NONE; } -CChunkFileReader::Error CChunkFileReader::GetFileTitle(const std::string &filename, std::string *title) { +CChunkFileReader::Error CChunkFileReader::GetFileTitle(const Path &filename, std::string *title) { if (!File::Exists(filename)) { ERROR_LOG(SAVESTATE, "ChunkReader: File doesn't exist"); return ERROR_BAD_FILE; @@ -260,7 +261,7 @@ CChunkFileReader::Error CChunkFileReader::GetFileTitle(const std::string &filena return LoadFileHeader(pFile, header, title); } -CChunkFileReader::Error CChunkFileReader::LoadFile(const std::string &filename, std::string *gitVersion, u8 *&_buffer, size_t &sz, std::string *failureReason) { +CChunkFileReader::Error CChunkFileReader::LoadFile(const Path &filename, std::string *gitVersion, u8 *&_buffer, size_t &sz, std::string *failureReason) { if (!File::Exists(filename)) { *failureReason = "LoadStateDoesntExist"; ERROR_LOG(SAVESTATE, "ChunkReader: File doesn't exist"); @@ -287,8 +288,15 @@ CChunkFileReader::Error CChunkFileReader::LoadFile(const std::string &filename, if (header.Compress) { u8 *uncomp_buffer = new u8[header.UncompressedSize]; size_t uncomp_size = header.UncompressedSize; - auto status = snappy_uncompress((const char *)buffer, sz, (char *)uncomp_buffer, &uncomp_size); - if (status != SNAPPY_OK) { + bool success = false; + if (header.Compress == 1) { + auto status = snappy_uncompress((const char *)buffer, sz, (char *)uncomp_buffer, &uncomp_size); + success = status == SNAPPY_OK; + } else { + auto status = ZSTD_decompress((char *)uncomp_buffer, uncomp_size, (const char *)buffer, sz); + success = !ZSTD_isError(status); + } + if (!success) { ERROR_LOG(SAVESTATE, "ChunkReader: Failed to decompress file"); delete [] uncomp_buffer; delete [] buffer; @@ -317,7 +325,7 @@ CChunkFileReader::Error CChunkFileReader::LoadFile(const std::string &filename, } // Takes ownership of buffer. -CChunkFileReader::Error CChunkFileReader::SaveFile(const std::string &filename, const std::string &title, const char *gitVersion, u8 *buffer, size_t sz) { +CChunkFileReader::Error CChunkFileReader::SaveFile(const Path &filename, const std::string &title, const char *gitVersion, u8 *buffer, size_t sz) { INFO_LOG(SAVESTATE, "ChunkReader: Writing %s", filename.c_str()); File::IOFile pFile(filename, "wb"); @@ -328,7 +336,7 @@ CChunkFileReader::Error CChunkFileReader::SaveFile(const std::string &filename, } // Make sure we can allocate a buffer to compress before compressing. - size_t write_len = snappy_max_compressed_length(sz); + size_t write_len = ZSTD_compressBound(sz); u8 *compressed_buffer = (u8 *)malloc(write_len); u8 *write_buffer = buffer; if (!compressed_buffer) { @@ -336,7 +344,8 @@ CChunkFileReader::Error CChunkFileReader::SaveFile(const std::string &filename, // We'll save uncompressed. Better than not saving... write_len = sz; } else { - snappy_compress((const char *)buffer, sz, (char *)compressed_buffer, &write_len); + // TODO: If free disk space is low, we could max this out to 22? + write_len = ZSTD_compress(compressed_buffer, write_len, buffer, sz, 1); free(buffer); write_buffer = compressed_buffer; @@ -344,7 +353,7 @@ CChunkFileReader::Error CChunkFileReader::SaveFile(const std::string &filename, // Create header SChunkHeader header{}; - header.Compress = compressed_buffer ? 1 : 0; + header.Compress = compressed_buffer ? 2 : 0; header.Revision = REVISION_CURRENT; header.ExpectedSize = (u32)write_len; header.UncompressedSize = (u32)sz; diff --git a/Common/Serialize/Serializer.h b/Common/Serialize/Serializer.h index 38fcd686c9..cfe14f01d8 100644 --- a/Common/Serialize/Serializer.h +++ b/Common/Serialize/Serializer.h @@ -35,6 +35,7 @@ #include "Common/CommonTypes.h" #include "Common/Log.h" +#include "Common/File/Path.h" namespace File { class IOFile; @@ -172,7 +173,7 @@ public: // Load file template template - static Error Load(const std::string &filename, std::string *gitVersion, T& _class, std::string *failureReason) + static Error Load(const Path &filename, std::string *gitVersion, T& _class, std::string *failureReason) { *failureReason = "LoadStateWrongVersion"; @@ -192,7 +193,7 @@ public: // Save file template template - static Error Save(const std::string &filename, const std::string &title, const char *gitVersion, T& _class) + static Error Save(const Path &filename, const std::string &title, const char *gitVersion, T& _class) { // Get data size_t const sz = MeasurePtr(_class); @@ -231,7 +232,7 @@ public: return ERROR_NONE; } - static Error GetFileTitle(const std::string &filename, std::string *title); + static Error GetFileTitle(const Path &filename, std::string *title); private: struct SChunkHeader @@ -249,7 +250,7 @@ private: REVISION_CURRENT = REVISION_TITLE, }; - static Error LoadFile(const std::string &filename, std::string *gitVersion, u8 *&buffer, size_t &sz, std::string *failureReason); - static Error SaveFile(const std::string &filename, const std::string &title, const char *gitVersion, u8 *buffer, size_t sz); + static Error LoadFile(const Path &filename, std::string *gitVersion, u8 *&buffer, size_t &sz, std::string *failureReason); + static Error SaveFile(const Path &filename, const std::string &title, const char *gitVersion, u8 *buffer, size_t sz); static Error LoadFileHeader(File::IOFile &pFile, SChunkHeader &header, std::string *title); }; diff --git a/Common/StringUtils.cpp b/Common/StringUtils.cpp index ee4478a5b4..f54b2e975f 100644 --- a/Common/StringUtils.cpp +++ b/Common/StringUtils.cpp @@ -125,22 +125,6 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _ return true; } -std::string GetFilenameFromPath(std::string full_path) { - size_t pos; -#ifdef _WIN32 - pos = full_path.rfind('\\'); - if (pos != std::string::npos) { - return full_path.substr(pos + 1); - } -#endif - pos = full_path.rfind('/'); - if (pos != std::string::npos) { - return full_path.substr(pos + 1); - } - // No directory components, just return the full path. - return full_path; -} - std::string LineNumberString(const std::string &str) { std::stringstream input(str); std::stringstream output; @@ -187,7 +171,7 @@ void DataToHexString(const uint8_t *data, size_t size, std::string *output) { buffer.TakeAll(output); } -void DataToHexString(const char* prefix, uint32_t startAddr, const uint8_t* data, size_t size, std::string* output) { +void DataToHexString(int indent, uint32_t startAddr, const uint8_t* data, size_t size, std::string* output) { Buffer buffer; size_t i = 0; for (; i < size; i++) { @@ -199,7 +183,7 @@ void DataToHexString(const char* prefix, uint32_t startAddr, const uint8_t* data buffer.Printf("\n"); } if (!(i & 15)) - buffer.Printf("%s%08x ", prefix, startAddr + i); + buffer.Printf("%*s%08x ", indent, "", startAddr + i); buffer.Printf("%02x ", data[i]); } if (size & 15) { @@ -207,8 +191,10 @@ void DataToHexString(const char* prefix, uint32_t startAddr, const uint8_t* data for (size_t j = size; j < padded_size; j++) { buffer.Printf(" "); } + } + if (size > 0) { buffer.Printf(" "); - for (size_t j = size & ~UINT64_C(0xF); j < size; j++) { + for (size_t j = (size - 1ULL) & ~UINT64_C(0xF); j < size; j++) { buffer.Printf("%c", ((data[j] < 0x20) || (data[j] > 0x7e)) ? 0x2e : data[j]); } } diff --git a/Common/StringUtils.h b/Common/StringUtils.h index 7a3e879dca..89127be810 100644 --- a/Common/StringUtils.h +++ b/Common/StringUtils.h @@ -62,7 +62,7 @@ inline bool endsWithNoCase(const std::string &str, const std::string &what) { } void DataToHexString(const uint8_t *data, size_t size, std::string *output); -void DataToHexString(const char* prefix, uint32_t startAddr, const uint8_t* data, size_t size, std::string* output); +void DataToHexString(int indent, uint32_t startAddr, const uint8_t* data, size_t size, std::string* output); std::string StringFromFormat(const char* format, ...); std::string StringFromInt(int value); @@ -101,5 +101,3 @@ inline void CharArrayFromFormat(char (& out)[Count], const char* format, ...) // "C:/Windows/winhelp.exe" to "C:/Windows/", "winhelp", ".exe" bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension); - -std::string GetFilenameFromPath(std::string full_path); diff --git a/Common/Thread/Executor.cpp b/Common/Thread/Executor.cpp deleted file mode 100644 index 6f9c689103..0000000000 --- a/Common/Thread/Executor.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "Common/Thread/Executor.h" - -#include -#include - -namespace threading { - -void SameThreadExecutor::Run(std::function func) { - func(); -} - -void NewThreadExecutor::Run(std::function func) { - threads_.push_back(std::thread(func)); -} - -NewThreadExecutor::~NewThreadExecutor() { - // If Run was ever called... - for (auto &thread : threads_) - thread.join(); - threads_.clear(); -} - -} // namespace threading diff --git a/Common/Thread/Executor.h b/Common/Thread/Executor.h deleted file mode 100644 index 44924952e6..0000000000 --- a/Common/Thread/Executor.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace threading { - -// Stuff that can execute other stuff, like threadpools, should inherit from this. -class Executor { -public: - virtual void Run(std::function func) = 0; - virtual ~Executor() {} -}; - -class SameThreadExecutor : public Executor { -public: - void Run(std::function func) override; -}; - -class NewThreadExecutor : public Executor { -public: - ~NewThreadExecutor() override; - void Run(std::function func) override; - -private: - std::vector threads_; -}; - -} // namespace threading diff --git a/Common/Thread/PrioritizedWorkQueue.cpp b/Common/Thread/PrioritizedWorkQueue.cpp index d80ad80c41..973ef43ce4 100644 --- a/Common/Thread/PrioritizedWorkQueue.cpp +++ b/Common/Thread/PrioritizedWorkQueue.cpp @@ -113,7 +113,7 @@ PrioritizedWorkQueueItem *PrioritizedWorkQueue::Pop() { static std::thread *workThread; static void threadfunc(PrioritizedWorkQueue *wq) { - setCurrentThreadName("PrioQueue"); + SetCurrentThreadName("PrioQueue"); while (true) { PrioritizedWorkQueueItem *item = wq->Pop(); if (!item) { diff --git a/Common/Thread/ThreadPool.cpp b/Common/Thread/ThreadPool.cpp index e64d1c8786..e2560d17dd 100644 --- a/Common/Thread/ThreadPool.cpp +++ b/Common/Thread/ThreadPool.cpp @@ -1,3 +1,5 @@ +#include +#include #include "Common/Thread/ThreadPool.h" #include "Common/Thread/ThreadUtil.h" @@ -36,7 +38,7 @@ void WorkerThread::WaitForCompletion() { } void WorkerThread::WorkFunc() { - setCurrentThreadName("Worker"); + SetCurrentThreadName("Worker"); std::unique_lock guard(mutex); while (active) { // 'active == false' is one of the conditions for signaling, @@ -54,43 +56,27 @@ void WorkerThread::WorkFunc() { } } -void LoopWorkerThread::Process(std::function work, int start, int end) { +void LoopWorkerThread::ProcessLoop(std::function work, int start, int end) { std::lock_guard guard(mutex); - work_ = std::move(work); + loopWork_ = std::move(work); + work_ = [this]() { + loopWork_(start_, end_); + }; start_ = start; end_ = end; jobsTarget = jobsDone + 1; signal.notify_one(); } -void LoopWorkerThread::WorkFunc() { - setCurrentThreadName("LoopWorker"); - std::unique_lock guard(mutex); - while (active) { - // 'active == false' is one of the conditions for signaling, - // do not "optimize" it - while (active && jobsTarget <= jobsDone) { - signal.wait(guard); - } - if (active) { - work_(start_, end_); - - std::lock_guard doneGuard(doneMutex); - jobsDone++; - done.notify_one(); - } - } -} - ///////////////////////////// ThreadPool ThreadPool::ThreadPool(int numThreads) { if (numThreads <= 0) { numThreads_ = 1; INFO_LOG(JIT, "ThreadPool: Bad number of threads %d", numThreads); - } else if (numThreads > 8) { - INFO_LOG(JIT, "ThreadPool: Capping number of threads to 8 (was %d)", numThreads); - numThreads_ = 8; + } else if (numThreads > 16) { + INFO_LOG(JIT, "ThreadPool: Capping number of threads to 16 (was %d)", numThreads); + numThreads_ = 16; } else { numThreads_ = numThreads; } @@ -108,23 +94,32 @@ void ThreadPool::StartWorkers() { } } -void ThreadPool::ParallelLoop(const std::function &loop, int lower, int upper) { +void ThreadPool::ParallelLoop(const std::function &loop, int lower, int upper, int minSize) { + // Don't parallelize tiny loops. + if (minSize == -1) + minSize = 4; + int range = upper - lower; - if (range >= numThreads_ * 2) { // don't parallelize tiny loops (this could be better, maybe add optional parameter that estimates work per iteration) + if (range >= minSize) { std::lock_guard guard(mutex); StartWorkers(); // could do slightly better load balancing for the generic case, // but doesn't matter since all our loops are power of 2 - int chunk = range / numThreads_; + int chunk = std::max(minSize, range / numThreads_); int s = lower; - for (auto& worker : workers) { - worker->Process(loop, s, s+chunk); - s+=chunk; + for (auto &worker : workers) { + // We'll do the last chunk on the current thread. + if (s + chunk >= upper) { + break; + } + worker->ProcessLoop(loop, s, s + chunk); + s += chunk; } // This is the final chunk. - loop(s, upper); - for (auto& worker : workers) { + if (s < upper) + loop(s, upper); + for (auto &worker : workers) { worker->WaitForCompletion(); } } else { @@ -132,3 +127,16 @@ void ThreadPool::ParallelLoop(const std::function &loop, int lowe } } +void ThreadPool::ParallelMemcpy(void *dest, const void *src, int size) { + static const int MIN_SIZE = 128 * 1024; + ParallelLoop([&](int l, int h) { + memmove((uint8_t *)dest + l, (const uint8_t *)src + l, h - l); + }, 0, size, MIN_SIZE); +} + +void ThreadPool::ParallelMemset(void *dest, uint8_t val, int size) { + static const int MIN_SIZE = 128 * 1024; + ParallelLoop([&](int l, int h) { + memset((uint8_t *)dest + l, val, h - l); + }, 0, size, MIN_SIZE); +} diff --git a/Common/Thread/ThreadPool.h b/Common/Thread/ThreadPool.h index 2064b31c28..ceac3c39c6 100644 --- a/Common/Thread/ThreadPool.h +++ b/Common/Thread/ThreadPool.h @@ -23,6 +23,8 @@ public: void WaitForCompletion(); protected: + virtual void WorkFunc(); + std::thread thread; // the worker thread std::condition_variable signal; // used to signal new work std::condition_variable done; // used to signal work completion @@ -30,11 +32,10 @@ protected: bool active = true; int jobsDone = 0; int jobsTarget = 0; -private: - virtual void WorkFunc(); std::function work_; // the work to be done by this thread +private: WorkerThread(const WorkerThread& other) = delete; // prevent copies void operator =(const WorkerThread &other) = delete; }; @@ -42,14 +43,12 @@ private: class LoopWorkerThread final : public WorkerThread { public: LoopWorkerThread() = default; - void Process(std::function work, int start, int end); + void ProcessLoop(std::function work, int start, int end); private: - virtual void WorkFunc() override; - int start_; int end_; - std::function work_; // the work to be done by this thread + std::function loopWork_; // the work to be done by this thread }; // A thread pool manages a set of worker threads, and allows the execution of parallel loops on them @@ -61,7 +60,9 @@ public: // don't need a destructor, "workers" is cleared on delete, // leading to the stopping and joining of all worker threads (RAII and all that) - void ParallelLoop(const std::function &loop, int lower, int upper); + void ParallelLoop(const std::function &loop, int lower, int upper, int minSize); + void ParallelMemcpy(void *dest, const void *src, int sz); + void ParallelMemset(void *dest, uint8_t val, int sz); private: int numThreads_; diff --git a/Common/Thread/ThreadUtil.cpp b/Common/Thread/ThreadUtil.cpp index b7c807e61b..8f35af321b 100644 --- a/Common/Thread/ThreadUtil.cpp +++ b/Common/Thread/ThreadUtil.cpp @@ -40,7 +40,7 @@ static EXCEPTION_DISPOSITION NTAPI ignore_handler(EXCEPTION_RECORD *rec, } #endif -void setCurrentThreadName(const char* threadName) { +void SetCurrentThreadName(const char* threadName) { #ifdef _WIN32 // Set the debugger-visible threadname through an unholy magic hack static const DWORD MS_VC_EXCEPTION = 0x406D1388; diff --git a/Common/Thread/ThreadUtil.h b/Common/Thread/ThreadUtil.h index 2723e2864f..60e6f731fd 100644 --- a/Common/Thread/ThreadUtil.h +++ b/Common/Thread/ThreadUtil.h @@ -1,6 +1,6 @@ #pragma once // Note that name must be a global string that lives until the end of the process, -// for assertThreadName to work. -void setCurrentThreadName(const char *threadName); -void AssertCurrentThreadName(const char *threadName); \ No newline at end of file +// for AssertCurrentThreadName to work. +void SetCurrentThreadName(const char *threadName); +void AssertCurrentThreadName(const char *threadName); diff --git a/Common/UI/Root.cpp b/Common/UI/Root.cpp index fadbaeeb6d..ef60f64e12 100644 --- a/Common/UI/Root.cpp +++ b/Common/UI/Root.cpp @@ -4,11 +4,11 @@ #include "ppsspp_config.h" -#include "Common/UI/Root.h" -#include "Common/UI/ViewGroup.h" - +#include "Common/Input/InputState.h" #include "Common/Log.h" #include "Common/TimeUtil.h" +#include "Common/UI/Root.h" +#include "Common/UI/ViewGroup.h" namespace UI { @@ -346,14 +346,21 @@ bool AxisEvent(const AxisInput &axis, ViewGroup *root) { old.x = dir; } if (axis.axisId == JOYSTICK_AXIS_Y || axis.axisId == JOYSTICK_AXIS_HAT_Y) { - // We stupidly interpret the joystick Y axis backwards on Android and Linux instead of reversing - // it early (see keymaps...). Too late to fix without invalidating a lot of config files, so we - // reverse it here too. + int direction = GetAnalogYDirection(axis.deviceId); + if (direction == 0) { + // We stupidly interpret the joystick Y axis backwards on Android and Linux instead of reversing + // it early (see keymaps...). Too late to fix without invalidating a lot of config files, so we + // reverse it here too. #if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(LINUX) - GenerateKeyFromAxis(old.y, dir, NKCODE_DPAD_UP, NKCODE_DPAD_DOWN); + GenerateKeyFromAxis(old.y, dir, NKCODE_DPAD_UP, NKCODE_DPAD_DOWN); #else - GenerateKeyFromAxis(old.y, dir, NKCODE_DPAD_DOWN, NKCODE_DPAD_UP); + GenerateKeyFromAxis(old.y, dir, NKCODE_DPAD_DOWN, NKCODE_DPAD_UP); #endif + } else if (direction == -1) { + GenerateKeyFromAxis(old.y, dir, NKCODE_DPAD_UP, NKCODE_DPAD_DOWN); + } else { + GenerateKeyFromAxis(old.y, dir, NKCODE_DPAD_DOWN, NKCODE_DPAD_UP); + } old.y = dir; } break; diff --git a/Common/UI/Screen.cpp b/Common/UI/Screen.cpp index f8f4cd10dc..1945b5be4f 100644 --- a/Common/UI/Screen.cpp +++ b/Common/UI/Screen.cpp @@ -106,6 +106,17 @@ bool ScreenManager::key(const KeyInput &key) { bool ScreenManager::axis(const AxisInput &axis) { std::lock_guard guard(inputLock_); + + // Ignore duplicate values to prevent axis values overwriting each other. + uint64_t key = ((uint64_t)axis.axisId << 32) | axis.deviceId; + // Center value far from zero just to ensure we send the first zero. + // PSP games can't see higher resolution than this. + int value = 128 + ceilf(axis.value * 127.5f + 127.5f); + if (lastAxis_[key] == value) { + return false; + } + lastAxis_[key] = value; + bool result = false; // Send center axis to every screen layer. if (axis.value == 0) { diff --git a/Common/UI/Screen.h b/Common/UI/Screen.h index 46f7261ddb..59429d75e7 100644 --- a/Common/UI/Screen.h +++ b/Common/UI/Screen.h @@ -13,9 +13,11 @@ #pragma once -#include +#include #include #include +#include +#include #include "Common/Common.h" #include "Common/Input/InputState.h" @@ -165,4 +167,6 @@ private: // Used for options, in-game menus and other things you expect to be able to back out from onto something. std::vector stack_; std::vector nextStack_; + + std::unordered_map lastAxis_; }; diff --git a/Common/UI/UIScreen.cpp b/Common/UI/UIScreen.cpp index 156eea5c35..3a852dc2ee 100644 --- a/Common/UI/UIScreen.cpp +++ b/Common/UI/UIScreen.cpp @@ -222,12 +222,13 @@ PopupScreen::PopupScreen(std::string title, std::string button1, std::string but } bool PopupScreen::touch(const TouchInput &touch) { - if (!box_ || (touch.flags & TOUCH_DOWN) == 0 || touch.id != 0) { + if (!box_ || (touch.flags & TOUCH_DOWN) == 0) { return UIDialogScreen::touch(touch); } - if (!box_->GetBounds().Contains(touch.x, touch.y)) + if (!box_->GetBounds().Contains(touch.x, touch.y)) { TriggerFinish(DR_BACK); + } return UIDialogScreen::touch(touch); } diff --git a/Core/AVIDump.cpp b/Core/AVIDump.cpp index 1e3f1803d5..99c74af7bf 100644 --- a/Core/AVIDump.cpp +++ b/Core/AVIDump.cpp @@ -23,8 +23,9 @@ extern "C" { #endif +#include "Common/Data/Convert/ColorConv.h" #include "Common/File/FileUtil.h" -#include "Common/ColorConv.h" +#include "Common/File/Path.h" #include "Core/Config.h" #include "Core/AVIDump.h" @@ -94,7 +95,7 @@ bool AVIDump::CreateAVI() { // Use gameID_EmulatedTimestamp for filename std::string discID = g_paramSFO.GetDiscID(); - std::string video_file_name = StringFromFormat("%s%s_%s.avi", GetSysDirectory(DIRECTORY_VIDEO).c_str(), discID.c_str(), KernelTimeNowFormatted().c_str()).c_str(); + Path video_file_name = GetSysDirectory(DIRECTORY_VIDEO) / StringFromFormat("%s_%s.avi", discID.c_str(), KernelTimeNowFormatted().c_str()); s_format_context = avformat_alloc_context(); @@ -106,14 +107,14 @@ bool AVIDump::CreateAVI() { const char *filename = s_format_context->filename; snprintf(s_format_context->filename, sizeof(s_format_context->filename), "%s", video_file_name.c_str()); #endif - INFO_LOG(COMMON, "Recording Video to: %s", filename); + INFO_LOG(COMMON, "Recording Video to: %s", video_file_name.ToVisualString().c_str()); // Make sure that the path exists if (!File::Exists(GetSysDirectory(DIRECTORY_VIDEO))) File::CreateDir(GetSysDirectory(DIRECTORY_VIDEO)); - if (File::Exists(filename)) - File::Delete(filename); + if (File::Exists(video_file_name)) + File::Delete(video_file_name); s_format_context->oformat = av_guess_format("avi", nullptr, nullptr); if (!s_format_context->oformat) diff --git a/Core/Compatibility.cpp b/Core/Compatibility.cpp index 9cddc208d3..b55c7e01d7 100644 --- a/Core/Compatibility.cpp +++ b/Core/Compatibility.cpp @@ -35,8 +35,8 @@ void Compatibility::Load(const std::string &gameID) { { IniFile compat2; // This one is user-editable. Need to load it after the system one. - std::string path = GetSysDirectory(DIRECTORY_SYSTEM) + "compat.ini"; - if (compat2.Load(path)) { + Path path = GetSysDirectory(DIRECTORY_SYSTEM) / "compat.ini"; + if (compat2.Load(path.ToString())) { CheckSettings(compat2, gameID); } } @@ -87,7 +87,6 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) { CheckSetting(iniFile, gameID, "MemstickFixedFree", &flags_.MemstickFixedFree); CheckSetting(iniFile, gameID, "DateLimited", &flags_.DateLimited); CheckSetting(iniFile, gameID, "ReinterpretFramebuffers", &flags_.ReinterpretFramebuffers); - CheckSetting(iniFile, gameID, "DoublePrecisionSinCos", &flags_.DoublePrecisionSinCos); CheckSetting(iniFile, gameID, "ShaderColorBitmask", &flags_.ShaderColorBitmask); CheckSetting(iniFile, gameID, "DisableFirstFrameReadback", &flags_.DisableFirstFrameReadback); CheckSetting(iniFile, gameID, "DisableRangeCulling", &flags_.DisableRangeCulling); diff --git a/Core/Compatibility.h b/Core/Compatibility.h index 1907a5cb33..2e4d47877c 100644 --- a/Core/Compatibility.h +++ b/Core/Compatibility.h @@ -85,7 +85,6 @@ struct CompatFlags { bool MemstickFixedFree; bool DateLimited; bool ReinterpretFramebuffers; - bool DoublePrecisionSinCos; bool ShaderColorBitmask; bool DisableFirstFrameReadback; bool DisableRangeCulling; diff --git a/Core/Config.cpp b/Core/Config.cpp index fc5dfc6fe3..8ba3633dbd 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -500,6 +500,8 @@ static ConfigSetting generalSettings[] = { #endif ConfigSetting("InternalScreenRotation", &g_Config.iInternalScreenRotation, ROTATION_LOCKED_HORIZONTAL), + ConfigSetting("BackgroundAnimation", &g_Config.iBackgroundAnimation, 1, true, false), + #if defined(USING_WIN_UI) ConfigSetting("TopMost", &g_Config.bTopMost, false), ConfigSetting("WindowX", &g_Config.iWindowX, -1), // -1 tells us to center the window. @@ -1215,7 +1217,7 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { bSaveSettings = true; IniFile iniFile; - if (!iniFile.Load(iniFilename_)) { + if (!iniFile.Load(iniFilename_.ToString())) { ERROR_LOG(LOADER, "Failed to read '%s'. Setting config to default.", iniFilename_.c_str()); // Continue anyway to initialize the config. } @@ -1225,7 +1227,7 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { }); iRunCount++; - if (!File::Exists(currentDirectory)) + if (!File::Exists(Path(currentDirectory))) currentDirectory = ""; Section *log = iniFile.GetOrCreateSection(logSectionName); @@ -1262,7 +1264,7 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { for (auto it = pinnedPaths.begin(), end = pinnedPaths.end(); it != end; ++it) { // Unpin paths that are deleted automatically. const std::string &path = it->second; - if (startsWith(path, "http://") || startsWith(path, "https://") || File::Exists(path)) { + if (startsWith(path, "http://") || startsWith(path, "https://") || File::Exists(Path(path))) { vPinnedPaths.push_back(File::ResolvePath(path)); } } @@ -1276,10 +1278,9 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { auto postShaderChain = iniFile.GetOrCreateSection("PostShaderList")->ToMap(); vPostShaderNames.clear(); for (auto it : postShaderChain) { - vPostShaderNames.push_back(it.second); + if (it.second != "Off") + vPostShaderNames.push_back(it.second); } - if (vPostShaderNames.empty()) - vPostShaderNames.push_back("Off"); // This caps the exponent 4 (so 16x.) if (iAnisotropyLevel > 4) { @@ -1315,7 +1316,7 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { // upgrade number in the ini. if (iRunCount % 10 == 0 && bCheckForNewVersion) { std::shared_ptr dl = g_DownloadManager.StartDownloadWithCallback( - "http://www.ppsspp.org/version.json", "", &DownloadCompletedCallback); + "http://www.ppsspp.org/version.json", Path(), &DownloadCompletedCallback); dl->SetHidden(true); } @@ -1367,12 +1368,12 @@ void Config::Save(const char *saveReason) { // if JIT has been forced off, we don't want to screw up the user's ppsspp.ini g_Config.iCpuCore = (int)CPUCore::JIT; } - if (iniFilename_.size() && g_Config.bSaveSettings) { + if (!iniFilename_.empty() && g_Config.bSaveSettings) { saveGameConfig(gameId_, gameIdTitle_); CleanRecent(); IniFile iniFile; - if (!iniFile.Load(iniFilename_.c_str())) { + if (!iniFile.Load(iniFilename_)) { ERROR_LOG(LOADER, "Error saving config - can't read ini '%s'", iniFilename_.c_str()); } @@ -1428,7 +1429,7 @@ void Config::Save(const char *saveReason) { if (LogManager::GetInstance()) LogManager::GetInstance()->SaveConfig(log); - if (!iniFile.Save(iniFilename_.c_str())) { + if (!iniFile.Save(iniFilename_)) { ERROR_LOG(LOADER, "Error saving config (%s)- can't write ini '%s'", saveReason, iniFilename_.c_str()); System_SendMessage("toast", "Failed to save settings!\nCheck permissions, or try to restart the device."); return; @@ -1438,11 +1439,11 @@ void Config::Save(const char *saveReason) { if (!bGameSpecific) //otherwise we already did this in saveGameConfig() { IniFile controllerIniFile; - if (!controllerIniFile.Load(controllerIniFilename_.c_str())) { - ERROR_LOG(LOADER, "Error saving config - can't read ini '%s'", controllerIniFilename_.c_str()); + if (!controllerIniFile.Load(controllerIniFilename_)) { + ERROR_LOG(LOADER, "Error saving controller config - can't read ini first '%s'", controllerIniFilename_.c_str()); } KeyMap::SaveToIni(controllerIniFile); - if (!controllerIniFile.Save(controllerIniFilename_.c_str())) { + if (!controllerIniFile.Save(controllerIniFilename_)) { ERROR_LOG(LOADER, "Error saving config - can't write ini '%s'", controllerIniFilename_.c_str()); return; } @@ -1550,7 +1551,7 @@ void Config::RemoveRecent(const std::string &file) { void Config::CleanRecent() { std::vector cleanedRecent; for (size_t i = 0; i < recentIsos.size(); i++) { - FileLoader *loader = ConstructFileLoader(recentIsos[i]); + FileLoader *loader = ConstructFileLoader(Path(recentIsos[i])); if (loader->ExistsFast()) { // Make sure we don't have any redundant items. auto duplicate = std::find(cleanedRecent.begin(), cleanedRecent.end(), recentIsos[i]); @@ -1563,36 +1564,36 @@ void Config::CleanRecent() { recentIsos = cleanedRecent; } -void Config::SetDefaultPath(const std::string &defaultPath) { +void Config::SetDefaultPath(const Path &defaultPath) { defaultPath_ = defaultPath; } -void Config::AddSearchPath(const std::string &path) { +void Config::AddSearchPath(const Path &path) { searchPath_.push_back(path); } -const std::string Config::FindConfigFile(const std::string &baseFilename) { +const Path Config::FindConfigFile(const std::string &baseFilename) { // Don't search for an absolute path. if (baseFilename.size() > 1 && baseFilename[0] == '/') { - return baseFilename; + return Path(baseFilename); } #ifdef _WIN32 if (baseFilename.size() > 3 && baseFilename[1] == ':' && (baseFilename[2] == '/' || baseFilename[2] == '\\')) { - return baseFilename; + return Path(baseFilename); } #endif for (size_t i = 0; i < searchPath_.size(); ++i) { - std::string filename = searchPath_[i] + baseFilename; + Path filename = searchPath_[i] / baseFilename; if (File::Exists(filename)) { return filename; } } - const std::string filename = defaultPath_.empty() ? baseFilename : defaultPath_ + baseFilename; + const Path filename = defaultPath_ / baseFilename; if (!File::Exists(filename)) { - std::string path; - SplitPath(filename, &path, NULL, NULL); + // Make sure at least the directory it's supposed to be in exists. + Path path = filename.NavigateUp(); if (createdPath_ != path) { File::CreateFullPath(path); createdPath_ = path; @@ -1615,7 +1616,7 @@ void Config::RestoreDefaults() { } bool Config::hasGameConfig(const std::string &pGameId) { - std::string fullIniFilePath = getGameConfigFile(pGameId); + Path fullIniFilePath = getGameConfigFile(pGameId); return File::Exists(fullIniFilePath); } @@ -1628,27 +1629,26 @@ void Config::changeGameSpecific(const std::string &pGameId, const std::string &t } bool Config::createGameConfig(const std::string &pGameId) { - std::string fullIniFilePath = getGameConfigFile(pGameId); + Path fullIniFilePath = getGameConfigFile(pGameId); if (hasGameConfig(pGameId)) { return false; } File::CreateEmptyFile(fullIniFilePath); - return true; } bool Config::deleteGameConfig(const std::string& pGameId) { - std::string fullIniFilePath = getGameConfigFile(pGameId); + Path fullIniFilePath = Path(getGameConfigFile(pGameId)); File::Delete(fullIniFilePath); return true; } -std::string Config::getGameConfigFile(const std::string &pGameId) { +Path Config::getGameConfigFile(const std::string &pGameId) { std::string iniFileName = pGameId + "_ppsspp.ini"; - std::string iniFileNameFull = FindConfigFile(iniFileName); + Path iniFileNameFull = FindConfigFile(iniFileName); return iniFileNameFull; } @@ -1658,7 +1658,7 @@ bool Config::saveGameConfig(const std::string &pGameId, const std::string &title return false; } - std::string fullIniFilePath = getGameConfigFile(pGameId); + Path fullIniFilePath = getGameConfigFile(pGameId); IniFile iniFile; @@ -1686,13 +1686,13 @@ bool Config::saveGameConfig(const std::string &pGameId, const std::string &title } KeyMap::SaveToIni(iniFile); - iniFile.Save(fullIniFilePath); + iniFile.Save(fullIniFilePath.ToString()); return true; } bool Config::loadGameConfig(const std::string &pGameId, const std::string &title) { - std::string iniFileNameFull = getGameConfigFile(pGameId); + Path iniFileNameFull = getGameConfigFile(pGameId); if (!hasGameConfig(pGameId)) { INFO_LOG(LOADER, "Failed to read %s. No game-specific settings found, using global defaults.", iniFileNameFull.c_str()); @@ -1701,7 +1701,7 @@ bool Config::loadGameConfig(const std::string &pGameId, const std::string &title changeGameSpecific(pGameId, title); IniFile iniFile; - iniFile.Load(iniFileNameFull); + iniFile.Load(iniFileNameFull.ToString()); auto postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting")->ToMap(); mPostShaderSetting.clear(); @@ -1712,10 +1712,9 @@ bool Config::loadGameConfig(const std::string &pGameId, const std::string &title auto postShaderChain = iniFile.GetOrCreateSection("PostShaderList")->ToMap(); vPostShaderNames.clear(); for (auto it : postShaderChain) { - vPostShaderNames.push_back(it.second); + if (it.second != "Off") + vPostShaderNames.push_back(it.second); } - if (vPostShaderNames.empty()) - vPostShaderNames.push_back("Off"); IterateSettings(iniFile, [](Section *section, ConfigSetting *setting) { if (setting->perGame_) { @@ -1732,7 +1731,7 @@ void Config::unloadGameConfig() { changeGameSpecific(); IniFile iniFile; - iniFile.Load(iniFilename_); + iniFile.Load(iniFilename_.ToString()); // Reload game specific settings back to standard. IterateSettings(iniFile, [](Section *section, ConfigSetting *setting) { @@ -1750,10 +1749,9 @@ void Config::unloadGameConfig() { auto postShaderChain = iniFile.GetOrCreateSection("PostShaderList")->ToMap(); vPostShaderNames.clear(); for (auto it : postShaderChain) { - vPostShaderNames.push_back(it.second); + if (it.second != "Off") + vPostShaderNames.push_back(it.second); } - if (vPostShaderNames.empty()) - vPostShaderNames.push_back("Off"); LoadStandardControllerIni(); } @@ -1761,7 +1759,7 @@ void Config::unloadGameConfig() { void Config::LoadStandardControllerIni() { IniFile controllerIniFile; - if (!controllerIniFile.Load(controllerIniFilename_)) { + if (!controllerIniFile.Load(controllerIniFilename_.ToString())) { ERROR_LOG(LOADER, "Failed to read %s. Setting controller config to default.", controllerIniFilename_.c_str()); KeyMap::RestoreDefault(); } else { diff --git a/Core/Config.h b/Core/Config.h index 77f9b85b91..81b9bfe8bd 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -22,7 +22,9 @@ #include #include "ppsspp_config.h" + #include "Common/CommonTypes.h" +#include "Common/File/Path.h" #include "Core/ConfigValues.h" extern const char *PPSSPP_GIT_VERSION; @@ -248,6 +250,7 @@ public: bool bShowIDOnGameIcon; float fGameGridScale; bool bShowOnScreenMessages; + int iBackgroundAnimation; // enum BackgroundAnimation // TODO: Maybe move to a separate theme system. uint32_t uItemStyleFg; @@ -491,11 +494,11 @@ public: // Various directories. Autoconfigured, not read from ini. std::string currentDirectory; - std::string externalDirectory; - std::string memStickDirectory; - std::string flash0Directory; - std::string internalDataDirectory; - std::string appCacheDirectory; + Path externalDirectory; + Path memStickDirectory; + Path flash0Directory; + Path internalDataDirectory; + Path appCacheDirectory; // Data for upgrade prompt std::string upgradeMessage; // The actual message from the server is currently not used, need a translation mechanism. So this just acts as a flag. @@ -514,14 +517,14 @@ public: bool loadGameConfig(const std::string &game_id, const std::string &title); bool saveGameConfig(const std::string &pGameId, const std::string &title); void unloadGameConfig(); - std::string getGameConfigFile(const std::string &gameId); + Path getGameConfigFile(const std::string &gameId); bool hasGameConfig(const std::string &game_id); // Used when the file is not found in the search path. Trailing slash. - void SetDefaultPath(const std::string &defaultPath); + void SetDefaultPath(const Path &defaultPath); // Use a trailing slash. - void AddSearchPath(const std::string &path); - const std::string FindConfigFile(const std::string &baseFilename); + void AddSearchPath(const Path &path); + const Path FindConfigFile(const std::string &baseFilename); // Utility functions for "recent" management void AddRecent(const std::string &file); @@ -546,11 +549,11 @@ private: bool reload_ = false; std::string gameId_; std::string gameIdTitle_; - std::string iniFilename_; - std::string controllerIniFilename_; - std::vector searchPath_; - std::string defaultPath_; - std::string createdPath_; + Path iniFilename_; + Path controllerIniFilename_; + std::vector searchPath_; + Path defaultPath_; + Path createdPath_; }; std::map> GetLangValuesMapping(); diff --git a/Core/ConfigValues.h b/Core/ConfigValues.h index 8936f63809..d55af77964 100644 --- a/Core/ConfigValues.h +++ b/Core/ConfigValues.h @@ -116,3 +116,9 @@ enum class UnthrottleMode { SKIP_DRAW = 1, SKIP_FLIP = 2, }; + +enum class BackgroundAnimation { + OFF = 0, + FLOATING_SYMBOLS = 1, + RECENT_GAMES = 2, +}; diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 30c555b669..68e918a7f0 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -138,7 +138,7 @@ Level3 - ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_M_IX86=1;_DEBUG;_LIB;_UNICODE;UNICODE;MINIUPNP_STATICLIB;%(PreprocessorDefinitions) StreamingSIMDExtensions2 Precise @@ -161,7 +161,7 @@ Level3 - ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib;../ext/zstd/lib _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_M_X64=1;_DEBUG;_LIB;_UNICODE;UNICODE;MINIUPNP_STATICLIB;%(PreprocessorDefinitions) NotSet Precise @@ -185,7 +185,7 @@ Level3 - ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\aarch64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\aarch64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) NotSet Precise @@ -209,7 +209,7 @@ Level3 - ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\arm\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\arm\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) NotSet Precise @@ -237,7 +237,7 @@ MaxSpeed true true - ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib false StreamingSIMDExtensions2 Precise @@ -268,7 +268,7 @@ MaxSpeed true true - ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib;../ext/zstd/lib NotSet Precise false @@ -299,7 +299,7 @@ MaxSpeed true true - ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\aarch64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\aarch64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib NotSet Precise false @@ -330,7 +330,7 @@ MaxSpeed true true - ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\arm\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\arm\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib NotSet Precise false @@ -361,14 +361,17 @@ - + + + - - + + + @@ -376,45 +379,123 @@ + + - + + + + + + + + + + + + - + + + + + + + + + true + true + true + true + true + true + true + true + + + + + true + true + true + true + true + true + true + true + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -442,6 +523,7 @@ + @@ -956,9 +1038,14 @@ + + + + + @@ -988,6 +1075,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index f66273cddd..c6a59d5d51 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -800,9 +800,6 @@ Ext\jpge - - Ext\libzip - Ext\libzip @@ -818,12 +815,6 @@ Ext\libzip - - Ext\libzip - - - Ext\libzip - Ext\libzip @@ -860,9 +851,6 @@ Ext\libzip - - Ext\libzip - Ext\libzip @@ -872,9 +860,6 @@ Ext\libzip - - Ext\libzip - Ext\libzip @@ -923,12 +908,6 @@ Ext\libzip - - Ext\libzip - - - Ext\libzip - Ext\libzip @@ -980,6 +959,219 @@ Debugger + + Debugger\WebSocket + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + @@ -1676,6 +1868,24 @@ Debugger + + Debugger\WebSocket + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + diff --git a/Core/CoreParameter.h b/Core/CoreParameter.h index b45384a002..a0589aefc5 100644 --- a/Core/CoreParameter.h +++ b/Core/CoreParameter.h @@ -55,9 +55,9 @@ struct CoreParameter { GraphicsContext *graphicsContext = nullptr; // TODO: Find a better place. bool enableSound; // there aren't multiple sound cores. - std::string fileToStart; - std::string mountIso; // If non-empty, and fileToStart is an ELF or PBP, will mount this ISO in the background to umd1:. - std::string mountRoot; // If non-empty, and fileToStart is an ELF or PBP, mount this as host0: / umd0:. + Path fileToStart; + Path mountIso; // If non-empty, and fileToStart is an ELF or PBP, will mount this ISO in the background to umd1:. + Path mountRoot; // If non-empty, and fileToStart is an ELF or PBP, mount this as host0: / umd0:. std::string errorString; bool startBreak; diff --git a/Core/CwCheat.cpp b/Core/CwCheat.cpp index c86ef008b4..5605601ff0 100644 --- a/Core/CwCheat.cpp +++ b/Core/CwCheat.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "Common/Data/Text/I18n.h" #include "Common/StringUtils.h" #include "Common/Serialize/Serializer.h" @@ -43,15 +44,14 @@ static inline std::string TrimString(const std::string &s) { class CheatFileParser { public: - CheatFileParser(const std::string &filename, const std::string &gameID = "") { -#if defined(_WIN32) && !defined(__MINGW32__) - file_.open(ConvertUTF8ToWString(filename)); -#else - file_.open(filename.c_str()); -#endif - + CheatFileParser(const Path &filename, const std::string &gameID = "") { + fp_ = File::OpenCFile(filename, "rt"); validGameID_ = ReplaceAll(gameID, "-", ""); } + ~CheatFileParser() { + if (fp_) + fclose(fp_); + } bool Parse(); @@ -75,7 +75,7 @@ protected: void ParseDataLine(const std::string &line, CheatCodeFormat format); bool ValidateGameID(const std::string &gameID); - std::ifstream file_; + FILE *fp_ = nullptr; std::string validGameID_; int line_ = 0; @@ -92,10 +92,13 @@ protected: }; bool CheatFileParser::Parse() { - for (line_ = 1; file_ && !file_.eof(); ++line_) { - std::string line; - getline(file_, line, '\n'); - line = TrimString(line); + for (line_ = 1; fp_ && !feof(fp_); ++line_) { + char temp[2048]; + char *tempLine = fgets(temp, sizeof(temp), fp_); + if (!tempLine) + continue; + + std::string line = TrimString(tempLine); // Minimum length 5 is shortest possible _ lines name of the game "_G N+" // and a minimum of 1 displayable character in cheat name string "_C0 1" @@ -353,7 +356,7 @@ void hleCheat(u64 userdata, int cyclesLate) { } CWCheatEngine::CWCheatEngine(const std::string &gameID) : gameID_(gameID) { - filename_ = GetSysDirectory(DIRECTORY_CHEATS) + gameID_ + ".ini"; + filename_ = GetSysDirectory(DIRECTORY_CHEATS) / (gameID_ + ".ini"); } void CWCheatEngine::CreateCheatFile() { @@ -372,7 +375,7 @@ void CWCheatEngine::CreateCheatFile() { } } -std::string CWCheatEngine::CheatFilename() { +Path CWCheatEngine::CheatFilename() { return filename_; } @@ -953,8 +956,7 @@ void CWCheatEngine::ExecuteOp(const CheatOperation &op, const CheatCode &cheat, auto shaderChain = GetFullPostShadersChain(g_Config.vPostShaderNames); if (op.PostShaderUniform.shader < shaderChain.size()) { std::string shaderName = shaderChain[op.PostShaderUniform.shader]->section; - if (shaderName != "Off") - g_Config.mPostShaderSetting[StringFromFormat("%sSettingValue%d", shaderName.c_str(), op.PostShaderUniform.uniform + 1)] = op.PostShaderUniform.value.f; + g_Config.mPostShaderSetting[StringFromFormat("%sSettingValue%d", shaderName.c_str(), op.PostShaderUniform.uniform + 1)] = op.PostShaderUniform.value.f; } } break; @@ -969,21 +971,19 @@ void CWCheatEngine::ExecuteOp(const CheatOperation &op, const CheatCode &cheat, } value; value.u = Memory::Read_U32(op.addr); std::string shaderName = shaderChain[op.PostShaderUniform.shader]->section; - if (shaderName != "Off") { - switch (op.PostShaderUniform.format) { - case 0: - g_Config.mPostShaderSetting[StringFromFormat("%sSettingValue%d", shaderName.c_str(), op.PostShaderUniform.uniform + 1)] = value.u & 0x000000FF; - break; - case 1: - g_Config.mPostShaderSetting[StringFromFormat("%sSettingValue%d", shaderName.c_str(), op.PostShaderUniform.uniform + 1)] = value.u & 0x0000FFFF; - break; - case 2: - g_Config.mPostShaderSetting[StringFromFormat("%sSettingValue%d", shaderName.c_str(), op.PostShaderUniform.uniform + 1)] = value.u; - break; - case 3: - g_Config.mPostShaderSetting[StringFromFormat("%sSettingValue%d", shaderName.c_str(), op.PostShaderUniform.uniform + 1)] = value.f; - break; - } + switch (op.PostShaderUniform.format) { + case 0: + g_Config.mPostShaderSetting[StringFromFormat("%sSettingValue%d", shaderName.c_str(), op.PostShaderUniform.uniform + 1)] = value.u & 0x000000FF; + break; + case 1: + g_Config.mPostShaderSetting[StringFromFormat("%sSettingValue%d", shaderName.c_str(), op.PostShaderUniform.uniform + 1)] = value.u & 0x0000FFFF; + break; + case 2: + g_Config.mPostShaderSetting[StringFromFormat("%sSettingValue%d", shaderName.c_str(), op.PostShaderUniform.uniform + 1)] = value.u; + break; + case 3: + g_Config.mPostShaderSetting[StringFromFormat("%sSettingValue%d", shaderName.c_str(), op.PostShaderUniform.uniform + 1)] = value.f; + break; } } } diff --git a/Core/CwCheat.h b/Core/CwCheat.h index 686694e769..28ed82791a 100644 --- a/Core/CwCheat.h +++ b/Core/CwCheat.h @@ -8,6 +8,7 @@ #include #include +#include "Common/File/Path.h" #include "Core/MemMap.h" class PointerWrap; @@ -49,7 +50,7 @@ public: std::vector FileInfo(); void ParseCheats(); void CreateCheatFile(); - std::string CheatFilename(); + Path CheatFilename(); void Run(); bool HasCheats(); void InvalidateICache(u32 addr, int size); @@ -67,5 +68,5 @@ private: std::vector cheats_; std::string gameID_; - std::string filename_; + Path filename_; }; diff --git a/Core/Debugger/DisassemblyManager.cpp b/Core/Debugger/DisassemblyManager.cpp index e113ef38d1..40daa83fef 100644 --- a/Core/Debugger/DisassemblyManager.cpp +++ b/Core/Debugger/DisassemblyManager.cpp @@ -23,6 +23,7 @@ #include "ext/xxhash.h" #include "Common/CommonTypes.h" +#include "Common/Data/Encoding/Utf8.h" #include "Core/MemMap.h" #include "Core/System.h" #include "Core/MIPS/MIPSCodeUtils.h" @@ -41,6 +42,49 @@ bool isInInterval(u32 start, u32 size, u32 value) return start <= value && value <= (start+size-1); } +bool IsLikelyStringAt(uint32_t addr) { + uint32_t maxLen = Memory::ValidSize(addr, 128); + if (maxLen <= 1) + return false; + const char *p = Memory::GetCharPointer(addr); + // If there's no terminator nearby, let's say no. + if (memchr(p, 0, maxLen) == nullptr) + return false; + + // Allow tabs and newlines. + static constexpr bool validControl[] = { + false, false, false, false, false, false, false, false, + false, true, true, true, false, true, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + }; + + // Check that there's some bytes before the terminator that look like a string. + UTF8 utf(p); + if (utf.end()) + return false; + + char verify[4]; + while (!utf.end()) { + if (utf.invalid()) + return false; + + int pos = utf.byteIndex(); + uint32_t c = utf.next(); + int len = UTF8::encode(verify, c); + // Our decoder is a bit lax, so let's verify this is a normal encoding. + // This prevents us from trying to output invalid encodings in the debugger. + if (memcmp(p + pos, verify, len) != 0 || pos + len != utf.byteIndex()) + return false; + + if (c < ARRAY_SIZE(validControl) && !validControl[c]) + return false; + if (c > 0x0010FFFF) + return false; + } + + return true; +} static HashType computeHash(u32 address, u32 size) { diff --git a/Core/Debugger/DisassemblyManager.h b/Core/Debugger/DisassemblyManager.h index 51b1f1959e..00ff46c6fc 100644 --- a/Core/Debugger/DisassemblyManager.h +++ b/Core/Debugger/DisassemblyManager.h @@ -224,3 +224,4 @@ private: }; bool isInInterval(u32 start, u32 size, u32 value); +bool IsLikelyStringAt(uint32_t addr); diff --git a/Core/Debugger/MemBlockInfo.cpp b/Core/Debugger/MemBlockInfo.cpp index 5fa8c70517..5750c6735f 100644 --- a/Core/Debugger/MemBlockInfo.cpp +++ b/Core/Debugger/MemBlockInfo.cpp @@ -46,7 +46,7 @@ private: uint64_t ticks = 0; uint32_t pc = 0; bool allocated = false; - char tag[32]{}; + char tag[128]{}; Slab *prev = nullptr; Slab *next = nullptr; @@ -77,7 +77,7 @@ struct PendingNotifyMem { uint32_t size; uint64_t ticks; uint32_t pc; - char tag[32]; + char tag[128]; }; static constexpr size_t MAX_PENDING_NOTIFIES = 512; @@ -87,6 +87,7 @@ static MemSlabMap writeMap; static MemSlabMap textureMap; static std::vector pendingNotifies; static std::mutex pendingMutex; +static int detailedOverride; MemSlabMap::MemSlabMap() { Reset(); @@ -199,7 +200,7 @@ void MemSlabMap::DoState(PointerWrap &p) { } void MemSlabMap::Slab::DoState(PointerWrap &p) { - auto s = p.Section("MemSlabMapSlab", 1, 2); + auto s = p.Section("MemSlabMapSlab", 1, 3); if (!s) return; @@ -208,8 +209,12 @@ void MemSlabMap::Slab::DoState(PointerWrap &p) { Do(p, ticks); Do(p, pc); Do(p, allocated); - if (s >= 2) { + if (s >= 3) { Do(p, tag); + } else if (s >= 2) { + char shortTag[32]; + Do(p, shortTag); + memcpy(tag, shortTag, sizeof(shortTag)); } else { std::string stringTag; Do(p, stringTag); @@ -369,7 +374,7 @@ void NotifyMemInfoPC(MemBlockFlags flags, uint32_t start, uint32_t size, uint32_ bool needFlush = false; // When the setting is off, we skip smaller info to keep things fast. - if (g_Config.bDebugMemInfoDetailed || size >= 0x100) { + if (size >= 0x100 || MemBlockInfoDetailed()) { PendingNotifyMem info{ flags, start, size }; info.ticks = CoreTiming::GetTicks(); info.pc = pc; @@ -390,10 +395,12 @@ void NotifyMemInfoPC(MemBlockFlags flags, uint32_t start, uint32_t size, uint32_ FlushPendingMemInfo(); } - if (flags & MemBlockFlags::WRITE) { - CBreakPoints::ExecMemCheck(start, true, size, pc, tagStr); - } else if (flags & MemBlockFlags::READ) { - CBreakPoints::ExecMemCheck(start, false, size, pc, tagStr); + if (!(flags & MemBlockFlags::SKIP_MEMCHECK)) { + if (flags & MemBlockFlags::WRITE) { + CBreakPoints::ExecMemCheck(start, true, size, pc, tagStr); + } else if (flags & MemBlockFlags::READ) { + CBreakPoints::ExecMemCheck(start, false, size, pc, tagStr); + } } } @@ -429,6 +436,20 @@ std::vector FindMemInfoByFlag(MemBlockFlags flags, uint32_t start, return results; } +std::string GetMemWriteTagAt(uint32_t start, uint32_t size) { + std::vector memRangeInfo = FindMemInfoByFlag(MemBlockFlags::WRITE, start, size); + for (auto range : memRangeInfo) { + return range.tag; + } + + // Fall back to alloc and texture, especially for VRAM. We prefer write above. + memRangeInfo = FindMemInfoByFlag(MemBlockFlags::ALLOC | MemBlockFlags::TEXTURE, start, size); + for (auto range : memRangeInfo) { + return range.tag; + } + return "none"; +} + void MemBlockInfoInit() { std::lock_guard guard(pendingMutex); pendingNotifies.reserve(MAX_PENDING_NOTIFIES); @@ -454,3 +475,16 @@ void MemBlockInfoDoState(PointerWrap &p) { writeMap.DoState(p); textureMap.DoState(p); } + +// Used by the debugger. +void MemBlockOverrideDetailed() { + detailedOverride++; +} + +void MemBlockReleaseDetailed() { + detailedOverride--; +} + +bool MemBlockInfoDetailed() { + return g_Config.bDebugMemInfoDetailed || detailedOverride != 0; +} diff --git a/Core/Debugger/MemBlockInfo.h b/Core/Debugger/MemBlockInfo.h index e46bea8c87..786e713ba0 100644 --- a/Core/Debugger/MemBlockInfo.h +++ b/Core/Debugger/MemBlockInfo.h @@ -34,6 +34,8 @@ enum class MemBlockFlags { READ = 0x0800, FREE = 0x1000, SUB_FREE = 0x2000, + + SKIP_MEMCHECK = 0x00010000, }; ENUM_CLASS_BITOPS(MemBlockFlags); @@ -47,7 +49,8 @@ struct MemBlockInfo { bool allocated; }; -void NotifyMemInfo(MemBlockFlags flags, uint32_t start, uint32_t size, const char *str, size_t strLength); +void NotifyMemInfo(MemBlockFlags flags, uint32_t start, uint32_t size, const char *tag, size_t tagLength); +void NotifyMemInfoPC(MemBlockFlags flags, uint32_t start, uint32_t size, uint32_t pc, const char *tag, size_t tagLength); // This lets us avoid calling strlen on string constants, instead the string length (including null, // so we have to subtract 1) is computed at compile time. @@ -63,6 +66,12 @@ inline void NotifyMemInfo(MemBlockFlags flags, uint32_t start, uint32_t size, co std::vector FindMemInfo(uint32_t start, uint32_t size); std::vector FindMemInfoByFlag(MemBlockFlags flags, uint32_t start, uint32_t size); +std::string GetMemWriteTagAt(uint32_t start, uint32_t size); + void MemBlockInfoInit(); void MemBlockInfoShutdown(); void MemBlockInfoDoState(PointerWrap &p); + +void MemBlockOverrideDetailed(); +void MemBlockReleaseDetailed(); +bool MemBlockInfoDetailed(); diff --git a/Core/Debugger/SymbolMap.cpp b/Core/Debugger/SymbolMap.cpp index 772f5ca1b5..809044353d 100644 --- a/Core/Debugger/SymbolMap.cpp +++ b/Core/Debugger/SymbolMap.cpp @@ -65,15 +65,16 @@ void SymbolMap::Clear() { activeNeedUpdate_ = false; } -bool SymbolMap::LoadSymbolMap(const char *filename) { +bool SymbolMap::LoadSymbolMap(const Path &filename) { Clear(); // let's not recurse the lock std::lock_guard guard(lock_); + // TODO(scoped): We're screwed here #if defined(_WIN32) && defined(UNICODE) - gzFile f = gzopen_w(ConvertUTF8ToWString(filename).c_str(), "r"); + gzFile f = gzopen_w(filename.ToWString().c_str(), "r"); #else - gzFile f = gzopen(filename, "r"); + gzFile f = gzopen(filename.c_str(), "r"); #endif if (f == Z_NULL) @@ -186,7 +187,7 @@ bool SymbolMap::LoadSymbolMap(const char *filename) { return started; } -void SymbolMap::SaveSymbolMap(const char *filename) const { +void SymbolMap::SaveSymbolMap(const Path &filename) const { std::lock_guard guard(lock_); // Don't bother writing a blank file. @@ -195,9 +196,10 @@ void SymbolMap::SaveSymbolMap(const char *filename) const { } #if defined(_WIN32) && defined(UNICODE) - gzFile f = gzopen_w(ConvertUTF8ToWString(filename).c_str(), "w9"); + gzFile f = gzopen_w(filename.ToWString().c_str(), "w9"); #else - gzFile f = gzopen(filename, "w9"); + // TODO(scoped): Use gzdopen? If we care, otherwise just compress into a buffer. + gzFile f = gzopen(filename.c_str(), "w9"); #endif if (f == Z_NULL) @@ -222,7 +224,7 @@ void SymbolMap::SaveSymbolMap(const char *filename) const { gzclose(f); } -bool SymbolMap::LoadNocashSym(const char *filename) { +bool SymbolMap::LoadNocashSym(const Path &filename) { std::lock_guard guard(lock_); FILE *f = File::OpenCFile(filename, "r"); if (!f) @@ -280,7 +282,7 @@ bool SymbolMap::LoadNocashSym(const char *filename) { return true; } -void SymbolMap::SaveNocashSym(const char *filename) const { +void SymbolMap::SaveNocashSym(const Path &filename) const { std::lock_guard guard(lock_); // Don't bother writing a blank file. diff --git a/Core/Debugger/SymbolMap.h b/Core/Debugger/SymbolMap.h index 1a58dd2ac1..c0ee42ee75 100644 --- a/Core/Debugger/SymbolMap.h +++ b/Core/Debugger/SymbolMap.h @@ -24,6 +24,7 @@ #include #include "Common/CommonTypes.h" +#include "Common/File/Path.h" enum SymbolType { ST_NONE = 0, @@ -69,10 +70,10 @@ public: void Clear(); void SortSymbols(); - bool LoadSymbolMap(const char *filename); - void SaveSymbolMap(const char *filename) const; - bool LoadNocashSym(const char *ilename); - void SaveNocashSym(const char *filename) const; + bool LoadSymbolMap(const Path &filename); + void SaveSymbolMap(const Path &filename) const; + bool LoadNocashSym(const Path &filename); + void SaveNocashSym(const Path &filename) const; SymbolType GetSymbolType(u32 address); bool GetSymbolInfo(SymbolInfo *info, u32 address, SymbolType symmask = ST_FUNCTION); diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index c609e08698..f3f83254af 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -57,6 +57,7 @@ #include "Core/Debugger/WebSocket/GPURecordSubscriber.h" #include "Core/Debugger/WebSocket/HLESubscriber.h" #include "Core/Debugger/WebSocket/InputSubscriber.h" +#include "Core/Debugger/WebSocket/MemoryInfoSubscriber.h" #include "Core/Debugger/WebSocket/MemorySubscriber.h" #include "Core/Debugger/WebSocket/SteppingSubscriber.h" @@ -70,6 +71,7 @@ static const std::vector subscribers({ &WebSocketGPURecordInit, &WebSocketHLEInit, &WebSocketInputInit, + &WebSocketMemoryInfoInit, &WebSocketMemoryInit, &WebSocketSteppingInit, }); @@ -124,7 +126,7 @@ void HandleDebuggerRequest(const http::Request &request) { if (!ws) return; - setCurrentThreadName("Debugger"); + SetCurrentThreadName("Debugger"); UpdateConnected(1); SetupDebuggerLock(); diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.cpp b/Core/Debugger/WebSocket/DisasmSubscriber.cpp index f8820cee48..ab43655d56 100644 --- a/Core/Debugger/WebSocket/DisasmSubscriber.cpp +++ b/Core/Debugger/WebSocket/DisasmSubscriber.cpp @@ -47,7 +47,6 @@ public: protected: void WriteDisasmLine(JsonWriter &json, const DisassemblyLineInfo &l); void WriteBranchGuide(JsonWriter &json, const BranchLine &l); - uint32_t RoundAddressUp(uint32_t addr); DisassemblyManager disasm_; }; @@ -135,12 +134,20 @@ void WebSocketDisasmState::WriteDisasmLine(JsonWriter &json, const DisassemblyLi json.writeNull("dataSymbol"); } - bool enabled; + bool enabled = false; + int breakpointOffset = -1; + for (u32 i = 0; i < l.totalSize; i += 4) { + if (CBreakPoints::IsAddressBreakPoint(addr + i, &enabled)) + breakpointOffset = i; + if (breakpointOffset != -1 && enabled) + break; + } // TODO: Account for bp inside macro? - if (CBreakPoints::IsAddressBreakPoint(addr, &enabled)) { + if (breakpointOffset != -1) { json.pushDict("breakpoint"); json.writeBool("enabled", enabled); - auto cond = CBreakPoints::GetBreakPointCondition(addr); + json.writeUint("address", addr + breakpointOffset); + auto cond = CBreakPoints::GetBreakPointCondition(addr + breakpointOffset); if (cond) json.writeString("condition", cond->expressionString); else @@ -181,6 +188,10 @@ void WebSocketDisasmState::WriteDisasmLine(JsonWriter &json, const DisassemblyLi json.writeUint("uintValue", Memory::ReadUnchecked_U32(l.info.relevantAddress)); else json.writeNull("uintValue"); + if (IsLikelyStringAt(l.info.relevantAddress)) + json.writeString("stringValue", Memory::GetCharPointer(l.info.relevantAddress)); + else + json.writeNull("stringValue"); json.pop(); } else { json.writeNull("relevantData"); @@ -240,18 +251,6 @@ void WebSocketDisasmState::WriteBranchGuide(JsonWriter &json, const BranchLine & json.pop(); } -uint32_t WebSocketDisasmState::RoundAddressUp(uint32_t addr) { - if (addr < PSP_GetScratchpadMemoryBase()) - return PSP_GetScratchpadMemoryBase(); - else if (addr >= PSP_GetScratchpadMemoryEnd() && addr < PSP_GetVidMemBase()) - return PSP_GetVidMemBase(); - else if (addr >= PSP_GetVidMemEnd() && addr < PSP_GetKernelMemoryBase()) - return PSP_GetKernelMemoryBase(); - else if (addr >= PSP_GetUserMemoryEnd()) - return PSP_GetScratchpadMemoryBase(); - return addr; -} - // Request the current PSP memory base address (memory.base) // // WARNING: Avoid this unless you have a good reason. Uses PPSSPP's address space. @@ -375,7 +374,7 @@ void WebSocketDisasmState::Disasm(DebuggerRequest &req) { // Parameters: // - thread: optional number indicating the thread id (may not affect search much.) // - address: starting address as a number. -// - end: optional end address as a number (otherwise uses start.) +// - end: optional end address as a number (otherwise uses start address.) // - match: string to search for. // - displaySymbols: optional, specify false to hide symbols in the searched parameters. // @@ -402,7 +401,7 @@ void WebSocketDisasmState::SearchDisasm(DebuggerRequest &req) { return; bool loopSearch = end <= start; - start = RoundAddressUp(start); + start = RoundMemAddressUp(start); if ((end <= start) != loopSearch) { // We must've passed end by rounding up. JsonWriter &json = req.Respond(); @@ -411,7 +410,7 @@ void WebSocketDisasmState::SearchDisasm(DebuggerRequest &req) { } // We do this after the check in case both were in unused memory. - end = RoundAddressUp(end); + end = RoundMemAddressUp(end); std::transform(match.begin(), match.end(), match.begin(), ::tolower); @@ -442,7 +441,7 @@ void WebSocketDisasmState::SearchDisasm(DebuggerRequest &req) { break; } - addr = RoundAddressUp(addr + line.totalSize); + addr = RoundMemAddressUp(addr + line.totalSize); } while (addr != end); JsonWriter &json = req.Respond(); diff --git a/Core/Debugger/WebSocket/GPURecordSubscriber.cpp b/Core/Debugger/WebSocket/GPURecordSubscriber.cpp index 4ed21fbbcf..f31aa26d54 100644 --- a/Core/Debugger/WebSocket/GPURecordSubscriber.cpp +++ b/Core/Debugger/WebSocket/GPURecordSubscriber.cpp @@ -31,7 +31,7 @@ struct WebSocketGPURecordState : public DebuggerSubscriber { protected: bool pending_ = false; std::string lastTicket_; - std::string lastFilename_; + Path lastFilename_; }; DebuggerSubscriber *WebSocketGPURecordInit(DebuggerEventHandlerMap &map) { @@ -63,7 +63,7 @@ void WebSocketGPURecordState::Dump(DebuggerRequest &req) { return req.Fail("Recording already in progress"); pending_ = true; - GPURecord::SetCallback([=](const std::string &filename) { + GPURecord::SetCallback([=](const Path &filename) { lastFilename_ = filename; pending_ = false; }); diff --git a/Core/Debugger/WebSocket/MemoryInfoSubscriber.cpp b/Core/Debugger/WebSocket/MemoryInfoSubscriber.cpp new file mode 100644 index 0000000000..30eb8cdd02 --- /dev/null +++ b/Core/Debugger/WebSocket/MemoryInfoSubscriber.cpp @@ -0,0 +1,394 @@ +// Copyright (c) 2021- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include +#include "Core/MIPS/MIPS.h" +#include "Core/MIPS/MIPSDebugInterface.h" +#include "Core/Debugger/MemBlockInfo.h" +#include "Core/Debugger/WebSocket/MemoryInfoSubscriber.h" +#include "Core/Debugger/WebSocket/WebSocketUtils.h" +#include "Core/MemMap.h" + +class WebSocketMemoryInfoState : public DebuggerSubscriber { +public: + WebSocketMemoryInfoState() { + } + ~WebSocketMemoryInfoState() override { + UpdateOverride(false); + } + + void Mapping(DebuggerRequest &req); + void Config(DebuggerRequest &req); + void Set(DebuggerRequest &req); + void List(DebuggerRequest &req); + void Search(DebuggerRequest &req); + +protected: + void UpdateOverride(bool flag); + + bool detailOverride_ = false; +}; + +DebuggerSubscriber *WebSocketMemoryInfoInit(DebuggerEventHandlerMap &map) { + auto p = new WebSocketMemoryInfoState(); + map["memory.mapping"] = std::bind(&WebSocketMemoryInfoState::Mapping, p, std::placeholders::_1); + map["memory.info.config"] = std::bind(&WebSocketMemoryInfoState::Config, p, std::placeholders::_1); + map["memory.info.set"] = std::bind(&WebSocketMemoryInfoState::Set, p, std::placeholders::_1); + map["memory.info.list"] = std::bind(&WebSocketMemoryInfoState::List, p, std::placeholders::_1); + map["memory.info.search"] = std::bind(&WebSocketMemoryInfoState::Search, p, std::placeholders::_1); + + return p; +} + +void WebSocketMemoryInfoState::UpdateOverride(bool flag) { + if (detailOverride_ && !flag) + MemBlockReleaseDetailed(); + if (!detailOverride_ && flag) + MemBlockOverrideDetailed(); + detailOverride_ = flag; +} + +// List memory map data (memory.mapping) +// +// No parameters. +// +// Response (same event name): +// - ranges: array of objects: +// - type: one of "ram", "vram", "sram". +// - subtype: "primary" or "mirror". +// - name: string, friendly name. +// - address: number, start address of range. +// - size: number, in bytes. +// +// Note: Even if you set false, may stay enabled if set by user or another debug session. +void WebSocketMemoryInfoState::Mapping(DebuggerRequest &req) { + struct MemRange { + const char *type; + const char *subtype; + const char *name; + const uint32_t address; + const uint32_t size; + }; + constexpr uint32_t kernelMemorySize = PSP_GetKernelMemoryEnd() - PSP_GetKernelMemoryBase(); + constexpr uint32_t volatileMemorySize = PSP_GetVolatileMemoryEnd() - PSP_GetVolatileMemoryStart(); + static constexpr MemRange ranges[] = { + { "sram", "primary", "Scratchpad", PSP_GetScratchpadMemoryBase(), Memory::SCRATCHPAD_SIZE }, + { "vram", "primary", "VRAM", PSP_GetVidMemBase(), Memory::VRAM_SIZE }, + { "vram", "mirror", "VRAM (Swizzled)", PSP_GetVidMemBase() + Memory::VRAM_SIZE * 1, Memory::VRAM_SIZE }, + { "vram", "mirror", "VRAM (Mirror)", PSP_GetVidMemBase() + Memory::VRAM_SIZE * 2, Memory::VRAM_SIZE }, + { "vram", "mirror", "VRAM (Swizzled + Interleaved)", PSP_GetVidMemBase() + Memory::VRAM_SIZE * 3, Memory::VRAM_SIZE }, + { "ram", "primary", "Kernel Memory", 0x80000000 | PSP_GetKernelMemoryBase(), kernelMemorySize }, + { "ram", "primary", "Volatile Memory", PSP_GetVolatileMemoryStart(), volatileMemorySize }, + // Size is specially calculated. + { "ram", "primary", "User Memory", PSP_GetUserMemoryBase(), 0 }, + }; + + JsonWriter &json = req.Respond(); + json.pushArray("ranges"); + for (auto range : ranges) { + uint32_t size = range.size; + if (size == 0) { + size = Memory::g_MemorySize; + if (size == 0) { + size = Memory::RAM_NORMAL_SIZE; + } + size -= kernelMemorySize + volatileMemorySize; + } + json.pushDict(); + json.writeString("type", range.type); + json.writeString("subtype", range.subtype); + json.writeString("name", range.name); + json.writeUint("address", range.address); + json.writeUint("size", size); + json.pop(); + + // Also write the uncached range. + json.pushDict(); + json.writeString("type", range.type); + json.writeString("subtype", "mirror"); + json.writeString("name", std::string("Uncached ") + range.name); + json.writeUint("address", 0x40000000 | range.address); + json.writeUint("size", size); + json.pop(); + } + json.pop(); +} + +// Update memory info tracking config (memory.info.config) +// +// Parameters: +// - detailed: optional, boolean to force enable detailed tracking (perf impact.) +// +// Response (same event name): +// - detailed: boolean state of tracking before any changes. +// +// Note: Even if you set false, may stay enabled if set by user or another debug session. +void WebSocketMemoryInfoState::Config(DebuggerRequest &req) { + bool setDetailed = req.HasParam("detailed"); + bool detailed = false; + if (!req.ParamBool("detailed", &detailed, DebuggerParamType::OPTIONAL)) + return; + + JsonWriter &json = req.Respond(); + json.writeBool("detailed", MemBlockInfoDetailed()); + + if (setDetailed) + UpdateOverride(detailed); +} + +static MemBlockFlags FlagFromType(const std::string &type) { + if (type == "write") + return MemBlockFlags::WRITE; + if (type == "texture") + return MemBlockFlags::TEXTURE; + if (type == "alloc") + return MemBlockFlags::ALLOC; + if (type == "suballoc") + return MemBlockFlags::SUB_ALLOC; + if (type == "free") + return MemBlockFlags::FREE; + if (type == "subfree") + return MemBlockFlags::SUB_FREE; + return MemBlockFlags::SKIP_MEMCHECK; +} + +static std::string TypeFromFlag(const MemBlockFlags &flag) { + if (flag & MemBlockFlags::WRITE) + return "write"; + else if (flag & MemBlockFlags::TEXTURE) + return "texture"; + else if (flag & MemBlockFlags::ALLOC) + return "alloc"; + else if (flag & MemBlockFlags::SUB_ALLOC) + return "suballoc"; + return "error"; +} + +// Update memory info tagging (memory.info.set) +// +// Parameters: +// - address: number representing start address of the range to modify. +// - size: number, bytes from start address. +// - type: string, one of: +// - "write" for last modification information. +// - "texture" for last texture usage information. +// - "alloc" for allocation information. +// - "suballoc" for allocations within an existing allocation. +// - "free" to mark a previous allocation and its suballocations freed (ignores tag.) +// - "subfree" to mark a previous suballocation freed (ignores tag.) +// - tag: string label to give the memory. Optional if type if free or subfree. +// - pc: optional, number indicating PC address for this tag. +// +// Response (same event name) with no extra data. +// +// Note: Only one tag per type is maintained for any given memory address. +// Small extent info may be ignored unless detailed tracking enabled (see memory.info.config.) +void WebSocketMemoryInfoState::Set(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) + return req.Fail("CPU not started"); + + std::string type; + if (!req.ParamString("type", &type)) + return; + std::string tag; + if (type != "free" && type != "subfree") { + if (!req.ParamString("tag", &tag)) + return; + } + uint32_t addr; + if (!req.ParamU32("address", &addr)) + return; + uint32_t size; + if (!req.ParamU32("size", &size)) + return; + uint32_t pc = currentMIPS->pc; + if (!req.ParamU32("pc", &pc, false, DebuggerParamType::OPTIONAL)) + return; + + MemBlockFlags flags = MemBlockFlags::SKIP_MEMCHECK | FlagFromType(type); + if (flags == MemBlockFlags::SKIP_MEMCHECK) + return req.Fail("Invaid type - expecting write, texture, alloc, suballoc, free, or subfree"); + + if (!Memory::IsValidAddress(addr)) + return req.Fail("Invalid address"); + else if (!Memory::IsValidRange(addr, size)) + return req.Fail("Invalid size"); + + NotifyMemInfoPC(flags, addr, size, pc, tag.c_str(), tag.size()); + req.Respond(); +} + +// List memory info tags for address range (memory.info.list) +// +// Parameters: +// - address: number representing start address of the range. +// - size: number, bytes from start address. +// - type: optional string to limit information to one of: +// - "write" for last modification information. +// - "texture" for last texture usage information. +// - "alloc" for allocation information. +// - "suballoc" for allocations within an existing allocation. +// +// Response (same event name): +// - extents: array of objects: +// - type: one of the above type string values. +// - address: number (may be outside requested range if overlapping.) +// - size: number (may be outside requested range if overlapping.) +// - ticks: number indicating tick counter as of last tag. +// - pc: number address of last tag. +// - tag: string tag for this memory extent. +// - allocated: boolean, if this extent is marked as allocated (for alloc/suballoc types.) +void WebSocketMemoryInfoState::List(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) + return req.Fail("CPU not started"); + + std::string type; + if (!req.ParamString("type", &type, DebuggerParamType::OPTIONAL)) + return; + uint32_t addr; + if (!req.ParamU32("address", &addr)) + return; + uint32_t size; + if (!req.ParamU32("size", &size)) + return; + + // Allow type to be omitted. + MemBlockFlags flags = MemBlockFlags::SKIP_MEMCHECK | FlagFromType(type); + if (flags == MemBlockFlags::SKIP_MEMCHECK && req.HasParam("type")) + return req.Fail("Invaid type - expecting write, texture, alloc, suballoc, free, or subfree"); + + if (!Memory::IsValidAddress(addr)) + return req.Fail("Invalid address"); + else if (!Memory::IsValidRange(addr, size)) + return req.Fail("Invalid size"); + + std::vector results; + if (flags == MemBlockFlags::SKIP_MEMCHECK) + results = FindMemInfo(addr, size); + else + results = FindMemInfoByFlag(flags, addr, size); + + JsonWriter &json = req.Respond(); + json.pushArray("extents"); + for (const auto &result : results) { + json.pushDict(); + json.writeString("type", TypeFromFlag(result.flags)); + json.writeUint("address", result.start); + json.writeUint("size", result.size); + json.writeFloat("ticks", result.ticks); + json.writeUint("pc", result.pc); + json.writeString("tag", result.tag); + json.writeBool("allocated", result.allocated); + json.pop(); + } + json.pop(); +} + +// Search memory info tags for a string (memory.info.search) +// +// Parameters: +// - address: optional number representing start address of the range. +// - end: optional end address as a number (otherwise uses start address.) +// - match: string to search for within tag. +// - type: optional string to limit information to one of: +// - "write" for last modification information. +// - "texture" for last texture usage information. +// - "alloc" for allocation information. +// - "suballoc" for allocations within an existing allocation. +// +// Response (same event name): +// - extent: null, or matching object containing: +// - type: one of the above type string values. +// - address: number (may be outside requested range if overlapping.) +// - size: number (may be outside requested range if overlapping.) +// - ticks: number indicating tick counter as of last tag. +// - pc: number address of last tag. +// - tag: string tag for this memory extent. +// - allocated: boolean, if this extent is marked as allocated (for alloc/suballoc types.) +// +// Note: may not be fast. +void WebSocketMemoryInfoState::Search(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) + return req.Fail("CPU not started"); + + uint32_t start = 0; + if (!req.ParamU32("address", &start, false, DebuggerParamType::OPTIONAL)) + return; + uint32_t end = start; + if (!req.ParamU32("end", &end, false, DebuggerParamType::OPTIONAL)) + return; + std::string type; + if (!req.ParamString("type", &type, DebuggerParamType::OPTIONAL)) + return; + std::string match; + if (!req.ParamString("match", &match)) + return; + + // Allow type to be omitted. + MemBlockFlags flags = MemBlockFlags::SKIP_MEMCHECK | FlagFromType(type); + if (flags == MemBlockFlags::SKIP_MEMCHECK && req.HasParam("type")) + return req.Fail("Invaid type - expecting write, texture, alloc, suballoc, free, or subfree"); + + start = RoundMemAddressUp(start); + end = RoundMemAddressUp(end); + std::transform(match.begin(), match.end(), match.begin(), ::tolower); + + bool found = false; + MemBlockInfo foundResult; + + uint32_t addr = start; + constexpr uint32_t CHUNK_SIZE = 0x1000; + do { + uint32_t chunk_end = addr + CHUNK_SIZE; + if (addr < end && chunk_end >= end) { + chunk_end = end; + } + + std::vector results; + if (flags == MemBlockFlags::SKIP_MEMCHECK) + results = FindMemInfo(addr, chunk_end - addr); + else + results = FindMemInfoByFlag(flags, addr, chunk_end - addr); + + for (const auto &result : results) { + std::string lowercase = result.tag; + std::transform(lowercase.begin(), lowercase.end(), lowercase.begin(), ::tolower); + + if (lowercase.find(match) != lowercase.npos) { + found = true; + foundResult = result; + break; + } + } + addr = RoundMemAddressUp(chunk_end); + } while (!found && addr != end); + + JsonWriter &json = req.Respond(); + if (found) { + json.pushDict("extent"); + json.writeString("type", TypeFromFlag(foundResult.flags)); + json.writeUint("address", foundResult.start); + json.writeUint("size", foundResult.size); + json.writeFloat("ticks", foundResult.ticks); + json.writeUint("pc", foundResult.pc); + json.writeString("tag", foundResult.tag); + json.writeBool("allocated", foundResult.allocated); + json.pop(); + } else { + json.writeNull("extent"); + } +} diff --git a/Core/Debugger/WebSocket/MemoryInfoSubscriber.h b/Core/Debugger/WebSocket/MemoryInfoSubscriber.h new file mode 100644 index 0000000000..a093c5b693 --- /dev/null +++ b/Core/Debugger/WebSocket/MemoryInfoSubscriber.h @@ -0,0 +1,22 @@ +// Copyright (c) 2021- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "Core/Debugger/WebSocket/WebSocketUtils.h" + +DebuggerSubscriber *WebSocketMemoryInfoInit(DebuggerEventHandlerMap &map); diff --git a/Core/Debugger/WebSocket/WebSocketUtils.cpp b/Core/Debugger/WebSocket/WebSocketUtils.cpp index a9120b5caa..cc49e33336 100644 --- a/Core/Debugger/WebSocket/WebSocketUtils.cpp +++ b/Core/Debugger/WebSocket/WebSocketUtils.cpp @@ -21,6 +21,7 @@ #include "Common/Data/Text/Parsers.h" #include "Common/StringUtils.h" #include "Core/Debugger/WebSocket/WebSocketUtils.h" +#include "Core/MemMap.h" inline void DebuggerJsonAddTicket(JsonWriter &writer, const JsonGet &data) { const JsonNode *value = data.get("ticket"); @@ -273,3 +274,15 @@ bool DebuggerRequest::ParamString(const char *name, std::string *out, DebuggerPa Fail(StringFromFormat("Invalid '%s' parameter type", name)); return false; } + +uint32_t RoundMemAddressUp(uint32_t addr) { + if (addr < PSP_GetScratchpadMemoryBase()) + return PSP_GetScratchpadMemoryBase(); + else if (addr >= PSP_GetScratchpadMemoryEnd() && addr < PSP_GetVidMemBase()) + return PSP_GetVidMemBase(); + else if (addr >= PSP_GetVidMemEnd() && addr < PSP_GetKernelMemoryBase()) + return PSP_GetKernelMemoryBase(); + else if (addr >= PSP_GetUserMemoryEnd()) + return PSP_GetScratchpadMemoryBase(); + return addr; +} diff --git a/Core/Debugger/WebSocket/WebSocketUtils.h b/Core/Debugger/WebSocket/WebSocketUtils.h index 5caa28adaf..38edc24a68 100644 --- a/Core/Debugger/WebSocket/WebSocketUtils.h +++ b/Core/Debugger/WebSocket/WebSocketUtils.h @@ -109,3 +109,5 @@ public: typedef std::function DebuggerEventHandler; typedef std::unordered_map DebuggerEventHandlerMap; + +uint32_t RoundMemAddressUp(uint32_t addr); diff --git a/Core/Dialog/PSPDialog.cpp b/Core/Dialog/PSPDialog.cpp index f15ea6da63..09257da935 100644 --- a/Core/Dialog/PSPDialog.cpp +++ b/Core/Dialog/PSPDialog.cpp @@ -75,6 +75,7 @@ void PSPDialog::ChangeStatus(DialogStatus newStatus, int delayUs) { } } status = newStatus; + pendingStatus = newStatus; pendingStatusTicks = 0; } else { pendingStatus = newStatus; @@ -93,6 +94,15 @@ void PSPDialog::FinishVolatile() { } } +int PSPDialog::FinishInit() { + if (ReadStatus() != SCE_UTILITY_STATUS_INITIALIZE) + return -1; + // The thread already locked. + volatileLocked_ = true; + ChangeStatus(SCE_UTILITY_STATUS_RUNNING, 0); + return 0; +} + int PSPDialog::FinishShutdown() { if (ReadStatus() != SCE_UTILITY_STATUS_SHUTDOWN) return -1; @@ -101,12 +111,18 @@ int PSPDialog::FinishShutdown() { } void PSPDialog::ChangeStatusInit(int delayUs) { - status = SCE_UTILITY_STATUS_INITIALIZE; - ChangeStatus(SCE_UTILITY_STATUS_RUNNING, delayUs); + ChangeStatus(SCE_UTILITY_STATUS_INITIALIZE, 0); + + auto params = GetCommonParam(); + if (params) + UtilityDialogInitialize(DialogType(), delayUs, params->accessThread); + else + ChangeStatus(SCE_UTILITY_STATUS_RUNNING, delayUs); } void PSPDialog::ChangeStatusShutdown(int delayUs) { - status = SCE_UTILITY_STATUS_SHUTDOWN; + ChangeStatus(SCE_UTILITY_STATUS_SHUTDOWN, 0); + auto params = GetCommonParam(); if (params) UtilityDialogShutdown(DialogType(), delayUs, params->accessThread); diff --git a/Core/Dialog/PSPDialog.h b/Core/Dialog/PSPDialog.h index e340314d18..54c95e217b 100644 --- a/Core/Dialog/PSPDialog.h +++ b/Core/Dialog/PSPDialog.h @@ -86,6 +86,7 @@ public: void EndDraw(); void FinishVolatile(); + int FinishInit(); int FinishShutdown(); protected: diff --git a/Core/Dialog/PSPSaveDialog.cpp b/Core/Dialog/PSPSaveDialog.cpp index a42d4389f5..b54a8ddd62 100755 --- a/Core/Dialog/PSPSaveDialog.cpp +++ b/Core/Dialog/PSPSaveDialog.cpp @@ -26,6 +26,7 @@ #include #include "Common/Data/Text/I18n.h" +#include "Common/Data/Encoding/Utf8.h" #include "Common/Thread/ThreadUtil.h" #include "Common/File/FileUtil.h" @@ -61,6 +62,7 @@ PSPSaveDialog::PSPSaveDialog(UtilityDialogType type) : PSPDialog(type) { } PSPSaveDialog::~PSPSaveDialog() { + JoinIOThread(); } int PSPSaveDialog::Init(int paramAddr) @@ -541,7 +543,8 @@ void PSPSaveDialog::DisplaySaveDataInfo2(bool showNewData) { s64 sizeK = data_size / 1024; PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_LEFT, 0.5f); - std::string saveinfoTxt = StringFromFormat("%.128s\n%s %s\n%lld KB", save_title, date_year, hour_time, sizeK); + std::string title = SanitizeUTF8(std::string(save_title, strnlen(save_title, 128))); + std::string saveinfoTxt = StringFromFormat("%s\n%s %s\n%lld KB", title.c_str(), date_year, hour_time, sizeK); PPGeDrawText(saveinfoTxt.c_str(), 8, 200, textStyle); } @@ -1159,7 +1162,7 @@ void PSPSaveDialog::JoinIOThread() { } static void DoExecuteIOAction(PSPSaveDialog *dialog) { - setCurrentThreadName("SaveIO"); + SetCurrentThreadName("SaveIO"); dialog->ExecuteIOAction(); } diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index 137d272e20..3da8a05947 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -25,6 +25,7 @@ #include "Core/Host.h" #include "Core/Reporting.h" #include "Core/System.h" +#include "Core/Debugger/MemBlockInfo.h" #include "Core/Dialog/SavedataParam.h" #include "Core/Dialog/PSPSaveDialog.h" #include "Core/FileSystems/MetaFileSystem.h" @@ -417,8 +418,7 @@ int SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &saveD u8* cryptedData = 0; int cryptedSize = 0; - u8 cryptedHash[0x10]; - memset(cryptedHash,0,0x10); + u8 cryptedHash[0x10]{}; // Encrypt save. // TODO: Is this the correct difference between MAKEDATA and MAKEDATASECURE? if (param->dataBuf.IsValid() && g_Config.bEncryptSave && secureMode) @@ -467,35 +467,35 @@ int SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &saveD sfoFile.SetValue("CATEGORY","MS",4); sfoFile.SetValue("SAVEDATA_DIRECTORY", GetSaveDir(param, saveDirName), 64); + // Always write and update the file list. // For each file, 13 bytes for filename, 16 bytes for file hash (0 in PPSSPP), 3 byte for padding - if (secureMode) { - const int FILE_LIST_COUNT_MAX = 99; - const u32 FILE_LIST_TOTAL_SIZE = sizeof(SaveSFOFileListEntry) * FILE_LIST_COUNT_MAX; - u32 tmpDataSize = 0; - SaveSFOFileListEntry *tmpDataOrig = (SaveSFOFileListEntry *)sfoFile.GetValueData("SAVEDATA_FILE_LIST", &tmpDataSize); - SaveSFOFileListEntry *updatedList = new SaveSFOFileListEntry[FILE_LIST_COUNT_MAX]; - if (tmpDataSize != 0) - memcpy(updatedList, tmpDataOrig, std::min(tmpDataSize, FILE_LIST_TOTAL_SIZE)); - if (tmpDataSize < FILE_LIST_TOTAL_SIZE) - memset(updatedList + tmpDataSize, 0, FILE_LIST_TOTAL_SIZE - tmpDataSize); - - if (param->dataBuf.IsValid()) { - const std::string saveFilename = GetFileName(param); - for (auto entry = updatedList; entry < updatedList + FILE_LIST_COUNT_MAX; ++entry) { - if (entry->filename[0] != '\0') { - if (strncmp(entry->filename, saveFilename.c_str(), sizeof(entry->filename)) != 0) - continue; - } - - snprintf(entry->filename, sizeof(entry->filename), "%s", saveFilename.c_str()); - memcpy(entry->hash, cryptedHash, 16); - break; + const int FILE_LIST_COUNT_MAX = 99; + const u32 FILE_LIST_TOTAL_SIZE = sizeof(SaveSFOFileListEntry) * FILE_LIST_COUNT_MAX; + u32 tmpDataSize = 0; + SaveSFOFileListEntry *tmpDataOrig = (SaveSFOFileListEntry *)sfoFile.GetValueData("SAVEDATA_FILE_LIST", &tmpDataSize); + SaveSFOFileListEntry *updatedList = new SaveSFOFileListEntry[FILE_LIST_COUNT_MAX]; + if (tmpDataSize != 0) + memcpy(updatedList, tmpDataOrig, std::min(tmpDataSize, FILE_LIST_TOTAL_SIZE)); + if (tmpDataSize < FILE_LIST_TOTAL_SIZE) + memset(updatedList + tmpDataSize, 0, FILE_LIST_TOTAL_SIZE - tmpDataSize); + // Leave a hash there and unchanged if it was already there. + if (secureMode && param->dataBuf.IsValid()) { + const std::string saveFilename = GetFileName(param); + for (auto entry = updatedList; entry < updatedList + FILE_LIST_COUNT_MAX; ++entry) { + if (entry->filename[0] != '\0') { + if (strncmp(entry->filename, saveFilename.c_str(), sizeof(entry->filename)) != 0) + continue; } + + snprintf(entry->filename, sizeof(entry->filename), "%s", saveFilename.c_str()); + memcpy(entry->hash, cryptedHash, 16); + break; } - sfoFile.SetValue("SAVEDATA_FILE_LIST", (u8 *)updatedList, FILE_LIST_TOTAL_SIZE, (int)FILE_LIST_TOTAL_SIZE); - delete[] updatedList; } + sfoFile.SetValue("SAVEDATA_FILE_LIST", (u8 *)updatedList, FILE_LIST_TOTAL_SIZE, (int)FILE_LIST_TOTAL_SIZE); + delete[] updatedList; + // Init param with 0. This will be used to detect crypted save or not on loading u8 *tmpData = new u8[128]; memset(tmpData, 0, 128); @@ -645,7 +645,7 @@ int SavedataParam::LoadSaveData(SceUtilitySavedataParam *param, const std::strin } WARN_LOG_REPORT(SCEUTILITY, "Savedata version requested: %d", param->secureVersion); } - u8 *data_ = param->dataBuf; + std::string filename = GetFileName(param); std::string filePath = dirPath + "/" + filename; s64 readSize; @@ -664,21 +664,27 @@ int SavedataParam::LoadSaveData(SceUtilitySavedataParam *param, const std::strin int prevCryptMode = GetSaveCryptMode(param, saveDirName); bool isCrypted = prevCryptMode != 0 && secureMode; bool saveDone = false; + u32 loadedSize = 0; if (isCrypted) { if (DetermineCryptMode(param) > 1 && !HasKey(param)) return SCE_UTILITY_SAVEDATA_ERROR_LOAD_PARAM; u8 hash[16]; bool hasExpectedHash = GetExpectedHash(dirPath, filename, hash); - LoadCryptedSave(param, data_, saveData, saveSize, prevCryptMode, hasExpectedHash ? hash : nullptr, saveDone); + loadedSize = LoadCryptedSave(param, param->dataBuf, saveData, saveSize, prevCryptMode, hasExpectedHash ? hash : nullptr, saveDone); // TODO: Should return SCE_UTILITY_SAVEDATA_ERROR_LOAD_DATA_BROKEN here if !saveDone. } if (!saveDone) { - LoadNotCryptedSave(param, data_, saveData, saveSize); + loadedSize = LoadNotCryptedSave(param, param->dataBuf, saveData, saveSize); } param->dataSize = (SceSize)saveSize; delete[] saveData; + if (loadedSize != 0) { + std::string tag = "LoadSaveData/" + filePath; + NotifyMemInfo(MemBlockFlags::WRITE, param->dataBuf.ptr, loadedSize, tag.c_str(), tag.size()); + } + return 0; } @@ -697,7 +703,7 @@ int SavedataParam::DetermineCryptMode(const SceUtilitySavedataParam *param) cons return decryptMode; } -void SavedataParam::LoadCryptedSave(SceUtilitySavedataParam *param, u8 *data, const u8 *saveData, int &saveSize, int prevCryptMode, const u8 *expectedHash, bool &saveDone) { +u32 SavedataParam::LoadCryptedSave(SceUtilitySavedataParam *param, u8 *data, const u8 *saveData, int &saveSize, int prevCryptMode, const u8 *expectedHash, bool &saveDone) { int orig_size = saveSize; int align_len = align16(saveSize); u8 *data_base = new u8[align_len]; @@ -761,18 +767,27 @@ void SavedataParam::LoadCryptedSave(SceUtilitySavedataParam *param, u8 *data, co err = DecryptSave(decryptMode, data_base, &saveSize, &align_len, hasKey ? cryptKey : nullptr, nullptr); } + u32 sz = 0; if (err == 0) { - if (param->dataBuf.IsValid()) - memcpy(data, data_base, std::min((u32)saveSize, (u32)param->dataBufSize)); + if (param->dataBuf.IsValid()) { + sz = std::min((u32)saveSize, (u32)param->dataBufSize); + memcpy(data, data_base, sz); + } saveDone = true; } delete[] data_base; delete[] cryptKey; + + return sz; } -void SavedataParam::LoadNotCryptedSave(SceUtilitySavedataParam *param, u8 *data, u8 *saveData, int &saveSize) { - if (param->dataBuf.IsValid()) - memcpy(data, saveData, std::min((u32)saveSize, (u32)param->dataBufSize)); +u32 SavedataParam::LoadNotCryptedSave(SceUtilitySavedataParam *param, u8 *data, u8 *saveData, int &saveSize) { + if (param->dataBuf.IsValid()) { + u32 sz = std::min((u32)saveSize, (u32)param->dataBufSize); + memcpy(data, saveData, sz); + return sz; + } + return 0; } void SavedataParam::LoadSFO(SceUtilitySavedataParam *param, const std::string& dirPath) { @@ -850,8 +865,11 @@ void SavedataParam::LoadFile(const std::string& dirPath, const std::string& file u8 *buf = fileData->buf; u32 size = Memory::ValidSize(fileData->buf.ptr, fileData->bufSize); s64 readSize = -1; - if (ReadPSPFile(filePath, &buf, size, &readSize)) + if (ReadPSPFile(filePath, &buf, size, &readSize)) { fileData->size = readSize; + const std::string tag = "SavedataLoad/" + filePath; + NotifyMemInfo(MemBlockFlags::WRITE, fileData->buf.ptr, fileData->size, tag.c_str(), tag.size()); + } } int SavedataParam::EncryptData(unsigned int mode, @@ -1067,6 +1085,7 @@ int SavedataParam::GetSizes(SceUtilitySavedataParam *param) const std::string spaceTxt = SavedataParam::GetSpaceText(freeBytes, false); memset(param->msFree->freeSpaceStr, 0, sizeof(param->msFree->freeSpaceStr)); strncpy(param->msFree->freeSpaceStr, spaceTxt.c_str(), sizeof(param->msFree->freeSpaceStr)); + NotifyMemInfo(MemBlockFlags::WRITE, param->msFree.ptr, sizeof(SceUtilitySavedataMsFreeInfo), "SavedataGetSizes"); } if (param->msData.IsValid()) { @@ -1104,6 +1123,7 @@ int SavedataParam::GetSizes(SceUtilitySavedataParam *param) strncpy(param->msData->info.usedSpace32Str, "", sizeof(param->msData->info.usedSpace32Str)); ret = SCE_UTILITY_SAVEDATA_ERROR_SIZES_NO_DATA; } + NotifyMemInfo(MemBlockFlags::WRITE, param->msData.ptr, sizeof(SceUtilitySavedataMsDataInfo), "SavedataGetSizes"); } if (param->utilityData.IsValid()) { @@ -1139,6 +1159,7 @@ int SavedataParam::GetSizes(SceUtilitySavedataParam *param) spaceTxt = SavedataParam::GetSpaceText(total_size, true); memset(param->utilityData->usedSpace32Str, 0, sizeof(param->utilityData->usedSpace32Str)); strncpy(param->utilityData->usedSpace32Str, spaceTxt.c_str(), sizeof(param->utilityData->usedSpace32Str)); + NotifyMemInfo(MemBlockFlags::WRITE, param->utilityData.ptr, sizeof(SceUtilitySavedataUsedDataInfo), "SavedataGetSizes"); } return ret; @@ -1158,47 +1179,47 @@ bool SavedataParam::GetList(SceUtilitySavedataParam *param) std::vector sfoFiles; std::vector allDir = pspFileSystem.GetDirListing(savePath); - if (param->idList.IsValid()) + std::string searchString = GetGameName(param)+GetSaveName(param); + for (size_t i = 0; i < allDir.size() && validDir.size() < maxFile; i++) { - std::string searchString = GetGameName(param)+GetSaveName(param); - for (size_t i = 0; i < allDir.size() && validDir.size() < maxFile; i++) + std::string dirName = allDir[i].name; + if(PSPMatch(dirName, searchString)) { - std::string dirName = allDir[i].name; - if(PSPMatch(dirName, searchString)) - { - validDir.push_back(allDir[i]); - } + validDir.push_back(allDir[i]); } + } - PSPFileInfo sfoFile; - for (size_t i = 0; i < validDir.size(); ++i) { - // GetFileName(param) == NUll here - // so use sfo files to set the date. - sfoFile = pspFileSystem.GetFileInfo(savePath + validDir[i].name + "/" + "PARAM.SFO"); - sfoFiles.push_back(sfoFile); - } + PSPFileInfo sfoFile; + for (size_t i = 0; i < validDir.size(); ++i) { + // GetFileName(param) == NUll here + // so use sfo files to set the date. + sfoFile = pspFileSystem.GetFileInfo(savePath + validDir[i].name + "/" + "PARAM.SFO"); + sfoFiles.push_back(sfoFile); + } - SceUtilitySavedataIdListEntry *entries = param->idList->entries; - for (u32 i = 0; i < (u32)validDir.size(); i++) - { - entries[i].st_mode = 0x11FF; - if (sfoFiles[i].exists) { - __IoCopyDate(entries[i].st_ctime, sfoFiles[i].ctime); - __IoCopyDate(entries[i].st_atime, sfoFiles[i].atime); - __IoCopyDate(entries[i].st_mtime, sfoFiles[i].mtime); - } else { - __IoCopyDate(entries[i].st_ctime, validDir[i].ctime); - __IoCopyDate(entries[i].st_atime, validDir[i].atime); - __IoCopyDate(entries[i].st_mtime, validDir[i].mtime); - } - // folder name without gamename (max 20 u8) - std::string outName = validDir[i].name.substr(GetGameName(param).size()); - memset(entries[i].name, 0, sizeof(entries[i].name)); - strncpy(entries[i].name, outName.c_str(), sizeof(entries[i].name)); + SceUtilitySavedataIdListEntry *entries = param->idList->entries; + for (u32 i = 0; i < (u32)validDir.size(); i++) + { + entries[i].st_mode = 0x11FF; + if (sfoFiles[i].exists) { + __IoCopyDate(entries[i].st_ctime, sfoFiles[i].ctime); + __IoCopyDate(entries[i].st_atime, sfoFiles[i].atime); + __IoCopyDate(entries[i].st_mtime, sfoFiles[i].mtime); + } else { + __IoCopyDate(entries[i].st_ctime, validDir[i].ctime); + __IoCopyDate(entries[i].st_atime, validDir[i].atime); + __IoCopyDate(entries[i].st_mtime, validDir[i].mtime); } + // folder name without gamename (max 20 u8) + std::string outName = validDir[i].name.substr(GetGameName(param).size()); + memset(entries[i].name, 0, sizeof(entries[i].name)); + strncpy(entries[i].name, outName.c_str(), sizeof(entries[i].name)); } // Save num of folder found param->idList->resultCount = (u32)validDir.size(); + + NotifyMemInfo(MemBlockFlags::WRITE, param->idList.ptr, sizeof(SceUtilitySavedataIdListInfo), "SavedataGetList"); + NotifyMemInfo(MemBlockFlags::WRITE, param->idList->entries.ptr, (uint32_t)validDir.size() * sizeof(SceUtilitySavedataIdListEntry), "SavedataGetList"); } return true; } @@ -1307,6 +1328,14 @@ int SavedataParam::GetFilesList(SceUtilitySavedataParam *param) // Don't know what it is, but PSP always respond this param->bind = 1021; + NotifyMemInfo(MemBlockFlags::WRITE, fileList.ptr, sizeof(SceUtilitySavedataFileListInfo), "SavedataGetFilesList"); + if (fileList->resultNumSystemEntries != 0) + NotifyMemInfo(MemBlockFlags::WRITE, fileList->systemEntries.ptr, fileList->resultNumSystemEntries * sizeof(SceUtilitySavedataFileListEntry), "SavedataGetFilesList"); + if (fileList->resultNumSecureEntries != 0) + NotifyMemInfo(MemBlockFlags::WRITE, fileList->secureEntries.ptr, fileList->resultNumSecureEntries * sizeof(SceUtilitySavedataFileListEntry), "SavedataGetFilesList"); + if (fileList->resultNumNormalEntries != 0) + NotifyMemInfo(MemBlockFlags::WRITE, fileList->normalEntries.ptr, fileList->resultNumNormalEntries * sizeof(SceUtilitySavedataFileListEntry), "SavedataGetFilesList"); + return 0; } @@ -1373,6 +1402,8 @@ bool SavedataParam::GetSize(SceUtilitySavedataParam *param) truncate_cpy(param->sizeInfo->overwriteString, spaceTxt.c_str()); } } + + NotifyMemInfo(MemBlockFlags::WRITE, param->sizeInfo.ptr, sizeof(PspUtilitySavedataSizeInfo), "SavedataGetSize"); } return exists; diff --git a/Core/Dialog/SavedataParam.h b/Core/Dialog/SavedataParam.h index 3cf343914b..7011d45e37 100644 --- a/Core/Dialog/SavedataParam.h +++ b/Core/Dialog/SavedataParam.h @@ -364,8 +364,8 @@ private: PSPFileInfo GetSaveInfo(std::string saveDir); int LoadSaveData(SceUtilitySavedataParam *param, const std::string &saveDirName, const std::string& dirPath, bool secureMode); - void LoadCryptedSave(SceUtilitySavedataParam *param, u8 *data, const u8 *saveData, int &saveSize, int prevCryptMode, const u8 *expectedHash, bool &saveDone); - void LoadNotCryptedSave(SceUtilitySavedataParam *param, u8 *data, u8 *saveData, int &saveSize); + u32 LoadCryptedSave(SceUtilitySavedataParam *param, u8 *data, const u8 *saveData, int &saveSize, int prevCryptMode, const u8 *expectedHash, bool &saveDone); + u32 LoadNotCryptedSave(SceUtilitySavedataParam *param, u8 *data, u8 *saveData, int &saveSize); void LoadSFO(SceUtilitySavedataParam *param, const std::string& dirPath); void LoadFile(const std::string& dirPath, const std::string& filename, PspUtilitySavedataFileData *fileData); diff --git a/Core/ELF/ElfReader.cpp b/Core/ELF/ElfReader.cpp index 69328d49e2..82b0d6116d 100644 --- a/Core/ELF/ElfReader.cpp +++ b/Core/ELF/ElfReader.cpp @@ -15,8 +15,10 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "Common/StringUtils.h" #include "Core/MemMap.h" #include "Core/Reporting.h" +#include "Core/ThreadPools.h" #include "Core/MIPS/MIPSTables.h" #include "Core/ELF/ElfReader.h" #include "Core/Debugger/MemBlockInfo.h" @@ -53,90 +55,106 @@ void addrToHiLo(u32 addr, u16 &hi, s16 &lo) } } -bool ElfReader::LoadRelocations(const Elf32_Rel *rels, int numRelocs) -{ - int numErrors = 0; +bool ElfReader::LoadRelocations(const Elf32_Rel *rels, int numRelocs) { + std::vector relocOps; + relocOps.resize(numRelocs); + DEBUG_LOG(LOADER, "Loading %i relocations...", numRelocs); - for (int r = 0; r < numRelocs; r++) - { - // INFO_LOG(LOADER, "Loading reloc %i (%p)...", r, rels + r); - u32 info = rels[r].r_info; - u32 addr = rels[r].r_offset; - int type = info & 0xf; + int numErrors = 0; + GlobalThreadPool::Loop([&](int l, int h) { + for (int r = l; r < h; r++) { + u32 info = rels[r].r_info; + u32 addr = rels[r].r_offset; - int readwrite = (info>>8) & 0xff; - int relative = (info>>16) & 0xff; + int type = info & 0xf; - //0 = code - //1 = data - - if (readwrite >= (int)ARRAY_SIZE(segmentVAddr)) { - if (numErrors < 10) { - ERROR_LOG_REPORT(LOADER, "Bad segment number %i", readwrite); + // Often: 0 = code, 1 = data. + int readwrite = (info >> 8) & 0xff; + if (readwrite >= (int)ARRAY_SIZE(segmentVAddr)) { + if (numErrors < 10) { + ERROR_LOG_REPORT(LOADER, "Bad segment number %i", readwrite); + } + numErrors++; + continue; } - numErrors++; - continue; - } - addr += segmentVAddr[readwrite]; + addr += segmentVAddr[readwrite]; - // It appears that misaligned relocations are allowed. - // Will they work correctly on big-endian? - - if (((addr & 3) && type != R_MIPS_32) || !Memory::IsValidAddress(addr)) { - if (numErrors < 10) { - WARN_LOG_REPORT(LOADER, "Suspicious address %08x, skipping reloc, type = %d", addr, type); - } else if (numErrors == 10) { - WARN_LOG(LOADER, "Too many bad relocations, skipping logging"); + // It appears that misaligned relocations are allowed. + if (((addr & 3) && type != R_MIPS_32) || !Memory::IsValidAddress(addr)) { + if (numErrors < 10) { + WARN_LOG_REPORT(LOADER, "Suspicious address %08x, skipping reloc, type = %d", addr, type); + } else if (numErrors == 10) { + WARN_LOG(LOADER, "Too many bad relocations, skipping logging"); + } + numErrors++; + continue; } - numErrors++; - continue; + + relocOps[r] = Memory::ReadUnchecked_Instruction(addr, true).encoding; } + }, 0, numRelocs, 128); - u32 op = Memory::Read_Instruction(addr, true).encoding; + GlobalThreadPool::Loop([&](int l, int h) { + for (int r = l; r < h; r++) { + VERBOSE_LOG(LOADER, "Loading reloc %i (%p)...", r, rels + r); + u32 info = rels[r].r_info; + u32 addr = rels[r].r_offset; - const bool log = false; - //log=true; - if (log) { - DEBUG_LOG(LOADER,"rel at: %08x info: %08x type: %i",addr, info, type); - } - u32 relocateTo = segmentVAddr[relative]; + int type = info & 0xf; + int readwrite = (info >> 8) & 0xff; + int relative = (info >> 16) & 0xff; - switch (type) - { - case R_MIPS_32: - if (log) - DEBUG_LOG(LOADER,"Full address reloc %08x", addr); - //full address, no problemo - op += relocateTo; - break; + if (readwrite >= (int)ARRAY_SIZE(segmentVAddr)) { + continue; + } - case R_MIPS_26: //j, jal - //add on to put in correct address space - if (log) - DEBUG_LOG(LOADER,"j/jal reloc %08x", addr); - op = (op & 0xFC000000) | (((op&0x03FFFFFF)+(relocateTo>>2))&0x03FFFFFF); - break; + addr += segmentVAddr[readwrite]; + if (((addr & 3) && type != R_MIPS_32) || !Memory::IsValidAddress(addr)) { + continue; + } - case R_MIPS_HI16: //lui part of lui-addiu pairs + u32 op = relocOps[r]; + + const bool log = false; + //log=true; + if (log) { + DEBUG_LOG(LOADER, "rel at: %08x info: %08x type: %i", addr, info, type); + } + u32 relocateTo = segmentVAddr[relative]; + + switch (type) { + case R_MIPS_32: + if (log) + DEBUG_LOG(LOADER, "Full address reloc %08x", addr); + //full address, no problemo + op += relocateTo; + break; + + case R_MIPS_26: //j, jal + //add on to put in correct address space + if (log) + DEBUG_LOG(LOADER, "j/jal reloc %08x", addr); + op = (op & 0xFC000000) | (((op & 0x03FFFFFF) + (relocateTo >> 2)) & 0x03FFFFFF); + break; + + case R_MIPS_HI16: //lui part of lui-addiu pairs { if (log) - DEBUG_LOG(LOADER,"HI reloc %08x", addr); + DEBUG_LOG(LOADER, "HI reloc %08x", addr); u32 cur = (op & 0xFFFF) << 16; u16 hi = 0; bool found = false; - for (int t = r + 1; tp_memsz); } } @@ -497,6 +519,8 @@ int ElfReader::LoadInto(u32 loadAddress, bool fromTop) if (s->sh_flags & SHF_ALLOC) { + std::string tag = name && name[0] ? StringFromFormat("ELF/%s", name) : StringFromFormat("ELF/%08x", writeAddr); + NotifyMemInfo(MemBlockFlags::SUB_ALLOC, writeAddr, s->sh_size, tag.c_str(), tag.size()); DEBUG_LOG(LOADER,"Data Section found: %s Sitting at %08x, size %08x", name, writeAddr, (u32)s->sh_size); } else diff --git a/Core/ELF/PBPReader.cpp b/Core/ELF/PBPReader.cpp index de9c4452bd..6bf9157813 100644 --- a/Core/ELF/PBPReader.cpp +++ b/Core/ELF/PBPReader.cpp @@ -15,7 +15,6 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include #include #include @@ -26,21 +25,21 @@ PBPReader::PBPReader(FileLoader *fileLoader) : file_(nullptr), header_(), isELF_(false) { if (!fileLoader->Exists()) { - ERROR_LOG(LOADER, "Failed to open PBP file %s", fileLoader->Path().c_str()); + ERROR_LOG(LOADER, "Failed to open PBP file %s", fileLoader->GetPath().c_str()); return; } fileSize_ = (size_t)fileLoader->FileSize(); if (fileLoader->ReadAt(0, sizeof(header_), (u8 *)&header_) != sizeof(header_)) { - ERROR_LOG(LOADER, "PBP is too small to be valid: %s", fileLoader->Path().c_str()); + ERROR_LOG(LOADER, "PBP is too small to be valid: %s", fileLoader->GetPath().c_str()); return; } if (memcmp(header_.magic, "\0PBP", 4) != 0) { if (memcmp(header_.magic, "\nFLE", 4) != 0) { - VERBOSE_LOG(LOADER, "%s: File actually an ELF, not a PBP", fileLoader->Path().c_str()); + VERBOSE_LOG(LOADER, "%s: File actually an ELF, not a PBP", fileLoader->GetPath().c_str()); isELF_ = true; } else { - ERROR_LOG(LOADER, "Magic number in %s indicated no PBP: %s", fileLoader->Path().c_str(), header_.magic); + ERROR_LOG(LOADER, "Magic number in %s indicated no PBP: %s", fileLoader->GetPath().c_str(), header_.magic); } return; } diff --git a/Core/ELF/ParamSFO.cpp b/Core/ELF/ParamSFO.cpp index 5b966f1bfb..af5a0ce812 100644 --- a/Core/ELF/ParamSFO.cpp +++ b/Core/ELF/ParamSFO.cpp @@ -271,7 +271,7 @@ void ParamSFOData::ValueData::SetData(const u8* data, int size) { std::string ParamSFOData::GenerateFakeID(std::string filename) { // Generates fake gameID for homebrew based on it's folder name. // Should probably not be a part of ParamSFO, but it'll be called in same places. - std::string file = PSP_CoreParameter().fileToStart; + std::string file = PSP_CoreParameter().fileToStart.ToString(); if (filename != "") file = filename; diff --git a/Core/FileLoaders/CachingFileLoader.cpp b/Core/FileLoaders/CachingFileLoader.cpp index c4029d01e5..05fa5eefba 100644 --- a/Core/FileLoaders/CachingFileLoader.cpp +++ b/Core/FileLoaders/CachingFileLoader.cpp @@ -267,7 +267,7 @@ void CachingFileLoader::StartReadAhead(s64 pos) { if (aheadThread_.joinable()) aheadThread_.join(); aheadThread_ = std::thread([this, pos] { - setCurrentThreadName("FileLoaderReadAhead"); + SetCurrentThreadName("FileLoaderReadAhead"); std::unique_lock guard(blocksMutex_); s64 cacheStartPos = pos >> BLOCK_SHIFT; diff --git a/Core/FileLoaders/DiskCachingFileLoader.cpp b/Core/FileLoaders/DiskCachingFileLoader.cpp index 93b2bebd38..275ce11cb6 100644 --- a/Core/FileLoaders/DiskCachingFileLoader.cpp +++ b/Core/FileLoaders/DiskCachingFileLoader.cpp @@ -27,6 +27,7 @@ #include "Common/File/DiskFree.h" #include "Common/File/DirListing.h" #include "Common/File/FileUtil.h" +#include "Common/File/Path.h" #include "Common/Log.h" #include "Common/CommonWindows.h" #include "Core/FileLoaders/DiskCachingFileLoader.h" @@ -42,9 +43,9 @@ static const s64 SAFETY_FREE_DISK_SPACE = 768 * 1024 * 1024; // 768 MB // Aim to allow this many files cached at once. static const u32 CACHE_SPACE_FLEX = 4; -std::string DiskCachingFileLoaderCache::cacheDir_; +Path DiskCachingFileLoaderCache::cacheDir_; -std::map DiskCachingFileLoader::caches_; +std::map DiskCachingFileLoader::caches_; std::mutex DiskCachingFileLoader::cachesMutex_; // Takes ownership of backend. @@ -117,11 +118,11 @@ size_t DiskCachingFileLoader::ReadAt(s64 absolutePos, size_t bytes, void *data, return readSize; } -std::vector DiskCachingFileLoader::GetCachedPathsInUse() { +std::vector DiskCachingFileLoader::GetCachedPathsInUse() { std::lock_guard guard(cachesMutex_); // This is on the file loader so that it can manage the caches_. - std::vector files; + std::vector files; for (auto it : caches_) { files.push_back(it.first); @@ -133,7 +134,7 @@ std::vector DiskCachingFileLoader::GetCachedPathsInUse() { void DiskCachingFileLoader::InitCache() { std::lock_guard guard(cachesMutex_); - std::string path = ProxiedFileLoader::Path(); + Path path = ProxiedFileLoader::GetPath(); auto &entry = caches_[path]; if (!entry) { entry = new DiskCachingFileLoaderCache(path, filesize_); @@ -149,12 +150,12 @@ void DiskCachingFileLoader::ShutdownCache() { if (cache_->Release()) { // If it ran out of counts, delete it. delete cache_; - caches_.erase(ProxiedFileLoader::Path()); + caches_.erase(ProxiedFileLoader::GetPath()); } cache_ = nullptr; } -DiskCachingFileLoaderCache::DiskCachingFileLoaderCache(const std::string &path, u64 filesize) +DiskCachingFileLoaderCache::DiskCachingFileLoaderCache(const Path &path, u64 filesize) : filesize_(filesize), origPath_(path) { InitCache(path); } @@ -163,7 +164,7 @@ DiskCachingFileLoaderCache::~DiskCachingFileLoaderCache() { ShutdownCache(); } -void DiskCachingFileLoaderCache::InitCache(const std::string &path) { +void DiskCachingFileLoaderCache::InitCache(const Path &filename) { cacheSize_ = 0; indexCount_ = 0; oldestGeneration_ = 0; @@ -171,7 +172,7 @@ void DiskCachingFileLoaderCache::InitCache(const std::string &path) { flags_ = 0; generation_ = 0; - const std::string cacheFilePath = MakeCacheFilePath(path); + const Path cacheFilePath = MakeCacheFilePath(filename); bool fileLoaded = LoadCacheFile(cacheFilePath); // We do some basic locking to protect against two things: crashes and concurrency. @@ -408,21 +409,20 @@ u32 DiskCachingFileLoaderCache::AllocateBlock(u32 indexPos) { return INVALID_BLOCK; } -std::string DiskCachingFileLoaderCache::MakeCacheFilename(const std::string &path) { +std::string DiskCachingFileLoaderCache::MakeCacheFilename(const Path &path) { static const char *const invalidChars = "?*:/\\^|<>\"'"; - std::string filename = path; + std::string filename = path.ToString(); for (size_t i = 0; i < filename.size(); ++i) { int c = filename[i]; if (strchr(invalidChars, c) != nullptr) { filename[i] = '_'; } } - return filename + ".ppdc"; } -std::string DiskCachingFileLoaderCache::MakeCacheFilePath(const std::string &path) { - std::string dir = cacheDir_; +::Path DiskCachingFileLoaderCache::MakeCacheFilePath(const Path &filename) { + Path dir = cacheDir_; if (dir.empty()) { dir = GetSysDirectory(DIRECTORY_CACHE); } @@ -431,7 +431,7 @@ std::string DiskCachingFileLoaderCache::MakeCacheFilePath(const std::string &pat File::CreateFullPath(dir); } - return dir + "/" + MakeCacheFilename(path); + return dir / MakeCacheFilename(filename); } s64 DiskCachingFileLoaderCache::GetBlockOffset(u32 block) { @@ -523,7 +523,7 @@ void DiskCachingFileLoaderCache::WriteIndexData(u32 indexPos, BlockInfo &info) { } } -bool DiskCachingFileLoaderCache::LoadCacheFile(const std::string &path) { +bool DiskCachingFileLoaderCache::LoadCacheFile(const Path &path) { FILE *fp = File::OpenCFile(path, "rb+"); if (!fp) { return false; @@ -607,7 +607,7 @@ void DiskCachingFileLoaderCache::LoadCacheIndex() { } } -void DiskCachingFileLoaderCache::CreateCacheFile(const std::string &path) { +void DiskCachingFileLoaderCache::CreateCacheFile(const Path &path) { maxBlocks_ = DetermineMaxBlocks(); if (maxBlocks_ < MAX_BLOCKS_LOWER_BOUND) { GarbageCollectCacheFiles(MAX_BLOCKS_LOWER_BOUND * DEFAULT_BLOCK_SIZE); @@ -721,7 +721,7 @@ bool DiskCachingFileLoaderCache::LockCacheFile(bool lockStatus) { return true; } -bool DiskCachingFileLoaderCache::RemoveCacheFile(const std::string &path) { +bool DiskCachingFileLoaderCache::RemoveCacheFile(const Path &path) { // Note that some platforms, you can't delete open files. So we check. CloseFileHandle(); return File::Delete(path); @@ -749,13 +749,14 @@ bool DiskCachingFileLoaderCache::HasData() const { } u64 DiskCachingFileLoaderCache::FreeDiskSpace() { - std::string dir = cacheDir_; + Path dir = cacheDir_; if (dir.empty()) { dir = GetSysDirectory(DIRECTORY_CACHE); } + // TODO(scoped): uint64_t result = 0; - if (free_disk_space(dir, result)) { + if (free_disk_space(dir.ToString(), result)) { return result; } @@ -789,34 +790,34 @@ u32 DiskCachingFileLoaderCache::DetermineMaxBlocks() { } u32 DiskCachingFileLoaderCache::CountCachedFiles() { - std::string dir = cacheDir_; + Path dir = cacheDir_; if (dir.empty()) { dir = GetSysDirectory(DIRECTORY_CACHE); } - std::vector files; - return (u32)getFilesInDir(dir.c_str(), &files, "ppdc:"); + std::vector files; + return (u32)GetFilesInDir(dir, &files, "ppdc:"); } void DiskCachingFileLoaderCache::GarbageCollectCacheFiles(u64 goalBytes) { // We attempt to free up at least enough files from the cache to get goalBytes more space. - const std::vector usedPaths = DiskCachingFileLoader::GetCachedPathsInUse(); + const std::vector usedPaths = DiskCachingFileLoader::GetCachedPathsInUse(); std::set used; - for (std::string path : usedPaths) { + for (const Path &path : usedPaths) { used.insert(MakeCacheFilename(path)); } - std::string dir = cacheDir_; + Path dir = cacheDir_; if (dir.empty()) { dir = GetSysDirectory(DIRECTORY_CACHE); } - std::vector files; - getFilesInDir(dir.c_str(), &files, "ppdc:"); + std::vector files; + File::GetFilesInDir(dir, &files, "ppdc:"); u64 remaining = goalBytes; // TODO: Could order by LRU or etc. - for (FileInfo file : files) { + for (File::FileInfo &file : files) { if (file.isDirectory) { continue; } @@ -826,7 +827,7 @@ void DiskCachingFileLoaderCache::GarbageCollectCacheFiles(u64 goalBytes) { } #ifdef _WIN32 - const std::wstring w32path = ConvertUTF8ToWString(file.fullName); + const std::wstring w32path = file.fullName.ToWString(); bool success = DeleteFileW(w32path.c_str()) != 0; #else bool success = unlink(file.fullName.c_str()) == 0; diff --git a/Core/FileLoaders/DiskCachingFileLoader.h b/Core/FileLoaders/DiskCachingFileLoader.h index 622ffb0f07..8dafab9c4f 100644 --- a/Core/FileLoaders/DiskCachingFileLoader.h +++ b/Core/FileLoaders/DiskCachingFileLoader.h @@ -22,6 +22,7 @@ #include #include "Common/Common.h" +#include "Common/File/Path.h" #include "Common/Swap.h" #include "Core/Loaders.h" @@ -41,7 +42,7 @@ public: } size_t ReadAt(s64 absolutePos, size_t bytes, void *data, Flags flags = Flags::NONE) override; - static std::vector GetCachedPathsInUse(); + static std::vector GetCachedPathsInUse(); private: void Prepare(); @@ -54,13 +55,13 @@ private: // We don't support concurrent disk cache access (we use memory cached indexes.) // So we have to ensure there's only one of these per. - static std::map caches_; + static std::map caches_; static std::mutex cachesMutex_; }; class DiskCachingFileLoaderCache { public: - DiskCachingFileLoaderCache(const std::string &path, u64 filesize); + DiskCachingFileLoaderCache(const Path &path, u64 filesize); ~DiskCachingFileLoaderCache(); bool IsValid() { @@ -75,7 +76,7 @@ public: return --refCount_ == 0; } - static void SetCacheDir(const std::string &path) { + static void SetCacheDir(const Path &path) { cacheDir_ = path; } @@ -86,7 +87,7 @@ public: bool HasData() const; private: - void InitCache(const std::string &path); + void InitCache(const Path &path); void ShutdownCache(); bool MakeCacheSpaceFor(size_t blocks); void RebalanceGenerations(); @@ -98,13 +99,13 @@ private: void WriteIndexData(u32 indexPos, BlockInfo &info); s64 GetBlockOffset(u32 block); - std::string MakeCacheFilePath(const std::string &path); - std::string MakeCacheFilename(const std::string &path); - bool LoadCacheFile(const std::string &path); + Path MakeCacheFilePath(const Path &filename); + std::string MakeCacheFilename(const Path &path); + bool LoadCacheFile(const Path &path); void LoadCacheIndex(); - void CreateCacheFile(const std::string &path); + void CreateCacheFile(const Path &path); bool LockCacheFile(bool lockStatus); - bool RemoveCacheFile(const std::string &path); + bool RemoveCacheFile(const Path &path); void CloseFileHandle(); u64 FreeDiskSpace(); @@ -146,7 +147,7 @@ private: size_t cacheSize_; size_t indexCount_; std::mutex lock_; - std::string origPath_; + Path origPath_; struct FileHeader { char magic[8]; @@ -176,5 +177,5 @@ private: FILE *f_ = nullptr; int fd_ = 0; - static std::string cacheDir_; + static Path cacheDir_; }; diff --git a/Core/FileLoaders/HTTPFileLoader.cpp b/Core/FileLoaders/HTTPFileLoader.cpp index 9cf2d653b6..5078dcc4a3 100644 --- a/Core/FileLoaders/HTTPFileLoader.cpp +++ b/Core/FileLoaders/HTTPFileLoader.cpp @@ -20,14 +20,17 @@ #include "Common/Common.h" #include "Common/Log.h" #include "Common/StringUtils.h" +#include "Core/Config.h" #include "Core/FileLoaders/HTTPFileLoader.h" -HTTPFileLoader::HTTPFileLoader(const std::string &filename) - : url_(filename), filename_(filename) { +HTTPFileLoader::HTTPFileLoader(const ::Path &filename) + : url_(filename.ToString()), progress_(&cancel_), filename_(filename) { } void HTTPFileLoader::Prepare() { std::call_once(preparedFlag_, [this](){ + client_.SetUserAgent(StringFromFormat("PPSSPP/%s", PPSSPP_GIT_VERSION)); + std::vector responseHeaders; Url resourceURL = url_; int redirectsLeft = 20; @@ -134,7 +137,7 @@ int HTTPFileLoader::SendHEAD(const Url &url, std::vector &responseH return -400; } - int err = client_.SendRequest("HEAD", url.Resource().c_str()); + int err = client_.SendRequest("HEAD", url.Resource().c_str(), nullptr, &progress_); if (err < 0) { ERROR_LOG(LOADER, "HTTP request failed, failed to send request: %s port %d", url.Host().c_str(), url.Port()); latestError_ = "Could not connect (could not request data)"; @@ -142,8 +145,8 @@ int HTTPFileLoader::SendHEAD(const Url &url, std::vector &responseH return -400; } - Buffer readbuf; - return client_.ReadResponseHeaders(&readbuf, responseHeaders); + net::Buffer readbuf; + return client_.ReadResponseHeaders(&readbuf, responseHeaders, &progress_); } HTTPFileLoader::~HTTPFileLoader() { @@ -169,7 +172,7 @@ s64 HTTPFileLoader::FileSize() { return filesize_; } -std::string HTTPFileLoader::Path() const { +Path HTTPFileLoader::GetPath() const { return filename_; } @@ -193,16 +196,16 @@ size_t HTTPFileLoader::ReadAt(s64 absolutePos, size_t bytes, void *data, Flags f snprintf(requestHeaders, sizeof(requestHeaders), "Range: bytes=%lld-%lld\r\n", absolutePos, absoluteEnd - 1); - int err = client_.SendRequest("GET", url_.Resource().c_str(), requestHeaders, nullptr); + int err = client_.SendRequest("GET", url_.Resource().c_str(), requestHeaders, &progress_); if (err < 0) { latestError_ = "Invalid response reading data"; Disconnect(); return 0; } - Buffer readbuf; + net::Buffer readbuf; std::vector responseHeaders; - int code = client_.ReadResponseHeaders(&readbuf, responseHeaders); + int code = client_.ReadResponseHeaders(&readbuf, responseHeaders, &progress_); if (code != 206) { ERROR_LOG(LOADER, "HTTP server did not respond with range, received code=%03d", code); latestError_ = "Invalid response reading data"; @@ -232,8 +235,8 @@ size_t HTTPFileLoader::ReadAt(s64 absolutePos, size_t bytes, void *data, Flags f } // TODO: Would be nice to read directly. - Buffer output; - int res = client_.ReadResponseEntity(&readbuf, responseHeaders, &output); + net::Buffer output; + int res = client_.ReadResponseEntity(&readbuf, responseHeaders, &output, &progress_); if (res != 0) { ERROR_LOG(LOADER, "Unable to read HTTP response entity: %d", res); // Let's take anything we got anyway. Not worse than returning nothing? @@ -256,8 +259,8 @@ size_t HTTPFileLoader::ReadAt(s64 absolutePos, size_t bytes, void *data, Flags f void HTTPFileLoader::Connect() { if (!connected_) { - cancelConnect_ = false; + cancel_ = false; // Latency is important here, so reduce the timeout. - connected_ = client_.Connect(3, 10.0, &cancelConnect_); + connected_ = client_.Connect(3, 10.0, &cancel_); } } diff --git a/Core/FileLoaders/HTTPFileLoader.h b/Core/FileLoaders/HTTPFileLoader.h index d8cb6f3c47..84a4361097 100644 --- a/Core/FileLoaders/HTTPFileLoader.h +++ b/Core/FileLoaders/HTTPFileLoader.h @@ -20,6 +20,7 @@ #include #include +#include "Common/File/Path.h" #include "Common/Net/HTTPClient.h" #include "Common/Net/Resolve.h" #include "Common/Net/URL.h" @@ -28,7 +29,7 @@ class HTTPFileLoader : public FileLoader { public: - HTTPFileLoader(const std::string &filename); + HTTPFileLoader(const ::Path &filename); virtual ~HTTPFileLoader() override; bool IsRemote() override { @@ -38,7 +39,7 @@ public: virtual bool ExistsFast() override; virtual bool IsDirectory() override; virtual s64 FileSize() override; - virtual std::string Path() const override; + virtual Path GetPath() const override; virtual size_t ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags = Flags::NONE) override { return ReadAt(absolutePos, bytes * count, data, flags) / bytes; @@ -46,7 +47,7 @@ public: virtual size_t ReadAt(s64 absolutePos, size_t bytes, void *data, Flags flags = Flags::NONE) override; void Cancel() override { - cancelConnect_ = true; + cancel_ = true; } std::string LatestError() const override { @@ -70,9 +71,10 @@ private: s64 filepos_ = 0; Url url_; http::Client client_; - std::string filename_; + http::RequestProgress progress_; + ::Path filename_; bool connected_ = false; - bool cancelConnect_ = false; + bool cancel_ = false; const char *latestError_ = ""; std::once_flag preparedFlag_; diff --git a/Core/FileLoaders/LocalFileLoader.cpp b/Core/FileLoaders/LocalFileLoader.cpp index 9af65c1887..df3593c585 100644 --- a/Core/FileLoaders/LocalFileLoader.cpp +++ b/Core/FileLoaders/LocalFileLoader.cpp @@ -32,7 +32,7 @@ #endif #ifndef _WIN32 -LocalFileLoader::LocalFileLoader(int fd, const std::string &filename) : fd_(fd), filename_(filename), isOpenedByFd_(fd != -1) { +LocalFileLoader::LocalFileLoader(int fd, const Path &filename) : fd_(fd), filename_(filename), isOpenedByFd_(fd != -1) { if (fd != -1) { DetectSizeFd(); } @@ -51,7 +51,7 @@ void LocalFileLoader::DetectSizeFd() { } #endif -LocalFileLoader::LocalFileLoader(const std::string &filename) +LocalFileLoader::LocalFileLoader(const Path &filename) : filesize_(0), filename_(filename), isOpenedByFd_(false) { if (filename.empty()) { ERROR_LOG(FILESYS, "LocalFileLoader can't load empty filenames"); @@ -70,9 +70,9 @@ LocalFileLoader::LocalFileLoader(const std::string &filename) const DWORD access = GENERIC_READ, share = FILE_SHARE_READ, mode = OPEN_EXISTING, flags = FILE_ATTRIBUTE_NORMAL; #if PPSSPP_PLATFORM(UWP) - handle_ = CreateFile2(ConvertUTF8ToWString(filename).c_str(), access, share, mode, nullptr); + handle_ = CreateFile2(filename.ToWString().c_str(), access, share, mode, nullptr); #else - handle_ = CreateFile(ConvertUTF8ToWString(filename).c_str(), access, share, nullptr, mode, flags, nullptr); + handle_ = CreateFile(filename.ToWString().c_str(), access, share, nullptr, mode, flags, nullptr); #endif if (handle_ == INVALID_HANDLE_VALUE) { return; @@ -112,15 +112,15 @@ bool LocalFileLoader::Exists() { #else if (handle_ != INVALID_HANDLE_VALUE || IsDirectory()) { #endif - FileInfo info; - return getFileInfo(filename_.c_str(), &info); + File::FileInfo info; + return File::GetFileInfo(filename_, &info); } return false; } bool LocalFileLoader::IsDirectory() { - FileInfo info; - if (getFileInfo(filename_.c_str(), &info)) { + File::FileInfo info; + if (File::GetFileInfo(filename_, &info)) { return info.isDirectory; } return false; @@ -130,10 +130,6 @@ s64 LocalFileLoader::FileSize() { return filesize_; } -std::string LocalFileLoader::Path() const { - return filename_; -} - size_t LocalFileLoader::ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags) { if (bytes == 0) return 0; diff --git a/Core/FileLoaders/LocalFileLoader.h b/Core/FileLoaders/LocalFileLoader.h index d3933b55c4..9ed9b185fa 100644 --- a/Core/FileLoaders/LocalFileLoader.h +++ b/Core/FileLoaders/LocalFileLoader.h @@ -18,22 +18,27 @@ #pragma once #include + #include "Common/CommonTypes.h" +#include "Common/File/Path.h" #include "Core/Loaders.h" + #ifdef _WIN32 typedef void *HANDLE; #endif class LocalFileLoader : public FileLoader { public: - LocalFileLoader(const std::string &filename); - LocalFileLoader(const int fd, const std::string &filename); + LocalFileLoader(const Path &filename); + LocalFileLoader(const int fd, const Path &filename); virtual ~LocalFileLoader(); virtual bool Exists() override; virtual bool IsDirectory() override; virtual s64 FileSize() override; - virtual std::string Path() const override; + virtual Path GetPath() const override { + return filename_; + } virtual size_t ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags = Flags::NONE) override; private: @@ -44,7 +49,7 @@ private: HANDLE handle_; #endif u64 filesize_; - std::string filename_; + Path filename_; std::mutex readLock_; bool isOpenedByFd_; }; diff --git a/Core/FileLoaders/RamCachingFileLoader.cpp b/Core/FileLoaders/RamCachingFileLoader.cpp index 73e0151b91..3e85777eec 100644 --- a/Core/FileLoaders/RamCachingFileLoader.cpp +++ b/Core/FileLoaders/RamCachingFileLoader.cpp @@ -225,7 +225,7 @@ void RamCachingFileLoader::StartReadAhead(s64 pos) { if (aheadThread_.joinable()) aheadThread_.join(); aheadThread_ = std::thread([this] { - setCurrentThreadName("FileLoaderReadAhead"); + SetCurrentThreadName("FileLoaderReadAhead"); while (aheadRemaining_ != 0 && !aheadCancel_) { // Where should we look? diff --git a/Core/FileSystems/BlobFileSystem.cpp b/Core/FileSystems/BlobFileSystem.cpp index 730dca7854..46f22085e5 100644 --- a/Core/FileSystems/BlobFileSystem.cpp +++ b/Core/FileSystems/BlobFileSystem.cpp @@ -128,11 +128,6 @@ bool BlobFileSystem::RemoveFile(const std::string &filename) { return false; } -bool BlobFileSystem::GetHostPath(const std::string &inpath, std::string &outpath) { - outpath = fileLoader_->Path(); - return true; -} - u64 BlobFileSystem::FreeSpace(const std::string &path) { return 0; } diff --git a/Core/FileSystems/BlobFileSystem.h b/Core/FileSystems/BlobFileSystem.h index 3657522ddd..faab90004a 100644 --- a/Core/FileSystems/BlobFileSystem.h +++ b/Core/FileSystems/BlobFileSystem.h @@ -23,6 +23,8 @@ #include #include + +#include "Common/File/Path.h" #include "Core/Loaders.h" #include "Core/FileSystems/FileSystem.h" @@ -50,7 +52,6 @@ public: bool RmDir(const std::string &dirname) override; int RenameFile(const std::string &from, const std::string &to) override; bool RemoveFile(const std::string &filename) override; - bool GetHostPath(const std::string &inpath, std::string &outpath) override; u64 FreeSpace(const std::string &path) override; private: diff --git a/Core/FileSystems/BlockDevices.cpp b/Core/FileSystems/BlockDevices.cpp index 5314fac125..0cbd59bb9a 100644 --- a/Core/FileSystems/BlockDevices.cpp +++ b/Core/FileSystems/BlockDevices.cpp @@ -202,7 +202,7 @@ CISOFileBlockDevice::CISOFileBlockDevice(FileLoader *fileLoader) u64 expectedFileSize = lastIndexPos << indexShift; if (expectedFileSize > fileSize) { ERROR_LOG(LOADER, "Expected CSO to at least be %lld bytes, but file is %lld bytes. File: '%s'", - expectedFileSize, fileSize, fileLoader->Path().c_str()); + expectedFileSize, fileSize, fileLoader->GetPath().c_str()); NotifyReadError(); } } diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index 2fe206c977..5e84837b31 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -36,6 +36,7 @@ #include "Common/File/FileUtil.h" #include "Common/File/DiskFree.h" #include "Common/File/VFS/VFS.h" +#include "Common/SysError.h" #include "Core/FileSystems/DirectoryFileSystem.h" #include "Core/FileSystems/ISOFileSystem.h" #include "Core/HLE/sceKernel.h" @@ -69,7 +70,7 @@ static bool FixFilenameCase(const std::string &path, std::string &filename) { // Are we lucky? - if (File::Exists(path + filename)) + if (File::Exists(Path(path + filename))) return true; size_t filenameSize = filename.size(); // size in bytes, not characters @@ -150,8 +151,8 @@ bool FixPathCase(const std::string &basePath, std::string &path, FixPathCaseBeha path.replace(start, i - start, component); - fullPath.append(component); fullPath.append(1, '/'); + fullPath.append(component); } start = i + 1; @@ -162,7 +163,7 @@ bool FixPathCase(const std::string &basePath, std::string &path, FixPathCaseBeha #endif -DirectoryFileSystem::DirectoryFileSystem(IHandleAllocator *_hAlloc, std::string _basePath, FileSystemFlags _flags) : basePath(_basePath), flags(_flags) { +DirectoryFileSystem::DirectoryFileSystem(IHandleAllocator *_hAlloc, const Path & _basePath, FileSystemFlags _flags) : basePath(_basePath), flags(_flags) { File::CreateFullPath(basePath); hAlloc = _hAlloc; } @@ -171,7 +172,7 @@ DirectoryFileSystem::~DirectoryFileSystem() { CloseAll(); } -std::string DirectoryFileHandle::GetLocalPath(const std::string &basePath, std::string localpath) +Path DirectoryFileHandle::GetLocalPath(const Path &basePath, std::string localpath) { if (localpath.empty()) return basePath; @@ -179,23 +180,16 @@ std::string DirectoryFileHandle::GetLocalPath(const std::string &basePath, std:: if (localpath[0] == '/') localpath.erase(0, 1); - std::string result = basePath + localpath; -#ifdef _WIN32 - for (char &c : result) { - if (c == '/') - c = '\\'; - } -#endif - return result; + return basePath / localpath; } -bool DirectoryFileHandle::Open(const std::string &basePath, std::string &fileName, FileAccess access, u32 &error) { +bool DirectoryFileHandle::Open(const Path &basePath, std::string &fileName, FileAccess access, u32 &error) { error = 0; #if HOST_IS_CASE_SENSITIVE if (access & (FILEACCESS_APPEND|FILEACCESS_CREATE|FILEACCESS_WRITE)) { DEBUG_LOG(FILESYS, "Checking case for path %s", fileName.c_str()); - if (!FixPathCase(basePath, fileName, FPC_PATH_MUST_EXIST)) { + if (!FixPathCase(basePath.ToString(), fileName, FPC_PATH_MUST_EXIST)) { error = SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND; return false; // or go on and attempt (for a better error code than just 0?) } @@ -203,8 +197,8 @@ bool DirectoryFileHandle::Open(const std::string &basePath, std::string &fileNam // else we try fopen first (in case we're lucky) before simulating case insensitivity #endif - std::string fullName = GetLocalPath(basePath, fileName); - VERBOSE_LOG(FILESYS,"Actually opening %s", fullName.c_str()); + Path fullName = GetLocalPath(basePath, fileName); + VERBOSE_LOG(FILESYS, "Actually opening %s", fullName.c_str()); // On the PSP, truncating doesn't lose data. If you seek later, you'll recover it. // This is abnormal, so we deviate from the PSP's behavior and truncate on write/close. @@ -239,9 +233,9 @@ bool DirectoryFileHandle::Open(const std::string &basePath, std::string &fileNam // Let's do it! #if PPSSPP_PLATFORM(UWP) - hFile = CreateFile2(ConvertUTF8ToWString(fullName).c_str(), desired, sharemode, openmode, nullptr); + hFile = CreateFile2(fullName.ToWString().c_str(), desired, sharemode, openmode, nullptr); #else - hFile = CreateFile(ConvertUTF8ToWString(fullName).c_str(), desired, sharemode, 0, openmode, 0, 0); + hFile = CreateFile(fullName.ToWString().c_str(), desired, sharemode, 0, openmode, 0, 0); #endif bool success = hFile != INVALID_HANDLE_VALUE; if (!success) { @@ -251,9 +245,9 @@ bool DirectoryFileHandle::Open(const std::string &basePath, std::string &fileNam // Sometimes, the file is locked for write, let's try again. sharemode |= FILE_SHARE_WRITE; #if PPSSPP_PLATFORM(UWP) - hFile = CreateFile2(ConvertUTF8ToWString(fullName).c_str(), desired, sharemode, openmode, nullptr); + hFile = CreateFile2(fullName.ToWString().c_str(), desired, sharemode, openmode, nullptr); #else - hFile = CreateFile(ConvertUTF8ToWString(fullName).c_str(), desired, sharemode, 0, openmode, 0, 0); + hFile = CreateFile(fullName.ToWString().c_str(), desired, sharemode, 0, openmode, 0, 0); #endif success = hFile != INVALID_HANDLE_VALUE; if (!success) { @@ -295,11 +289,11 @@ bool DirectoryFileHandle::Open(const std::string &basePath, std::string &fileNam #if HOST_IS_CASE_SENSITIVE if (!success && !(access & FILEACCESS_CREATE)) { - if (!FixPathCase(basePath, fileName, FPC_PATH_MUST_EXIST)) { + if (!FixPathCase(basePath.ToString(), fileName, FPC_PATH_MUST_EXIST)) { error = SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND; return false; } - fullName = GetLocalPath(basePath,fileName); + fullName = GetLocalPath(basePath, fileName); const char *fullNameC = fullName.c_str(); DEBUG_LOG(FILESYS, "Case may have been incorrect, second try opening %s (%s)", fullNameC, fileName.c_str()); @@ -336,7 +330,7 @@ bool DirectoryFileHandle::Open(const std::string &basePath, std::string &fileNam #endif // Try to detect reads/writes to PSP/GAME to avoid them in replays. - if (fullName.find("/PSP/GAME/") != fullName.npos || fullName.find("\\PSP\\GAME\\") != fullName.npos) { + if (fullName.FilePathContains("PSP/GAME/")) { inGameDir_ = true; } @@ -478,20 +472,20 @@ void DirectoryFileSystem::CloseAll() { entries.clear(); } -std::string DirectoryFileSystem::GetLocalPath(std::string localpath) { - if (localpath.empty()) +Path DirectoryFileSystem::GetLocalPath(std::string internalPath) { + if (internalPath.empty()) return basePath; - if (localpath[0] == '/') - localpath.erase(0,1); + if (internalPath[0] == '/') + internalPath.erase(0, 1); //Convert slashes #ifdef _WIN32 - for (size_t i = 0; i < localpath.size(); i++) { - if (localpath[i] == '/') - localpath[i] = '\\'; + for (size_t i = 0; i < internalPath.size(); i++) { + if (internalPath[i] == '/') + internalPath[i] = '\\'; } #endif - return basePath + localpath; + return basePath / internalPath; } bool DirectoryFileSystem::MkDir(const std::string &dirname) { @@ -501,7 +495,7 @@ bool DirectoryFileSystem::MkDir(const std::string &dirname) { // duplicate (different case) directories std::string fixedCase = dirname; - if (!FixPathCase(basePath,fixedCase, FPC_PARTIAL_ALLOWED)) + if (!FixPathCase(basePath.ToString(), fixedCase, FPC_PARTIAL_ALLOWED)) result = false; else result = File::CreateFullPath(GetLocalPath(fixedCase)); @@ -512,7 +506,7 @@ bool DirectoryFileSystem::MkDir(const std::string &dirname) { } bool DirectoryFileSystem::RmDir(const std::string &dirname) { - std::string fullName = GetLocalPath(dirname); + Path fullName = GetLocalPath(dirname); #if HOST_IS_CASE_SENSITIVE // Maybe we're lucky? @@ -520,11 +514,11 @@ bool DirectoryFileSystem::RmDir(const std::string &dirname) { return (bool)ReplayApplyDisk(ReplayAction::RMDIR, true, CoreTiming::GetGlobalTimeUs()); // Nope, fix case and try again. Should we try again? - fullName = dirname; - if (!FixPathCase(basePath,fullName, FPC_FILE_MUST_EXIST)) + std::string fullPath = dirname; + if (!FixPathCase(basePath.ToString(), fullPath, FPC_FILE_MUST_EXIST)) return (bool)ReplayApplyDisk(ReplayAction::RMDIR, false, CoreTiming::GetGlobalTimeUs()); - fullName = GetLocalPath(fullName); + fullName = GetLocalPath(fullPath); #endif /*#ifdef _WIN32 @@ -553,36 +547,35 @@ int DirectoryFileSystem::RenameFile(const std::string &from, const std::string & if (from == fullTo) return ReplayApplyDisk(ReplayAction::FILE_RENAME, SCE_KERNEL_ERROR_ERRNO_FILE_ALREADY_EXISTS, CoreTiming::GetGlobalTimeUs()); - std::string fullFrom = GetLocalPath(from); + Path fullFrom = GetLocalPath(from); #if HOST_IS_CASE_SENSITIVE // In case TO should overwrite a file with different case. Check error code? - if (!FixPathCase(basePath,fullTo, FPC_PATH_MUST_EXIST)) + if (!FixPathCase(basePath.ToString(), fullTo, FPC_PATH_MUST_EXIST)) return ReplayApplyDisk(ReplayAction::FILE_RENAME, -1, CoreTiming::GetGlobalTimeUs()); #endif - fullTo = GetLocalPath(fullTo); - const char * fullToC = fullTo.c_str(); + Path fullToPath = GetLocalPath(fullTo); #ifdef _WIN32 - bool retValue = (MoveFileEx(ConvertUTF8ToWString(fullFrom).c_str(), ConvertUTF8ToWString(fullToC).c_str(), 0) == TRUE); + bool retValue = (MoveFileEx(fullFrom.ToWString().c_str(), fullToPath.ToWString().c_str(), 0) == TRUE); #else - bool retValue = (0 == rename(fullFrom.c_str(), fullToC)); + bool retValue = (0 == rename(fullFrom.c_str(), fullToPath.c_str())); #endif #if HOST_IS_CASE_SENSITIVE if (! retValue) { // May have failed due to case sensitivity on FROM, so try again. Check error code? - fullFrom = from; - if (!FixPathCase(basePath,fullFrom, FPC_FILE_MUST_EXIST)) + std::string fullFromPath = from; + if (!FixPathCase(basePath.ToString(), fullFromPath, FPC_FILE_MUST_EXIST)) return ReplayApplyDisk(ReplayAction::FILE_RENAME, -1, CoreTiming::GetGlobalTimeUs()); - fullFrom = GetLocalPath(fullFrom); + fullFrom = GetLocalPath(fullFromPath); #ifdef _WIN32 - retValue = (MoveFile(fullFrom.c_str(), fullToC) == TRUE); + retValue = (MoveFile(fullFrom.c_str(), fullToPath.c_str()) == TRUE); #else - retValue = (0 == rename(fullFrom.c_str(), fullToC)); + retValue = (0 == rename(fullFrom.c_str(), fullToPath.c_str())); #endif } #endif @@ -593,7 +586,7 @@ int DirectoryFileSystem::RenameFile(const std::string &from, const std::string & } bool DirectoryFileSystem::RemoveFile(const std::string &filename) { - std::string fullName = GetLocalPath(filename); + Path fullName = GetLocalPath(filename); #ifdef _WIN32 bool retValue = (::DeleteFileA(fullName.c_str()) == TRUE); #else @@ -604,10 +597,10 @@ bool DirectoryFileSystem::RemoveFile(const std::string &filename) { if (! retValue) { // May have failed due to case sensitivity, so try again. Try even if it fails? - fullName = filename; - if (!FixPathCase(basePath,fullName, FPC_FILE_MUST_EXIST)) + std::string fullNamePath = filename; + if (!FixPathCase(basePath.ToString(), fullNamePath, FPC_FILE_MUST_EXIST)) return (bool)ReplayApplyDisk(ReplayAction::FILE_REMOVE, false, CoreTiming::GetGlobalTimeUs()); - fullName = GetLocalPath(fullName); + fullName = GetLocalPath(fullNamePath); #ifdef _WIN32 retValue = (::DeleteFileA(fullName.c_str()) == TRUE); @@ -631,7 +624,8 @@ int DirectoryFileSystem::OpenFile(std::string filename, FileAccess access, const err = ReplayApplyDisk(ReplayAction::FILE_OPEN, err, CoreTiming::GetGlobalTimeUs()); if (err != 0) { #ifdef _WIN32 - ERROR_LOG(FILESYS, "DirectoryFileSystem::OpenFile: FAILED, %i - access = %i", (int)GetLastError(), (int)access); + auto win32err = GetLastError(); + ERROR_LOG(FILESYS, "DirectoryFileSystem::OpenFile: FAILED, %i - access = %i, %s", (int)win32err, (int)access, GetStringErrorMsg(win32err).c_str()); #else ERROR_LOG(FILESYS, "DirectoryFileSystem::OpenFile: FAILED, %i - access = %i", errno, (int)access); #endif @@ -734,10 +728,10 @@ PSPFileInfo DirectoryFileSystem::GetFileInfo(std::string filename) { PSPFileInfo x; x.name = filename; - std::string fullName = GetLocalPath(filename); + Path fullName = GetLocalPath(filename); if (!File::Exists(fullName)) { #if HOST_IS_CASE_SENSITIVE - if (! FixPathCase(basePath,filename, FPC_FILE_MUST_EXIST)) + if (! FixPathCase(basePath.ToString(), filename, FPC_FILE_MUST_EXIST)) return ReplayApplyDiskFileInfo(x, CoreTiming::GetGlobalTimeUs()); fullName = GetLocalPath(filename); @@ -751,15 +745,15 @@ PSPFileInfo DirectoryFileSystem::GetFileInfo(std::string filename) { x.exists = true; if (x.type != FILETYPE_DIRECTORY) { - File::FileDetails details; - if (!File::GetFileDetails(fullName, &details)) { - ERROR_LOG(FILESYS, "DirectoryFileSystem::GetFileInfo: GetFileDetails failed: %s", fullName.c_str()); + File::FileInfo info; + if (!File::GetFileInfo(fullName, &info)) { + ERROR_LOG(FILESYS, "DirectoryFileSystem::GetFileInfo: GetFileInfo failed: %s", fullName.c_str()); } else { - x.size = details.size; - x.access = details.access; - time_t atime = details.atime; - time_t ctime = details.ctime; - time_t mtime = details.mtime; + x.size = info.size; + x.access = info.access; + time_t atime = info.atime; + time_t ctime = info.ctime; + time_t mtime = info.mtime; localtime_r((time_t*)&atime, &x.atime); localtime_r((time_t*)&ctime, &x.ctime); @@ -770,11 +764,6 @@ PSPFileInfo DirectoryFileSystem::GetFileInfo(std::string filename) { return ReplayApplyDiskFileInfo(x, CoreTiming::GetGlobalTimeUs()); } -bool DirectoryFileSystem::GetHostPath(const std::string &inpath, std::string &outpath) { - outpath = GetLocalPath(inpath); - return true; -} - #ifdef _WIN32 #define FILETIME_FROM_UNIX_EPOCH_US 11644473600000000ULL @@ -856,9 +845,9 @@ std::vector DirectoryFileSystem::GetDirListing(std::string path) { WIN32_FIND_DATA findData; HANDLE hFind; - std::string w32path = GetLocalPath(path) + "\\*.*"; + std::wstring w32path = GetLocalPath(path).ToWString() + L"\\*.*"; - hFind = FindFirstFileEx(ConvertUTF8ToWString(w32path).c_str(), FindExInfoStandard, &findData, FindExSearchNameMatch, NULL, 0); + hFind = FindFirstFileEx(w32path.c_str(), FindExInfoStandard, &findData, FindExSearchNameMatch, NULL, 0); if (hFind == INVALID_HANDLE_VALUE) { // Just return the empty array. @@ -903,11 +892,11 @@ std::vector DirectoryFileSystem::GetDirListing(std::string path) { FindClose(hFind); #else dirent *dirp; - std::string localPath = GetLocalPath(path); + Path localPath = GetLocalPath(path); DIR *dp = opendir(localPath.c_str()); #if HOST_IS_CASE_SENSITIVE - if (dp == NULL && FixPathCase(basePath,path, FPC_FILE_MUST_EXIST)) { + if (dp == NULL && FixPathCase(basePath.ToString(), path, FPC_FILE_MUST_EXIST)) { // May have failed due to case sensitivity, try again localPath = GetLocalPath(path); dp = opendir(localPath.c_str()); @@ -923,7 +912,7 @@ std::vector DirectoryFileSystem::GetDirListing(std::string path) { while ((dirp = readdir(dp)) != NULL) { PSPFileInfo entry; struct stat s; - std::string fullName = GetLocalPath(path) + "/"+dirp->d_name; + Path fullName = GetLocalPath(path) / std::string(dirp->d_name); stat(fullName.c_str(), &s); if (S_ISDIR(s.st_mode)) entry.type = FILETYPE_DIRECTORY; @@ -955,15 +944,15 @@ std::vector DirectoryFileSystem::GetDirListing(std::string path) { u64 DirectoryFileSystem::FreeSpace(const std::string &path) { uint64_t result = 0; - if (free_disk_space(GetLocalPath(path), result)) { + if (free_disk_space(GetLocalPath(path).ToString(), result)) { return ReplayApplyDisk64(ReplayAction::FREESPACE, result, CoreTiming::GetGlobalTimeUs()); } #if HOST_IS_CASE_SENSITIVE std::string fixedCase = path; - if (FixPathCase(basePath, fixedCase, FPC_FILE_MUST_EXIST)) { + if (FixPathCase(basePath.ToString(), fixedCase, FPC_FILE_MUST_EXIST)) { // May have failed due to case sensitivity, try again. - if (free_disk_space(GetLocalPath(fixedCase), result)) { + if (free_disk_space(GetLocalPath(fixedCase).ToString(), result)) { return ReplayApplyDisk64(ReplayAction::FREESPACE, result, CoreTiming::GetGlobalTimeUs()); } } @@ -1095,7 +1084,7 @@ PSPFileInfo VFSFileSystem::GetFileInfo(std::string filename) { x.name = filename; std::string fullName = GetLocalPath(filename); - FileInfo fo; + File::FileInfo fo; if (VFSGetFileInfo(fullName.c_str(), &fo)) { x.exists = fo.exists; if (x.exists) { @@ -1179,12 +1168,6 @@ size_t VFSFileSystem::SeekFile(u32 handle, s32 position, FileMove type) { } } - -bool VFSFileSystem::GetHostPath(const std::string &inpath, std::string &outpath) { - // NOT SUPPORTED - return false; -} - std::vector VFSFileSystem::GetDirListing(std::string path) { std::vector myVector; // TODO diff --git a/Core/FileSystems/DirectoryFileSystem.h b/Core/FileSystems/DirectoryFileSystem.h index 84e51bbf32..2c7a2ca709 100644 --- a/Core/FileSystems/DirectoryFileSystem.h +++ b/Core/FileSystems/DirectoryFileSystem.h @@ -20,6 +20,8 @@ // TODO: Remove the Windows-specific code, FILE is fine there too. #include + +#include "Common/File/Path.h" #include "Core/FileSystems/FileSystem.h" #ifdef _WIN32 @@ -74,8 +76,8 @@ struct DirectoryFileHandle { DirectoryFileHandle(Flags flags) : replay_(flags != SKIP_REPLAY) { } - std::string GetLocalPath(const std::string &basePath, std::string localpath); - bool Open(const std::string &basePath, std::string &fileName, FileAccess access, u32 &err); + Path GetLocalPath(const Path &basePath, std::string localpath); + bool Open(const Path &basePath, std::string &fileName, FileAccess access, u32 &err); size_t Read(u8* pointer, s64 size); size_t Write(const u8* pointer, s64 size); size_t Seek(s32 position, FileMove type); @@ -84,7 +86,7 @@ struct DirectoryFileHandle { class DirectoryFileSystem : public IFileSystem { public: - DirectoryFileSystem(IHandleAllocator *_hAlloc, std::string _basePath, FileSystemFlags _flags = FileSystemFlags::NONE); + DirectoryFileSystem(IHandleAllocator *_hAlloc, const Path &_basePath, FileSystemFlags _flags = FileSystemFlags::NONE); ~DirectoryFileSystem(); void CloseAll(); @@ -107,7 +109,6 @@ public: bool RmDir(const std::string &dirname) override; int RenameFile(const std::string &from, const std::string &to) override; bool RemoveFile(const std::string &filename) override; - bool GetHostPath(const std::string &inpath, std::string &outpath) override; FileSystemFlags Flags() override { return flags; } u64 FreeSpace(const std::string &path) override; @@ -120,11 +121,11 @@ private: typedef std::map EntryMap; EntryMap entries; - std::string basePath; + Path basePath; IHandleAllocator *hAlloc; FileSystemFlags flags; // In case of Windows: Translate slashes, etc. - std::string GetLocalPath(std::string localpath); + Path GetLocalPath(std::string internalPath); }; // VFSFileSystem: Ability to map in Android APK paths as well! Does not support all features, only meant for fonts. @@ -152,7 +153,6 @@ public: bool RmDir(const std::string &dirname) override; int RenameFile(const std::string &from, const std::string &to) override; bool RemoveFile(const std::string &filename) override; - bool GetHostPath(const std::string &inpath, std::string &outpath) override; FileSystemFlags Flags() override { return FileSystemFlags::FLASH; } u64 FreeSpace(const std::string &path) override { return 0; } diff --git a/Core/FileSystems/FileSystem.h b/Core/FileSystems/FileSystem.h index acb3852328..87e8c92e07 100644 --- a/Core/FileSystems/FileSystem.h +++ b/Core/FileSystems/FileSystem.h @@ -23,6 +23,7 @@ #include #include "Common.h" +#include "Common/File/Path.h" #include "Core/HLE/sceKernel.h" enum FileAccess { @@ -130,7 +131,6 @@ public: virtual bool RmDir(const std::string &dirname) = 0; virtual int RenameFile(const std::string &from, const std::string &to) = 0; virtual bool RemoveFile(const std::string &filename) = 0; - virtual bool GetHostPath(const std::string &inpath, std::string &outpath) = 0; virtual int Ioctl(u32 handle, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen, int &usec) = 0; virtual PSPDevType DevType(u32 handle) = 0; virtual FileSystemFlags Flags() = 0; @@ -156,7 +156,6 @@ public: virtual bool RmDir(const std::string &dirname) override {return false;} virtual int RenameFile(const std::string &from, const std::string &to) override {return -1;} virtual bool RemoveFile(const std::string &filename) override {return false;} - virtual bool GetHostPath(const std::string &inpath, std::string &outpath) override {return false;} virtual int Ioctl(u32 handle, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen, int &usec) override {return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED; } virtual PSPDevType DevType(u32 handle) override { return PSPDevType::INVALID; } virtual FileSystemFlags Flags() override { return FileSystemFlags::NONE; } diff --git a/Core/FileSystems/ISOFileSystem.cpp b/Core/FileSystems/ISOFileSystem.cpp index d5f20409bd..74c1fc9104 100644 --- a/Core/FileSystems/ISOFileSystem.cpp +++ b/Core/FileSystems/ISOFileSystem.cpp @@ -182,7 +182,7 @@ void ISOFileSystem::ReadDirectory(TreeEntry *root) { u8 theSector[2048]; if (!blockDevice->ReadBlock(secnum, theSector)) { blockDevice->NotifyReadError(); - ERROR_LOG(FILESYS, "Error reading block for directory %s - skipping", root->name.c_str()); + ERROR_LOG(FILESYS, "Error reading block for directory '%s' in sector %d - skipping", root->name.c_str(), secnum); root->valid = true; // Prevents re-reading return; } @@ -439,6 +439,8 @@ int ISOFileSystem::Ioctl(u32 handle, u32 cmd, u32 indataPtr, u32 inlen, u32 outd PSPDevType ISOFileSystem::DevType(u32 handle) { EntryMap::iterator iter = entries.find(handle); + if (iter == entries.end()) + return PSPDevType::FILE; PSPDevType type = iter->second.isBlockSectorMode ? PSPDevType::BLOCK : PSPDevType::FILE; if (iter->second.isRawSector) type |= PSPDevType::EMU_LBN; diff --git a/Core/FileSystems/ISOFileSystem.h b/Core/FileSystems/ISOFileSystem.h index 273d845301..1941cc998c 100644 --- a/Core/FileSystems/ISOFileSystem.h +++ b/Core/FileSystems/ISOFileSystem.h @@ -48,7 +48,6 @@ public: size_t WriteFile(u32 handle, const u8 *pointer, s64 size) override; size_t WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec) override; - bool GetHostPath(const std::string &inpath, std::string &outpath) override {return false;} bool MkDir(const std::string &dirname) override {return false;} bool RmDir(const std::string &dirname) override { return false; } int RenameFile(const std::string &from, const std::string &to) override { return -1; } @@ -146,7 +145,6 @@ public: size_t WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec) override { return isoFileSystem_->WriteFile(handle, pointer, size, usec); } - bool GetHostPath(const std::string &inpath, std::string &outpath) override { return false; } bool MkDir(const std::string &dirname) override { return false; } bool RmDir(const std::string &dirname) override { return false; } int RenameFile(const std::string &from, const std::string &to) override { return -1; } diff --git a/Core/FileSystems/MetaFileSystem.cpp b/Core/FileSystems/MetaFileSystem.cpp index c7f3b46c1c..58ed10f31e 100644 --- a/Core/FileSystems/MetaFileSystem.cpp +++ b/Core/FileSystems/MetaFileSystem.cpp @@ -377,19 +377,6 @@ PSPFileInfo MetaFileSystem::GetFileInfo(std::string filename) } } -bool MetaFileSystem::GetHostPath(const std::string &inpath, std::string &outpath) -{ - std::lock_guard guard(lock); - std::string of; - IFileSystem *system; - int error = MapFilePath(inpath, of, &system); - if (error == 0) { - return system->GetHostPath(of, outpath); - } else { - return false; - } -} - std::vector MetaFileSystem::GetDirListing(std::string path) { std::lock_guard guard(lock); diff --git a/Core/FileSystems/MetaFileSystem.h b/Core/FileSystems/MetaFileSystem.h index 5d3984349a..a1152e1abd 100644 --- a/Core/FileSystems/MetaFileSystem.h +++ b/Core/FileSystems/MetaFileSystem.h @@ -93,9 +93,6 @@ public: std::string NormalizePrefix(std::string prefix) const; - // Only possible if a file system is a DirectoryFileSystem or similar. - bool GetHostPath(const std::string &inpath, std::string &outpath) override; - std::vector GetDirListing(std::string path) override; int OpenFile(std::string filename, FileAccess access, const char *devicename = nullptr) override; void CloseFile(u32 handle) override; @@ -106,8 +103,7 @@ public: size_t SeekFile(u32 handle, s32 position, FileMove type) override; PSPFileInfo GetFileInfo(std::string filename) override; bool OwnsHandle(u32 handle) override { return false; } - inline size_t GetSeekPos(u32 handle) - { + inline size_t GetSeekPos(u32 handle) { return SeekFile(handle, 0, FILEMOVE_CURRENT); } diff --git a/Core/FileSystems/VirtualDiscFileSystem.cpp b/Core/FileSystems/VirtualDiscFileSystem.cpp index 0805688d88..90bd14b1e7 100644 --- a/Core/FileSystems/VirtualDiscFileSystem.cpp +++ b/Core/FileSystems/VirtualDiscFileSystem.cpp @@ -25,6 +25,7 @@ #include #include "Common/File/FileUtil.h" +#include "Common/File/DirListing.h" #include "Common/StringUtils.h" #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" @@ -49,17 +50,8 @@ const std::string INDEX_FILENAME = ".ppsspp-index.lst"; -VirtualDiscFileSystem::VirtualDiscFileSystem(IHandleAllocator *_hAlloc, std::string _basePath) - : basePath(_basePath),currentBlockIndex(0) { - -#ifdef _WIN32 - if (!endsWith(basePath, "\\") && !endsWith(basePath, "/")) - basePath = basePath + "\\"; -#else - if (!endsWith(basePath, "/")) - basePath = basePath + "/"; -#endif - +VirtualDiscFileSystem::VirtualDiscFileSystem(IHandleAllocator *_hAlloc, const Path &_basePath) + : basePath(_basePath), currentBlockIndex(0) { hAlloc = _hAlloc; LoadFileListIndex(); } @@ -76,24 +68,24 @@ VirtualDiscFileSystem::~VirtualDiscFileSystem() { } void VirtualDiscFileSystem::LoadFileListIndex() { - const std::string filename = basePath + INDEX_FILENAME; + const Path filename = basePath / INDEX_FILENAME; if (!File::Exists(filename)) { return; } - std::ifstream in; - in.open(filename.c_str(), std::ios::in); - if (in.fail()) { + FILE *f = File::OpenCFile(filename, "r"); + if (!f) { return; } std::string buf; - static const int MAX_LINE_SIZE = 1024; - while (!in.eof()) { - buf.resize(MAX_LINE_SIZE, '\0'); - in.getline(&buf[0], MAX_LINE_SIZE); - - std::string line = buf.data(); + static const int MAX_LINE_SIZE = 2048; + char linebuf[MAX_LINE_SIZE]{}; + while (fgets(linebuf, MAX_LINE_SIZE, f)) { + std::string line = linebuf; + // Strip newline from fgets. + if (!line.empty() && line.back() == '\n') + line.resize(line.size() - 1); // Ignore any UTF-8 BOM. if (line.substr(0, 3) == "\xEF\xBB\xBF") { @@ -143,7 +135,7 @@ void VirtualDiscFileSystem::LoadFileListIndex() { entry.firstBlock = strtol(line.c_str(), NULL, 16); if (entry.handler != NULL && entry.handler->IsValid()) { HandlerFileHandle temp = entry.handler; - if (temp.Open(basePath, entry.fileName, FILEACCESS_READ)) { + if (temp.Open(basePath.ToString(), entry.fileName, FILEACCESS_READ)) { entry.totalSize = (u32)temp.Seek(0, FILEMOVE_END); temp.Close(); } else { @@ -162,7 +154,7 @@ void VirtualDiscFileSystem::LoadFileListIndex() { fileList.push_back(entry); } - in.close(); + fclose(f); } void VirtualDiscFileSystem::DoState(PointerWrap &p) @@ -247,7 +239,7 @@ void VirtualDiscFileSystem::DoState(PointerWrap &p) // We don't savestate handlers (loaded on fs load), but if they change, it may not load properly. } -std::string VirtualDiscFileSystem::GetLocalPath(std::string localpath) { +Path VirtualDiscFileSystem::GetLocalPath(std::string localpath) { if (localpath.empty()) return basePath; @@ -260,7 +252,7 @@ std::string VirtualDiscFileSystem::GetLocalPath(std::string localpath) { localpath[i] = '\\'; } #endif - return basePath + localpath; + return basePath / localpath; } int VirtualDiscFileSystem::getFileListIndex(std::string &fileName) @@ -279,10 +271,10 @@ int VirtualDiscFileSystem::getFileListIndex(std::string &fileName) } // unknown file - add it - std::string fullName = GetLocalPath(fileName); + Path fullName = GetLocalPath(fileName); if (! File::Exists(fullName)) { #if HOST_IS_CASE_SENSITIVE - if (! FixPathCase(basePath,fileName, FPC_FILE_MUST_EXIST)) + if (! FixPathCase(basePath.ToString(), fileName, FPC_FILE_MUST_EXIST)) return -1; fullName = GetLocalPath(fileName); @@ -561,6 +553,8 @@ int VirtualDiscFileSystem::Ioctl(u32 handle, u32 cmd, u32 indataPtr, u32 inlen, PSPDevType VirtualDiscFileSystem::DevType(u32 handle) { EntryMap::iterator iter = entries.find(handle); + if (iter == entries.end()) + return PSPDevType::FILE; PSPDevType type = iter->second.type == VFILETYPE_ISO ? PSPDevType::BLOCK : PSPDevType::FILE; if (iter->second.type == VFILETYPE_LBN) type |= PSPDevType::EMU_LBN; @@ -596,7 +590,7 @@ PSPFileInfo VirtualDiscFileSystem::GetFileInfo(std::string filename) { x.access = 0555; HandlerFileHandle temp = fileList[fileIndex].handler; - if (temp.Open(basePath, filename, FILEACCESS_READ)) { + if (temp.Open(basePath.ToString(), filename, FILEACCESS_READ)) { x.exists = true; x.size = temp.Seek(0, FILEMOVE_END); temp.Close(); @@ -606,10 +600,10 @@ PSPFileInfo VirtualDiscFileSystem::GetFileInfo(std::string filename) { return x; } - std::string fullName = GetLocalPath(filename); - if (! File::Exists(fullName)) { + Path fullName = GetLocalPath(filename); + if (!File::Exists(fullName)) { #if HOST_IS_CASE_SENSITIVE - if (! FixPathCase(basePath,filename, FPC_FILE_MUST_EXIST)) + if (! FixPathCase(basePath.ToString(), filename, FPC_FILE_MUST_EXIST)) return x; fullName = GetLocalPath(filename); @@ -629,9 +623,9 @@ PSPFileInfo VirtualDiscFileSystem::GetFileInfo(std::string filename) { } if (x.type != FILETYPE_DIRECTORY) { - File::FileDetails details; - if (!File::GetFileDetails(fullName, &details)) { - ERROR_LOG(FILESYS, "DirectoryFileSystem::GetFileInfo: GetFileDetails failed: %s", fullName.c_str()); + File::FileInfo details; + if (!File::GetFileInfo(fullName, &details)) { + ERROR_LOG(FILESYS, "DirectoryFileSystem::GetFileInfo: GetFileInfo failed: %s", fullName.c_str()); x.size = 0; x.access = 0; } else { @@ -652,12 +646,6 @@ PSPFileInfo VirtualDiscFileSystem::GetFileInfo(std::string filename) { return x; } -bool VirtualDiscFileSystem::GetHostPath(const std::string &inpath, std::string &outpath) -{ - ERROR_LOG(FILESYS,"VirtualDiscFileSystem: Retrieving host path"); - return false; -} - #ifdef _WIN32 #define FILETIME_FROM_UNIX_EPOCH_US 11644473600000000ULL @@ -680,9 +668,9 @@ std::vector VirtualDiscFileSystem::GetDirListing(std::string path) // TODO: Handler files that are virtual might not be listed. - std::string w32path = GetLocalPath(path) + "\\*.*"; + std::wstring w32path = GetLocalPath(path).ToWString() + L"\\*.*"; - hFind = FindFirstFileEx(ConvertUTF8ToWString(w32path).c_str(), FindExInfoStandard, &findData, FindExSearchNameMatch, NULL, 0); + hFind = FindFirstFileEx(w32path.c_str(), FindExInfoStandard, &findData, FindExSearchNameMatch, NULL, 0); if (hFind == INVALID_HANDLE_VALUE) { return myVector; //the empty list @@ -717,11 +705,11 @@ std::vector VirtualDiscFileSystem::GetDirListing(std::string path) FindClose(hFind); #else dirent *dirp; - std::string localPath = GetLocalPath(path); + Path localPath = GetLocalPath(path); DIR *dp = opendir(localPath.c_str()); #if HOST_IS_CASE_SENSITIVE - if(dp == NULL && FixPathCase(basePath,path, FPC_FILE_MUST_EXIST)) { + if(dp == NULL && FixPathCase(basePath.ToString(), path, FPC_FILE_MUST_EXIST)) { // May have failed due to case sensitivity, try again localPath = GetLocalPath(path); dp = opendir(localPath.c_str()); @@ -740,7 +728,7 @@ std::vector VirtualDiscFileSystem::GetDirListing(std::string path) PSPFileInfo entry; struct stat s; - std::string fullName = GetLocalPath(path) + "/"+dirp->d_name; + std::string fullName = (GetLocalPath(path) / std::string(dirp->d_name)).ToString(); stat(fullName.c_str(), &s); if (S_ISDIR(s.st_mode)) entry.type = FILETYPE_DIRECTORY; diff --git a/Core/FileSystems/VirtualDiscFileSystem.h b/Core/FileSystems/VirtualDiscFileSystem.h index cdf6a54ccd..bd8604ae3d 100644 --- a/Core/FileSystems/VirtualDiscFileSystem.h +++ b/Core/FileSystems/VirtualDiscFileSystem.h @@ -21,12 +21,13 @@ #include +#include "Common/File/Path.h" #include "Core/FileSystems/FileSystem.h" #include "Core/FileSystems/DirectoryFileSystem.h" class VirtualDiscFileSystem: public IFileSystem { public: - VirtualDiscFileSystem(IHandleAllocator *_hAlloc, std::string _basePath); + VirtualDiscFileSystem(IHandleAllocator *_hAlloc, const Path &_basePath); ~VirtualDiscFileSystem(); void DoState(PointerWrap &p) override; @@ -39,7 +40,6 @@ public: bool OwnsHandle(u32 handle) override; int Ioctl(u32 handle, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen, int &usec) override; PSPDevType DevType(u32 handle) override; - bool GetHostPath(const std::string &inpath, std::string &outpath) override; std::vector GetDirListing(std::string path) override; FileSystemFlags Flags() override { return FileSystemFlags::UMD; } u64 FreeSpace(const std::string &path) override { return 0; } @@ -57,7 +57,7 @@ private: // Warning: modifies input string. int getFileListIndex(std::string &fileName); int getFileListIndex(u32 accessBlock, u32 accessSize, bool blockMode = false); - std::string GetLocalPath(std::string localpath); + Path GetLocalPath(std::string localpath); typedef void *HandlerLibrary; typedef int HandlerHandle; @@ -99,7 +99,7 @@ private: HandlerFileHandle(Handler *handler_) : handler(handler_), handle(-1) { } - bool Open(std::string& basePath, std::string& fileName, FileAccess access) { + bool Open(const std::string& basePath, const std::string& fileName, FileAccess access) { // Ignore access, read only. handle = handler->Open(basePath.c_str(), fileName.c_str()); return handle > 0; @@ -135,11 +135,11 @@ private: u64 startOffset; // only used by lbn files u64 size; // only used by lbn files - bool Open(std::string& basePath, std::string& fileName, FileAccess access) { + bool Open(const Path &basePath, std::string& fileName, FileAccess access) { // Ignored, we're read only. u32 err; if (handler.IsValid()) { - return handler.Open(basePath, fileName, access); + return handler.Open(basePath.ToString(), fileName, access); } else { return hFile.Open(basePath, fileName, access, err); } @@ -170,7 +170,7 @@ private: typedef std::map EntryMap; EntryMap entries; IHandleAllocator *hAlloc; - std::string basePath; + Path basePath; struct FileListEntry { std::string fileName; diff --git a/Core/HLE/Plugins.cpp b/Core/HLE/Plugins.cpp index 95e6c0efa5..1647c6d55a 100644 --- a/Core/HLE/Plugins.cpp +++ b/Core/HLE/Plugins.cpp @@ -82,17 +82,18 @@ static PluginInfo ReadPluginIni(const std::string &subdir, IniFile &ini) { } static std::vector FindPlugins(const std::string &gameID, const std::string &lang) { - std::vector pluginDirs; - getFilesInDir(GetSysDirectory(DIRECTORY_PLUGINS).c_str(), &pluginDirs); + std::vector pluginDirs; + GetFilesInDir(GetSysDirectory(DIRECTORY_PLUGINS), &pluginDirs); std::vector found; - for (auto subdir : pluginDirs) { - if (!subdir.isDirectory || !File::Exists(subdir.fullName + "/plugin.ini")) + for (const auto &subdir : pluginDirs) { + const Path &subdirFullName = subdir.fullName; + if (!subdir.isDirectory || !File::Exists(subdirFullName / "plugin.ini")) continue; IniFile ini; - if (!ini.Load(subdir.fullName + "/plugin.ini")) { - ERROR_LOG(SYSTEM, "Failed to load plugin ini: %s/plugin.ini", subdir.fullName.c_str()); + if (!ini.Load(subdirFullName / "plugin.ini")) { + ERROR_LOG(SYSTEM, "Failed to load plugin ini: %s/plugin.ini", subdirFullName.c_str()); continue; } @@ -117,8 +118,8 @@ static std::vector FindPlugins(const std::string &gameID, const std: std::set langMatches; for (const std::string &subini : matches) { - if (!ini.Load(subdir.fullName + "/" + subini)) { - ERROR_LOG(SYSTEM, "Failed to load plugin ini: %s/%s", subdir.fullName.c_str(), subini.c_str()); + if (!ini.Load(subdirFullName / subini)) { + ERROR_LOG(SYSTEM, "Failed to load plugin ini: %s/%s", subdirFullName.c_str(), subini.c_str()); continue; } @@ -132,8 +133,8 @@ static std::vector FindPlugins(const std::string &gameID, const std: } for (const std::string &subini : langMatches) { - if (!ini.Load(subdir.fullName + "/" + subini)) { - ERROR_LOG(SYSTEM, "Failed to load plugin ini: %s/%s", subdir.fullName.c_str(), subini.c_str()); + if (!ini.Load(subdirFullName / subini)) { + ERROR_LOG(SYSTEM, "Failed to load plugin ini: %s/%s", subdirFullName.c_str(), subini.c_str()); continue; } diff --git a/Core/HLE/ReplaceTables.cpp b/Core/HLE/ReplaceTables.cpp index 992c893971..efde566c56 100644 --- a/Core/HLE/ReplaceTables.cpp +++ b/Core/HLE/ReplaceTables.cpp @@ -156,8 +156,9 @@ static int Replace_memcpy() { } RETURN(destPtr); - NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, "ReplaceMemcpy"); - NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, "ReplaceMemcpy"); + const std::string tag = "ReplaceMemcpy/" + GetMemWriteTagAt(srcPtr, bytes); + NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, tag.c_str(), tag.size()); return 10 + bytes / 4; // approximation } @@ -198,8 +199,9 @@ static int Replace_memcpy_jak() { currentMIPS->r[MIPS_REG_A3] = destPtr + bytes; RETURN(destPtr); - NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, "ReplaceMemcpy"); - NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, "ReplaceMemcpy"); + const std::string tag = "ReplaceMemcpy/" + GetMemWriteTagAt(srcPtr, bytes); + NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, tag.c_str(), tag.size()); return 5 + bytes * 8 + 2; // approximation. This is a slow memcpy - a byte copy loop.. } @@ -226,8 +228,9 @@ static int Replace_memcpy16() { } RETURN(destPtr); - NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, "ReplaceMemcpy16"); - NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, "ReplaceMemcpy16"); + const std::string tag = "ReplaceMemcpy16/" + GetMemWriteTagAt(srcPtr, bytes); + NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, tag.c_str(), tag.size()); return 10 + bytes / 4; // approximation } @@ -264,8 +267,9 @@ static int Replace_memcpy_swizzled() { RETURN(0); - NotifyMemInfo(MemBlockFlags::READ, srcPtr, pitch * h, "ReplaceMemcpySwizzle"); - NotifyMemInfo(MemBlockFlags::WRITE, destPtr, pitch * h, "ReplaceMemcpySwizzle"); + const std::string tag = "ReplaceMemcpySwizzle/" + GetMemWriteTagAt(srcPtr, pitch * h); + NotifyMemInfo(MemBlockFlags::READ, srcPtr, pitch * h, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, destPtr, pitch * h, tag.c_str(), tag.size()); return 10 + (pitch * h) / 4; // approximation } @@ -292,8 +296,9 @@ static int Replace_memmove() { } RETURN(destPtr); - NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, "ReplaceMemmove"); - NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, "ReplaceMemmove"); + const std::string tag = "ReplaceMemmove/" + GetMemWriteTagAt(srcPtr, bytes); + NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, tag.c_str(), tag.size()); return 10 + bytes / 4; // approximation } diff --git a/Core/HLE/__sceAudio.cpp b/Core/HLE/__sceAudio.cpp index 1f722d11d1..9db3237a74 100644 --- a/Core/HLE/__sceAudio.cpp +++ b/Core/HLE/__sceAudio.cpp @@ -19,6 +19,7 @@ #include #include "Common/CommonTypes.h" +#include "Common/File/Path.h" #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" #include "Common/Data/Collections/FixedSizeQueue.h" @@ -429,7 +430,7 @@ void __AudioUpdate(bool resetRecording) { if (g_Config.bSaveLoadResetsAVdumping && resetRecording) { __StopLogAudio(); std::string discID = g_paramSFO.GetDiscID(); - std::string audio_file_name = StringFromFormat("%s%s_%s.wav", GetSysDirectory(DIRECTORY_AUDIO).c_str(), discID.c_str(), KernelTimeNowFormatted().c_str()).c_str(); + Path audio_file_name = GetSysDirectory(DIRECTORY_AUDIO) / StringFromFormat("%s_%s.wav", discID.c_str(), KernelTimeNowFormatted().c_str()).c_str(); INFO_LOG(COMMON, "Restarted audio recording to: %s", audio_file_name.c_str()); if (!File::Exists(GetSysDirectory(DIRECTORY_AUDIO))) File::CreateDir(GetSysDirectory(DIRECTORY_AUDIO)); @@ -440,7 +441,7 @@ void __AudioUpdate(bool resetRecording) { if (g_Config.bDumpAudio) { // Use gameID_EmulatedTimestamp for filename std::string discID = g_paramSFO.GetDiscID(); - std::string audio_file_name = StringFromFormat("%s%s_%s.wav", GetSysDirectory(DIRECTORY_AUDIO).c_str(), discID.c_str(), KernelTimeNowFormatted().c_str()).c_str(); + Path audio_file_name = GetSysDirectory(DIRECTORY_AUDIO) / StringFromFormat("%s_%s.wav", discID.c_str(), KernelTimeNowFormatted().c_str()); INFO_LOG(COMMON,"Recording audio to: %s", audio_file_name.c_str()); // Create the path just in case it doesn't exist if (!File::Exists(GetSysDirectory(DIRECTORY_AUDIO))) @@ -480,7 +481,7 @@ void __PushExternalAudio(const s32 *audio, int numSamples) { } } #ifndef MOBILE_DEVICE -void __StartLogAudio(const std::string& filename) { +void __StartLogAudio(const Path& filename) { if (!m_logAudio) { m_logAudio = true; g_wave_writer.Start(filename, 44100); diff --git a/Core/HLE/__sceAudio.h b/Core/HLE/__sceAudio.h index f6619ece7d..ae9ca35126 100644 --- a/Core/HLE/__sceAudio.h +++ b/Core/HLE/__sceAudio.h @@ -17,6 +17,8 @@ #pragma once +#include "Common/File/Path.h" + #include "sceAudio.h" struct AudioDebugStats { @@ -49,7 +51,7 @@ void __AudioGetDebugStats(char *buf, size_t bufSize); void __PushExternalAudio(const s32 *audio, int numSamples); // Should not be used in-game, only at the menu! // Audio Dumping stuff -void __StartLogAudio(const std::string& filename); +void __StartLogAudio(const Path &filename); void __StopLogAudio(); class WAVDump diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index 98891072eb..aec5f8a894 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -85,6 +85,7 @@ SceNetAdhocctlPeerInfo * friends = NULL; SceNetAdhocctlScanInfo * networks = NULL; SceNetAdhocctlScanInfo * newnetworks = NULL; u64 adhocctlStartTime = 0; +bool isAdhocctlNeedLogin = false; bool isAdhocctlBusy = false; int adhocctlState = ADHOCCTL_STATE_DISCONNECTED; int adhocctlCurrentMode = ADHOCCTL_MODE_NONE; @@ -1324,7 +1325,7 @@ std::vector getChatLog() { } int friendFinder(){ - setCurrentThreadName("FriendFinder"); + SetCurrentThreadName("FriendFinder"); auto n = GetI18NCategory("Networking"); // Receive Buffer int rxpos = 0; @@ -1371,7 +1372,7 @@ int friendFinder(){ //_acquireNetworkLock(); // Reconnect when disconnected while Adhocctl is still inited - if (metasocket == (int)INVALID_SOCKET && netAdhocctlInited) { + if (metasocket == (int)INVALID_SOCKET && netAdhocctlInited && isAdhocctlNeedLogin) { if (g_Config.bEnableWlan) { if (initNetwork(&product_code) == 0) { networkInited = true; @@ -1389,6 +1390,8 @@ int friendFinder(){ } } } + // Prevent retrying to Login again unless it was on demand + isAdhocctlNeedLogin = false; if (networkInited) { // Ping Server @@ -1897,6 +1900,10 @@ bool isPrivateIP(uint32_t ip) { return false; } +bool isLoopbackIP(uint32_t ip) { + return ((uint8_t*)&ip)[0] == 0x7f; +} + void getLocalMac(SceNetEtherAddr * addr){ // Read MAC Address from config uint8_t mac[ETHER_ADDR_LEN] = {0}; @@ -2143,8 +2150,9 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ // If Server is at localhost Try to Bind socket to specific adapter before connecting to prevent 2nd instance being recognized as already existing 127.0.0.1 by AdhocServer // (may not works in WinXP/2003 for IPv4 due to "Weak End System" model) - if (((uint8_t*)&g_adhocServerIP.in.sin_addr.s_addr)[0] == 0x7f) { // (serverIp.S_un.S_un_b.s_b1 == 0x7f) + if (isLoopbackIP(g_adhocServerIP.in.sin_addr.s_addr)) { int on = 1; + // Not sure what is this SO_DONTROUTE supposed to fix, but i do remembered there were issue related to multiple-instances without SO_DONTROUTE, but forgot how to reproduce it :( setsockopt(metasocket, SOL_SOCKET, SO_DONTROUTE, (const char*)&on, sizeof(on)); setSockReuseAddrPort(metasocket); @@ -2194,7 +2202,7 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ } if (IsSocketReady(metasocket, false, true) <= 0) { ERROR_LOG(SCENET, "Socket error (%i) when connecting to AdhocServer [%s/%s:%u]", errorcode, g_Config.proAdhocServer.c_str(), ip2str(g_adhocServerIP.in.sin_addr).c_str(), ntohs(g_adhocServerIP.in.sin_port)); - host->NotifyUserMessage(n->T("Failed to connect to Adhoc Server"), 1.0f, 0x0000ff); + host->NotifyUserMessage(std::string(n->T("Failed to connect to Adhoc Server")) + " (" + std::string(n->T("Error")) + ": " + std::to_string(errorcode) + ")", 1.0f, 0x0000ff); return iResult; } } diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index dc7d0e8996..4b11dee1c4 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -931,6 +931,7 @@ extern bool friendFinderRunning; extern SceNetAdhocctlPeerInfo * friends; extern SceNetAdhocctlScanInfo * networks; extern u64 adhocctlStartTime; +extern bool isAdhocctlNeedLogin; extern bool isAdhocctlBusy; extern int adhocctlState; extern int adhocctlCurrentMode; @@ -1283,6 +1284,11 @@ uint32_t getLocalIp(int sock); */ bool isPrivateIP(uint32_t ip); +/* + * Check if an IP (big-endian/network order) is Loopback IP + */ +bool isLoopbackIP(uint32_t ip); + /* * Get Number of bytes available in buffer to be Received * @param sock fd diff --git a/Core/HLE/proAdhocServer.cpp b/Core/HLE/proAdhocServer.cpp index 2734978cb6..bead86c49c 100644 --- a/Core/HLE/proAdhocServer.cpp +++ b/Core/HLE/proAdhocServer.cpp @@ -1444,7 +1444,7 @@ void game_product_override(SceNetAdhocctlProductCode * product) void update_status() { // Open Logfile - FILE * log = File::OpenCFile(SERVER_STATUS_XMLOUT, "w"); + FILE * log = File::OpenCFile(Path(SERVER_STATUS_XMLOUT), "w"); // Opened Logfile if(log != NULL) @@ -1696,7 +1696,7 @@ const char * strcpyxml(char * out, const char * in, uint32_t size) */ int proAdhocServerThread(int port) // (int argc, char * argv[]) { - setCurrentThreadName("AdhocServer"); + SetCurrentThreadName("AdhocServer"); // Result int result = 0; diff --git a/Core/HLE/sceAtrac.cpp b/Core/HLE/sceAtrac.cpp index 8bcf49d003..f45e1c4c4c 100644 --- a/Core/HLE/sceAtrac.cpp +++ b/Core/HLE/sceAtrac.cpp @@ -153,7 +153,7 @@ struct InputBuffer { struct Atrac; int __AtracSetContext(Atrac *atrac); -void _AtracGenerateContext(Atrac *atrac, SceAtracId *context); +void _AtracGenerateContext(Atrac *atrac); struct AtracLoopInfo { int cuePointID; @@ -520,6 +520,15 @@ struct Atrac { currentSample_ = sample; } + uint32_t CurBufferAddress(int adjust = 0) { + u32 off = FileOffsetBySample(currentSample_ + adjust); + if (off < first_.size && ignoreDataBuf_) { + return first_.addr + off; + } + // If it's in dataBug, it's not in PSP memory. + return 0; + } + bool FillPacket(int adjust = 0) { u32 off = FileOffsetBySample(currentSample_ + adjust); if (off < first_.size) { @@ -1031,7 +1040,7 @@ u32 _AtracAddStreamData(int atracID, u32 bufPtr, u32 bytesToAdd) { if (!atrac) return 0; int addbytes = std::min(bytesToAdd, atrac->first_.filesize - atrac->first_.fileoffset); - Memory::Memcpy(atrac->dataBuf_ + atrac->first_.fileoffset, bufPtr, addbytes); + Memory::Memcpy(atrac->dataBuf_ + atrac->first_.fileoffset, bufPtr, addbytes, "AtracAddStreamData"); atrac->first_.size += bytesToAdd; if (atrac->first_.size >= atrac->first_.filesize) { atrac->first_.size = atrac->first_.filesize; @@ -1041,7 +1050,7 @@ u32 _AtracAddStreamData(int atracID, u32 bufPtr, u32 bytesToAdd) { atrac->first_.fileoffset += addbytes; if (atrac->context_.IsValid()) { // refresh context_ - _AtracGenerateContext(atrac, atrac->context_); + _AtracGenerateContext(atrac); } return 0; } @@ -1142,7 +1151,7 @@ static u32 sceAtracAddStreamData(int atracID, u32 bytesToAdd) { atrac->first_.fileoffset = readOffset; int addbytes = std::min(bytesToAdd, atrac->first_.filesize - atrac->first_.fileoffset); if (!atrac->ignoreDataBuf_) { - Memory::Memcpy(atrac->dataBuf_ + atrac->first_.fileoffset, atrac->first_.addr + atrac->first_.offset, addbytes); + Memory::Memcpy(atrac->dataBuf_ + atrac->first_.fileoffset, atrac->first_.addr + atrac->first_.offset, addbytes, "AtracAddStreamData"); } atrac->first_.fileoffset += addbytes; } @@ -1152,7 +1161,7 @@ static u32 sceAtracAddStreamData(int atracID, u32 bytesToAdd) { if (atrac->bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER) atrac->bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED; if (atrac->context_.IsValid()) { - _AtracGenerateContext(atrac, atrac->context_); + _AtracGenerateContext(atrac); } } @@ -1208,6 +1217,8 @@ u32 _AtracDecodeData(int atracID, u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u3 AtracDecodeResult res = ATDECODE_FEEDME; while (atrac->FillPacket(-skipSamples)) { + uint32_t packetAddr = atrac->CurBufferAddress(-skipSamples); + int packetSize = atrac->packet_->size; res = atrac->DecodePacket(); if (res == ATDECODE_FAILED) { *SamplesNum = 0; @@ -1246,7 +1257,13 @@ u32 _AtracDecodeData(int atracID, u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u3 int avret = swr_convert(atrac->swrCtx_, &out, numSamples, inbuf, numSamples); if (outbufPtr != 0) { u32 outBytes = numSamples * atrac->outputChannels_ * sizeof(s16); - NotifyMemInfo(MemBlockFlags::WRITE, outbufPtr, outBytes, "AtracDecode"); + if (packetAddr != 0 && MemBlockInfoDetailed()) { + const std::string tag = "AtracDecode/" + GetMemWriteTagAt(packetAddr, packetSize); + NotifyMemInfo(MemBlockFlags::READ, packetAddr, packetSize, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, outbufPtr, outBytes, tag.c_str(), tag.size()); + } else { + NotifyMemInfo(MemBlockFlags::WRITE, outbufPtr, outBytes, "AtracDecode"); + } } if (avret < 0) { ERROR_LOG(ME, "swr_convert: Error while converting %d", avret); @@ -1314,7 +1331,7 @@ u32 _AtracDecodeData(int atracID, u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u3 } if (atrac->context_.IsValid()) { // refresh context_ - _AtracGenerateContext(atrac, atrac->context_); + _AtracGenerateContext(atrac); } } @@ -1730,7 +1747,7 @@ static u32 sceAtracResetPlayPosition(int atracID, int sample, int bytesWrittenFi // Okay, it's a valid number of bytes. Let's set them up. if (bytesWrittenFirstBuf != 0) { if (!atrac->ignoreDataBuf_) { - Memory::Memcpy(atrac->dataBuf_ + atrac->first_.size, atrac->first_.addr + atrac->first_.size, bytesWrittenFirstBuf); + Memory::Memcpy(atrac->dataBuf_ + atrac->first_.size, atrac->first_.addr + atrac->first_.size, bytesWrittenFirstBuf, "AtracResetPlayPosition"); } atrac->first_.fileoffset += bytesWrittenFirstBuf; atrac->first_.size += bytesWrittenFirstBuf; @@ -1753,7 +1770,7 @@ static u32 sceAtracResetPlayPosition(int atracID, int sample, int bytesWrittenFi if (bytesWrittenFirstBuf != 0) { if (!atrac->ignoreDataBuf_) { - Memory::Memcpy(atrac->dataBuf_ + atrac->first_.fileoffset, atrac->first_.addr, bytesWrittenFirstBuf); + Memory::Memcpy(atrac->dataBuf_ + atrac->first_.fileoffset, atrac->first_.addr, bytesWrittenFirstBuf, "AtracResetPlayPosition"); } atrac->first_.fileoffset += bytesWrittenFirstBuf; } @@ -1770,7 +1787,7 @@ static u32 sceAtracResetPlayPosition(int atracID, int sample, int bytesWrittenFi } if (atrac->context_.IsValid()) { - _AtracGenerateContext(atrac, atrac->context_); + _AtracGenerateContext(atrac); } return hleDelayResult(hleLogSuccessInfoI(ME, 0), "reset play pos", 3000); @@ -1925,7 +1942,7 @@ static int _AtracSetData(Atrac *atrac, u32 buffer, u32 readSize, u32 bufferSize, atrac->dataBuf_ = new u8[atrac->first_.filesize]; if (!atrac->ignoreDataBuf_) { u32 copybytes = std::min(bufferSize, atrac->first_.filesize); - Memory::Memcpy(atrac->dataBuf_, buffer, copybytes); + Memory::Memcpy(atrac->dataBuf_, buffer, copybytes, "AtracSetData"); } int ret = __AtracSetContext(atrac); if (ret < 0) { @@ -2084,7 +2101,7 @@ static u32 sceAtracSetLoopNum(int atracID, int loopNum) { atrac->loopEndSample_ = atrac->endSample_ + atrac->firstSampleOffset_ + atrac->FirstOffsetExtra(); } if (atrac->context_.IsValid()) { - _AtracGenerateContext(atrac, atrac->context_); + _AtracGenerateContext(atrac); } } return 0; @@ -2292,7 +2309,8 @@ int _AtracGetIDByContext(u32 contextAddr) { return atracID; } -void _AtracGenerateContext(Atrac *atrac, SceAtracId *context) { +void _AtracGenerateContext(Atrac *atrac) { + SceAtracId *context = atrac->context_; context->info.buffer = atrac->first_.addr; context->info.bufferByte = atrac->bufferMaxSize_; context->info.secondBuffer = atrac->second_.addr; @@ -2322,6 +2340,8 @@ void _AtracGenerateContext(Atrac *atrac, SceAtracId *context) { u8 *buf = (u8 *)context; *(u32_le *)(buf + 0xfc) = atrac->atracID_; + + NotifyMemInfo(MemBlockFlags::WRITE, atrac->context_.ptr, sizeof(SceAtracId), "AtracContext"); } static u32 _sceAtracGetContextAddress(int atracID) { @@ -2342,7 +2362,7 @@ static u32 _sceAtracGetContextAddress(int atracID) { else WARN_LOG(ME, "%08x=_sceAtracGetContextAddress(%i)", atrac->context_.ptr, atracID); if (atrac->context_.IsValid()) - _AtracGenerateContext(atrac, atrac->context_); + _AtracGenerateContext(atrac); return atrac->context_.ptr; } @@ -2431,7 +2451,7 @@ static int sceAtracLowLevelInitDecoder(int atracID, u32 paramsAddr) { int ret = __AtracSetContext(atrac); if (atrac->context_.IsValid()) { - _AtracGenerateContext(atrac, atrac->context_); + _AtracGenerateContext(atrac); } if (ret < 0) { diff --git a/Core/HLE/sceDeflt.cpp b/Core/HLE/sceDeflt.cpp index 2f0b6ec469..fdf7b0d55c 100644 --- a/Core/HLE/sceDeflt.cpp +++ b/Core/HLE/sceDeflt.cpp @@ -18,147 +18,63 @@ #include "zlib.h" #include "Common/CommonTypes.h" +#include "Core/Debugger/MemBlockInfo.h" #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" #include "Core/HLE/sceDeflt.h" #include "Core/MemMap.h" -// All the decompress functions are identical with only differing window bits. Possibly should be one function - -static int sceDeflateDecompress(u32 OutBuffer, int OutBufferLength, u32 InBuffer, u32 Crc32Addr) { - DEBUG_LOG(HLE, "sceGzipDecompress(%08x, %x, %08x, %08x)", OutBuffer, OutBufferLength, InBuffer, Crc32Addr); - int err; - uLong crc; - z_stream stream; - u8 *outBufferPtr; - u32_le *crc32AddrPtr = nullptr; - +// All the decompress functions are identical with only differing window bits. +static int CommonDecompress(int windowBits, u32 OutBuffer, int OutBufferLength, u32 InBuffer, u32 Crc32Addr) { if (!Memory::IsValidAddress(OutBuffer) || !Memory::IsValidAddress(InBuffer)) { - ERROR_LOG(HLE, "sceZlibDecompress: Bad address %08x %08x", OutBuffer, InBuffer); - return 0; + return hleLogError(HLE, 0, "bad address"); } - if (Crc32Addr) { - if (!Memory::IsValidAddress(Crc32Addr)) { - ERROR_LOG(HLE, "sceZlibDecompress: Bad address %08x", Crc32Addr); - return 0; - } - crc32AddrPtr = (u32_le *)Memory::GetPointer(Crc32Addr); + + auto crc32Addr = PSPPointer::Create(Crc32Addr); + if (Crc32Addr && !crc32Addr.IsValid()) { + return hleLogError(HLE, 0, "bad crc32 address"); } - outBufferPtr = Memory::GetPointer(OutBuffer); + + z_stream stream{}; + u8 *outBufferPtr = Memory::GetPointer(OutBuffer); stream.next_in = (Bytef*)Memory::GetPointer(InBuffer); - stream.avail_in = (uInt)OutBufferLength; + // We don't know the available length, just let it use as much as it wants. + stream.avail_in = (uInt)Memory::ValidSize(InBuffer, Memory::g_MemorySize); stream.next_out = outBufferPtr; stream.avail_out = (uInt)OutBufferLength; - stream.zalloc = (alloc_func)0; - stream.zfree = (free_func)0; - err = inflateInit2(&stream, -MAX_WBITS); + + int err = inflateInit2(&stream, windowBits); if (err != Z_OK) { - ERROR_LOG(HLE, "sceZlibDecompress: inflateInit2 failed %08x", err); - return 0; + return hleLogError(HLE, 0, "inflateInit2 failed %08x", err); } err = inflate(&stream, Z_FINISH); - if (err != Z_STREAM_END) { - inflateEnd(&stream); - ERROR_LOG(HLE, "sceZlibDecompress: inflate failed %08x", err); - return 0; - } inflateEnd(&stream); - if (crc32AddrPtr) { - crc = crc32(0L, Z_NULL, 0); - *crc32AddrPtr = crc32(crc, outBufferPtr, stream.total_out); + + if (err != Z_STREAM_END) { + return hleLogError(HLE, 0, "inflate failed %08x", err); } - return stream.total_out; + if (crc32Addr.IsValid()) { + uLong crc = crc32(0L, Z_NULL, 0); + *crc32Addr = crc32(crc, outBufferPtr, stream.total_out); + } + + const std::string tag = "sceDeflt/" + GetMemWriteTagAt(InBuffer, stream.total_in); + NotifyMemInfo(MemBlockFlags::READ, InBuffer, stream.total_in, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, OutBuffer, stream.total_out, tag.c_str(), tag.size()); + + return hleLogSuccessI(HLE, stream.total_out); } +static int sceDeflateDecompress(u32 OutBuffer, int OutBufferLength, u32 InBuffer, u32 Crc32Addr) { + return CommonDecompress(-MAX_WBITS, OutBuffer, OutBufferLength, InBuffer, Crc32Addr); +} static int sceGzipDecompress(u32 OutBuffer, int OutBufferLength, u32 InBuffer, u32 Crc32Addr) { - DEBUG_LOG(HLE, "sceGzipDecompress(%08x, %x, %08x, %08x)", OutBuffer, OutBufferLength, InBuffer, Crc32Addr); - int err; - uLong crc; - z_stream stream; - u8 *outBufferPtr; - u32_le *crc32AddrPtr = nullptr; - - if (!Memory::IsValidAddress(OutBuffer) || !Memory::IsValidAddress(InBuffer)) { - ERROR_LOG(HLE, "sceZlibDecompress: Bad address %08x %08x", OutBuffer, InBuffer); - return 0; - } - if (Crc32Addr) { - if (!Memory::IsValidAddress(Crc32Addr)) { - ERROR_LOG(HLE, "sceZlibDecompress: Bad address %08x", Crc32Addr); - return 0; - } - crc32AddrPtr = (u32_le *)Memory::GetPointer(Crc32Addr); - } - outBufferPtr = Memory::GetPointer(OutBuffer); - stream.next_in = (Bytef*)Memory::GetPointer(InBuffer); - stream.avail_in = (uInt)OutBufferLength; - stream.next_out = outBufferPtr; - stream.avail_out = (uInt)OutBufferLength; - stream.zalloc = (alloc_func)0; - stream.zfree = (free_func)0; - err = inflateInit2(&stream, 16+MAX_WBITS); - if (err != Z_OK) { - ERROR_LOG(HLE, "sceZlibDecompress: inflateInit2 failed %08x", err); - return 0; - } - err = inflate(&stream, Z_FINISH); - if (err != Z_STREAM_END) { - inflateEnd(&stream); - ERROR_LOG(HLE, "sceZlibDecompress: inflate failed %08x", err); - return 0; - } - inflateEnd(&stream); - if (crc32AddrPtr) { - crc = crc32(0L, Z_NULL, 0); - *crc32AddrPtr = crc32(crc, outBufferPtr, stream.total_out); - } - return stream.total_out; + return CommonDecompress(16 + MAX_WBITS, OutBuffer, OutBufferLength, InBuffer, Crc32Addr); } static int sceZlibDecompress(u32 OutBuffer, int OutBufferLength, u32 InBuffer, u32 Crc32Addr) { - DEBUG_LOG(HLE, "sceZlibDecompress(%08x, %x, %08x, %08x)", OutBuffer, OutBufferLength, InBuffer, Crc32Addr); - int err; - uLong crc; - z_stream stream; - u8 *outBufferPtr; - u32_le *crc32AddrPtr = 0; - - if (!Memory::IsValidAddress(OutBuffer) || !Memory::IsValidAddress(InBuffer)) { - ERROR_LOG(HLE, "sceZlibDecompress: Bad address %08x %08x", OutBuffer, InBuffer); - return 0; - } - if (Crc32Addr) { - if (!Memory::IsValidAddress(Crc32Addr)) { - ERROR_LOG(HLE, "sceZlibDecompress: Bad address %08x", Crc32Addr); - return 0; - } - crc32AddrPtr = (u32_le *)Memory::GetPointer(Crc32Addr); - } - outBufferPtr = Memory::GetPointer(OutBuffer); - stream.next_in = (Bytef*)Memory::GetPointer(InBuffer); - stream.avail_in = (uInt)OutBufferLength; - stream.next_out = outBufferPtr; - stream.avail_out = (uInt)OutBufferLength; - stream.zalloc = (alloc_func)0; - stream.zfree = (free_func)0; - err = inflateInit2(&stream, MAX_WBITS); - if (err != Z_OK) { - ERROR_LOG(HLE, "sceZlibDecompress: inflateInit failed %08x", err); - return 0; - } - err = inflate(&stream, Z_FINISH); - if (err != Z_STREAM_END) { - inflateEnd(&stream); - ERROR_LOG(HLE, "sceZlibDecompress: inflate failed %08x", err); - return 0; - } - inflateEnd(&stream); - if (crc32AddrPtr) { - crc = crc32(0L, Z_NULL, 0); - *crc32AddrPtr = crc32(crc, outBufferPtr, stream.total_out); - } - return stream.total_out; + return CommonDecompress(MAX_WBITS, OutBuffer, OutBufferLength, InBuffer, Crc32Addr); } const HLEFunction sceDeflt[] = { @@ -166,11 +82,11 @@ const HLEFunction sceDeflt[] = { {0X106A3552, nullptr, "sceGzipGetName", '?', "" }, {0X1B5B82BC, nullptr, "sceGzipIsValid", '?', "" }, {0X2EE39A64, nullptr, "sceZlibAdler32", '?', "" }, - {0X44054E03, &WrapI_UIUU, "sceDeflateDecompress", 'i', "xixx"}, + {0X44054E03, &WrapI_UIUU, "sceDeflateDecompress", 'i', "xixp"}, {0X6A548477, nullptr, "sceZlibGetCompressedData", '?', "" }, - {0X6DBCF897, &WrapI_UIUU, "sceGzipDecompress", 'i', "xixx"}, + {0X6DBCF897, &WrapI_UIUU, "sceGzipDecompress", 'i', "xixp"}, {0X8AA82C92, nullptr, "sceGzipGetInfo", '?', "" }, - {0XA9E4FB28, &WrapI_UIUU, "sceZlibDecompress", 'i', "xixx"}, + {0XA9E4FB28, &WrapI_UIUU, "sceZlibDecompress", 'i', "xixp"}, {0XAFE01FD3, nullptr, "sceZlibGetInfo", '?', "" }, {0XB767F9A0, nullptr, "sceGzipGetComment", '?', "" }, {0XE46EB986, nullptr, "sceZlibIsValid", '?', "" }, diff --git a/Core/HLE/sceDmac.cpp b/Core/HLE/sceDmac.cpp index 4defe951ca..2bd493f016 100644 --- a/Core/HLE/sceDmac.cpp +++ b/Core/HLE/sceDmac.cpp @@ -50,8 +50,9 @@ static int __DmacMemcpy(u32 dst, u32 src, u32 size) { skip = gpu->PerformMemoryCopy(dst, src, size); } if (!skip) { - // TODO: InvalidateICache src before copy? - Memory::Memcpy(dst, Memory::GetPointer(src), size, "DmacMemcpy"); + currentMIPS->InvalidateICache(src, size); + const std::string tag = "DmacMemcpy/" + GetMemWriteTagAt(src, size); + Memory::Memcpy(dst, src, size, tag.c_str(), tag.size()); currentMIPS->InvalidateICache(dst, size); } diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 55ffcff2fe..2c815c27e5 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -26,6 +26,7 @@ #include "Common/Serialize/SerializeFuncs.h" #include "Common/Serialize/SerializeMap.h" #include "Common/Serialize/SerializeSet.h" +#include "Common/StringUtils.h" #include "Core/Core.h" #include "Core/Config.h" #include "Core/ConfigValues.h" @@ -559,7 +560,7 @@ static VFSFileSystem *flash0System = nullptr; #endif static void __IoManagerThread() { - setCurrentThreadName("IO"); + SetCurrentThreadName("IO"); while (ioManagerThreadEnabled && coreState != CORE_BOOT_ERROR && coreState != CORE_RUNTIME_ERROR && coreState != CORE_POWERDOWN) { ioManager.RunEventsUntil(CoreTiming::GetTicks() + msToCycles(1000)); } @@ -638,7 +639,7 @@ void __IoInit() { if (g_RemasterMode) { const std::string gameId = g_paramSFO.GetDiscID(); - const std::string exdataPath = g_Config.memStickDirectory + "exdata/" + gameId + "/"; + const Path exdataPath = g_Config.memStickDirectory / "exdata" / gameId; if (File::Exists(exdataPath)) { exdataSystem = new DirectoryFileSystem(&pspFileSystem, exdataPath, FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD); pspFileSystem.Mount("exdata0:", exdataSystem); @@ -773,10 +774,19 @@ void __IoShutdown() { delete flash0System; flash0System = nullptr; + MemoryStick_Shutdown(); memStickCallbacks.clear(); memStickFatCallbacks.clear(); } +static std::string IODetermineFilename(FileNode *f) { + uint64_t offset = pspFileSystem.GetSeekPos(f->handle); + if ((pspFileSystem.DevType(f->handle) & PSPDevType::BLOCK) != 0) { + return StringFromFormat("%s offset 0x%08llx", f->fullpath.c_str(), offset * 2048); + } + return StringFromFormat("%s offset 0x%08llx", f->fullpath.c_str(), offset); +} + u32 __IoGetFileHandleFromId(u32 id, u32 &outError) { FileNode *f = __IoGetFd(id, outError); @@ -940,11 +950,15 @@ static u32 sceIoGetstat(const char *filename, u32 addr) { } static u32 sceIoChstat(const char *filename, u32 iostatptr, u32 changebits) { + auto iostat = PSPPointer::Create(iostatptr); + if (!iostat.IsValid()) + return hleReportError(SCEIO, SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT, "bad address"); + ERROR_LOG_REPORT(SCEIO, "UNIMPL sceIoChstat(%s, %08x, %08x)", filename, iostatptr, changebits); if (changebits & SCE_CST_MODE) - ERROR_LOG(SCEIO, "sceIoChstat: change mode requested"); + ERROR_LOG_REPORT(SCEIO, "sceIoChstat: change mode to %03o requested", iostat->st_mode); if (changebits & SCE_CST_ATTR) - ERROR_LOG(SCEIO, "sceIoChstat: change attr requested"); + ERROR_LOG_REPORT(SCEIO, "sceIoChstat: change attr to %04x requested", iostat->st_attr); if (changebits & SCE_CST_SIZE) ERROR_LOG(SCEIO, "sceIoChstat: change size requested"); if (changebits & SCE_CST_CT) @@ -952,7 +966,7 @@ static u32 sceIoChstat(const char *filename, u32 iostatptr, u32 changebits) { if (changebits & SCE_CST_AT) ERROR_LOG(SCEIO, "sceIoChstat: change access time requested"); if (changebits & SCE_CST_MT) - ERROR_LOG(SCEIO, "sceIoChstat: change modification time requested"); + ERROR_LOG_REPORT(SCEIO, "sceIoChstat: change modification time to %04d-%02d-%02d requested", iostat->st_m_time.year, iostat->st_m_time.month, iostat->st_m_time.day); if (changebits & SCE_CST_PRVT) ERROR_LOG(SCEIO, "sceIoChstat: change private data requested"); return 0; @@ -1026,7 +1040,8 @@ static bool __IoRead(int &result, int id, u32 data_addr, int size, int &us) { result = SCE_KERNEL_ERROR_ILLEGAL_ADDR; return true; } else if (Memory::IsValidAddress(data_addr)) { - NotifyMemInfo(MemBlockFlags::WRITE, data_addr, size, "IoRead"); + const std::string tag = "IoRead/" + IODetermineFilename(f); + NotifyMemInfo(MemBlockFlags::WRITE, data_addr, size, tag.c_str(), tag.size()); u8 *data = (u8 *)Memory::GetPointer(data_addr); u32 validSize = Memory::ValidSize(data_addr, size); if (f->npdrm) { @@ -1162,7 +1177,8 @@ static bool __IoWrite(int &result, int id, u32 data_addr, int size, int &us) { return true; } - NotifyMemInfo(MemBlockFlags::READ, data_addr, size, "IoWrite"); + const std::string tag = "IoWrite/" + IODetermineFilename(f); + NotifyMemInfo(MemBlockFlags::READ, data_addr, size, tag.c_str(), tag.size()); bool useThread = __KernelIsDispatchEnabled() && ioManagerThreadEnabled && size > IO_THREAD_MIN_DATA_SIZE; if (useThread) { diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 5907848036..d4b4619878 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -913,6 +913,32 @@ const HLEFunction ThreadManForKernel[] = {0x616403ba, &WrapI_I, "sceKernelTerminateThread", 'i', "i" ,HLE_KERNEL_SYSCALL }, {0x383f7bcc, &WrapI_I, "sceKernelTerminateDeleteThread", 'i', "i" ,HLE_KERNEL_SYSCALL }, {0x57cf62dd, &WrapU_U, "sceKernelGetThreadmanIdType", 'x', "x" ,HLE_KERNEL_SYSCALL }, + {0x94aa61ee, &WrapI_V, "sceKernelGetThreadCurrentPriority", 'i', "", HLE_KERNEL_SYSCALL }, + {0x293B45B8, &WrapI_V, "sceKernelGetThreadId", 'i', "", HLE_KERNEL_SYSCALL | HLE_NOT_IN_INTERRUPT }, + {0x3B183E26, &WrapI_I, "sceKernelGetThreadExitStatus", 'i', "i", HLE_KERNEL_SYSCALL }, + {0x82BC5777, &WrapU64_V, "sceKernelGetSystemTimeWide", 'X', "", HLE_KERNEL_SYSCALL }, + {0xDB738F35, &WrapI_U, "sceKernelGetSystemTime", 'i', "x", HLE_KERNEL_SYSCALL }, + {0x369ED59D, &WrapU_V, "sceKernelGetSystemTimeLow", 'x', "", HLE_KERNEL_SYSCALL }, + {0x6652B8CA, &WrapI_UUU, "sceKernelSetAlarm", 'i', "xxx", HLE_KERNEL_SYSCALL }, + {0xB2C25152, &WrapI_UUU, "sceKernelSetSysClockAlarm", 'i', "xxx", HLE_KERNEL_SYSCALL }, + {0x7E65B999, &WrapI_I, "sceKernelCancelAlarm", 'i', "i", HLE_KERNEL_SYSCALL }, + {0xDAA3F564, &WrapI_IU, "sceKernelReferAlarmStatus", 'i', "ix", HLE_KERNEL_SYSCALL }, + {0x8125221D, &WrapI_CUU, "sceKernelCreateMbx", 'i', "sxx", HLE_KERNEL_SYSCALL }, + {0x86255ADA, &WrapI_I, "sceKernelDeleteMbx", 'i', "i", HLE_KERNEL_SYSCALL }, + {0xE9B3061E, &WrapI_IU, "sceKernelSendMbx", 'i', "ix", HLE_KERNEL_SYSCALL }, + {0x18260574, &WrapI_IUU, "sceKernelReceiveMbx", 'i', "ixx", HLE_KERNEL_SYSCALL | HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, + {0xF3986382, &WrapI_IUU, "sceKernelReceiveMbxCB", 'i', "ixx", HLE_KERNEL_SYSCALL | HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, + {0x0D81716A, &WrapI_IU, "sceKernelPollMbx", 'i', "ix", HLE_KERNEL_SYSCALL }, + {0x87D4DD36, &WrapI_IU, "sceKernelCancelReceiveMbx", 'i', "ix", HLE_KERNEL_SYSCALL }, + {0xA8E8C846, &WrapI_IU, "sceKernelReferMbxStatus", 'i', "ix", HLE_KERNEL_SYSCALL }, + {0x56C039B5, &WrapI_CIUUU, "sceKernelCreateVpl", 'i', "sixxx", HLE_KERNEL_SYSCALL }, + {0x89B3D48C, &WrapI_I, "sceKernelDeleteVpl", 'i', "i", HLE_KERNEL_SYSCALL }, + {0xBED27435, &WrapI_IUUU, "sceKernelAllocateVpl", 'i', "ixxx", HLE_KERNEL_SYSCALL | HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, + {0xEC0A693F, &WrapI_IUUU, "sceKernelAllocateVplCB", 'i', "ixxx", HLE_KERNEL_SYSCALL | HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, + {0xAF36D708, &WrapI_IUU, "sceKernelTryAllocateVpl", 'i', "ixx", HLE_KERNEL_SYSCALL }, + {0xB736E9FF, &WrapI_IU, "sceKernelFreeVpl", 'i', "ix", HLE_KERNEL_SYSCALL }, + {0x1D371B8A, &WrapI_IU, "sceKernelCancelVpl", 'i', "ix", HLE_KERNEL_SYSCALL }, + {0x39810265, &WrapI_IU, "sceKernelReferVplStatus", 'i', "ix", HLE_KERNEL_SYSCALL }, }; void Register_ThreadManForUser() diff --git a/Core/HLE/sceKernelInterrupt.cpp b/Core/HLE/sceKernelInterrupt.cpp index adaabd362f..88a3848be5 100644 --- a/Core/HLE/sceKernelInterrupt.cpp +++ b/Core/HLE/sceKernelInterrupt.cpp @@ -15,9 +15,10 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include +#include #include #include +#include #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" @@ -658,8 +659,9 @@ static u32 sceKernelMemcpy(u32 dst, u32 src, u32 size) } } - NotifyMemInfo(MemBlockFlags::READ, src, size, "KernelMemcpy"); - NotifyMemInfo(MemBlockFlags::WRITE, dst, size, "KernelMemcpy"); + const std::string tag = "KernelMemcpy/" + GetMemWriteTagAt(src, size); + NotifyMemInfo(MemBlockFlags::READ, src, size, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, dst, size, tag.c_str(), tag.size()); return dst; } @@ -690,8 +692,9 @@ static u32 sysclib_memcpy(u32 dst, u32 src, u32 size) { if (Memory::IsValidRange(dst, size) && Memory::IsValidRange(src, size)) { memcpy(Memory::GetPointer(dst), Memory::GetPointer(src), size); } - NotifyMemInfo(MemBlockFlags::READ, src, size, "KernelMemcpy"); - NotifyMemInfo(MemBlockFlags::WRITE, dst, size, "KernelMemcpy"); + const std::string tag = "KernelMemcpy/" + GetMemWriteTagAt(src, size); + NotifyMemInfo(MemBlockFlags::READ, src, size, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, dst, size, tag.c_str(), tag.size()); return dst; } @@ -790,11 +793,37 @@ static u32 sysclib_memmove(u32 dst, u32 src, u32 size) { if (Memory::IsValidRange(dst, size) && Memory::IsValidRange(src, size)) { memmove(Memory::GetPointer(dst), Memory::GetPointer(src), size); } - NotifyMemInfo(MemBlockFlags::READ, src, size, "KernelMemmove"); - NotifyMemInfo(MemBlockFlags::WRITE, dst, size, "KernelMemmove"); + const std::string tag = "KernelMemmove/" + GetMemWriteTagAt(src, size); + NotifyMemInfo(MemBlockFlags::READ, src, size, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, dst, size, tag.c_str(), tag.size()); return 0; } +static u32 sysclib_strncpy(u32 dest, u32 src, u32 size) { + if (!Memory::IsValidAddress(dest) || Memory::IsValidAddress(src)) { + return hleLogError(SCEKERNEL, 0, "invalid address"); + } + + // This is just regular strncpy, but being explicit to avoid warnings/safety fixes on missing null. + u32 i = 0; + u32 srcSize = Memory::ValidSize(src, size); + const u8 *srcp = Memory::GetPointer(src); + u8 *destp = Memory::GetPointer(dest); + for (i = 0; i < srcSize; ++i) { + u8 c = *srcp++; + if (c == 0) + break; + *destp++ = c; + } + + u32 destSize = Memory::ValidSize(dest, size); + for (; i < destSize; ++i) { + *destp++ = 0; + } + + return hleLogSuccessX(SCEKERNEL, dest); +} + const HLEFunction SysclibForKernel[] = { {0xAB7592FF, &WrapU_UUU, "memcpy", 'x', "xxx", HLE_KERNEL_SYSCALL }, @@ -806,8 +835,9 @@ const HLEFunction SysclibForKernel[] = {0x7661E728, &WrapI_UU, "sprintf", 'i', "xx", HLE_KERNEL_SYSCALL }, {0x10F3BB61, &WrapU_UII, "memset", 'x', "xii", HLE_KERNEL_SYSCALL }, {0x0D188658, &WrapI_UU, "strstr", 'i', "xx", HLE_KERNEL_SYSCALL }, - {0x7AB35214, &WrapI_UUU, "strncmp", 'i', "xxx", HLE_KERNEL_SYSCALL }, - {0xA48D2592, &WrapU_UUU, "memmove", 'x', "xxx", HLE_KERNEL_SYSCALL }, + {0x7AB35214, &WrapI_UUU, "strncmp", 'i', "xxx", HLE_KERNEL_SYSCALL }, + {0xA48D2592, &WrapU_UUU, "memmove", 'x', "xxx", HLE_KERNEL_SYSCALL }, + {0xB49A7697, &WrapU_UUU, "strncpy", 'x', "xxi", HLE_KERNEL_SYSCALL }, }; void Register_Kernel_Library() diff --git a/Core/HLE/sceKernelMbx.cpp b/Core/HLE/sceKernelMbx.cpp index 29ff672e2a..412c70188d 100644 --- a/Core/HLE/sceKernelMbx.cpp +++ b/Core/HLE/sceKernelMbx.cpp @@ -423,11 +423,10 @@ int sceKernelSendMbx(SceUID id, u32 packetAddr) bool inserted = false; if (m->nmb.attr & SCE_KERNEL_MBA_MSPRI) { - NativeMbxPacket p; for (int i = 0, n = m->nmb.numMessages; i < n; i++) { - Memory::ReadStructUnchecked(next, &p); - if (addPacket->priority < p.priority) + auto p = PSPPointer::Create(next); + if (addPacket->priority < p->priority) { if (i == 0) m->AddFirstMessage(prev, packetAddr); @@ -438,7 +437,7 @@ int sceKernelSendMbx(SceUID id, u32 packetAddr) } prev = next; - next = Memory::Read_U32(next); + next = p->next; } } if (!inserted) diff --git a/Core/HLE/sceKernelMemory.cpp b/Core/HLE/sceKernelMemory.cpp index fa8a80b61f..634eb43f58 100644 --- a/Core/HLE/sceKernelMemory.cpp +++ b/Core/HLE/sceKernelMemory.cpp @@ -24,10 +24,11 @@ #include "Core/Debugger/MemBlockInfo.h" #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" -#include "Core/System.h" #include "Core/MIPS/MIPS.h" #include "Core/MemMapHelpers.h" #include "Core/Reporting.h" +#include "Core/System.h" +#include "Core/ThreadPools.h" #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" #include "Common/Serialize/SerializeMap.h" @@ -430,8 +431,8 @@ void __KernelMemoryInit() MemBlockInfoInit(); kernelMemory.Init(PSP_GetKernelMemoryBase(), PSP_GetKernelMemoryEnd() - PSP_GetKernelMemoryBase(), false); userMemory.Init(PSP_GetUserMemoryBase(), PSP_GetUserMemoryEnd() - PSP_GetUserMemoryBase(), false); - Memory::Memset(PSP_GetKernelMemoryBase(), 0, PSP_GetKernelMemoryEnd() - PSP_GetKernelMemoryBase(), "MemInit"); - Memory::Memset(PSP_GetUserMemoryBase(), 0, PSP_GetUserMemoryEnd() - PSP_GetUserMemoryBase(), "MemInit"); + GlobalThreadPool::Memset(Memory::GetPointer(PSP_GetKernelMemoryBase()), 0, PSP_GetUserMemoryEnd() - PSP_GetKernelMemoryBase()); + NotifyMemInfo(MemBlockFlags::WRITE, PSP_GetKernelMemoryBase(), PSP_GetUserMemoryEnd() - PSP_GetKernelMemoryBase(), "MemInit"); INFO_LOG(SCEKERNEL, "Kernel and user memory pools initialized"); vplWaitTimer = CoreTiming::RegisterEvent("VplTimeout", __KernelVplTimeout); diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index b6b270d916..332379aa13 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -15,7 +15,6 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include #include #include @@ -863,9 +862,11 @@ void PSPModule::Cleanup() { if (memoryBlockAddr != 0 && nm.text_addr != 0 && memoryBlockSize >= nm.data_size + nm.bss_size + nm.text_size) { DEBUG_LOG(LOADER, "Zeroing out module %s memory: %08x - %08x", nm.name, memoryBlockAddr, memoryBlockAddr + memoryBlockSize); - for (u32 i = 0; i < (u32)(nm.text_size + 3); i += 4) { - Memory::Write_U32(MIPS_MAKE_BREAK(1), nm.text_addr + i); + u32 clearSize = Memory::ValidSize(nm.text_addr, (u32)nm.text_size + 3); + for (u32 i = 0; i < clearSize; i += 4) { + Memory::WriteUnchecked_U32(MIPS_MAKE_BREAK(1), nm.text_addr + i); } + NotifyMemInfo(MemBlockFlags::WRITE, nm.text_addr, clearSize, "ModuleClear"); Memory::Memset(nm.text_addr + nm.text_size, -1, nm.data_size + nm.bss_size, "ModuleClear"); // Let's also invalidate, just to make sure it's cleared out for any future data. @@ -885,8 +886,8 @@ static void __SaveDecryptedEbootToStorageMedia(const u8 *decryptedEbootDataPtr, } const std::string filenameToDumpTo = g_paramSFO.GetDiscID() + ".BIN"; - const std::string dumpDirectory = GetSysDirectory(DIRECTORY_DUMP); - const std::string fullPath = dumpDirectory + filenameToDumpTo; + const Path dumpDirectory = GetSysDirectory(DIRECTORY_DUMP); + const Path fullPath = dumpDirectory / filenameToDumpTo; // If the file already exists, don't dump it again. if (File::Exists(fullPath)) { @@ -986,7 +987,7 @@ static bool KernelImportModuleFuncs(PSPModule *module, u32 *firstImportStubAddr, return false; } if (!Memory::IsValidRange(module->libstub, module->libstubend - module->libstub)) { - ERROR_LOG_REPORT(LOADER, "Garbage libstub address or end"); + ERROR_LOG_REPORT(LOADER, "Garbage libstub address %08x or end %08x", module->libstub, module->libstubend); return false; } @@ -1184,6 +1185,8 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load *error_string = StringFromFormat("ELF/PRX truncated: %d > %d", (int)size, (int)elfSize); module->Cleanup(); kernelObjects.Destroy(module->GetUID()); + // TODO: Might be the wrong error code. + error = SCE_KERNEL_ERROR_FILEERR; return nullptr; } const auto maxElfSize = std::max(head->elf_size, head->psp_size); @@ -1266,7 +1269,7 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load ElfReader reader((void*)ptr, elfSize); int result = reader.LoadInto(loadAddress, fromTop); - if (result != SCE_KERNEL_ERROR_OK) { + if (result != SCE_KERNEL_ERROR_OK) { ERROR_LOG(SCEMODULE, "LoadInto failed with error %08x",result); if (newptr) delete [] newptr; @@ -1431,8 +1434,14 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load u8 unknown2; }; + module->nm.ent_top = modinfo->libent; + module->nm.ent_size = modinfo->libentend - modinfo->libent; + module->nm.stub_top = modinfo->libstub; + module->nm.stub_size = modinfo->libstubend - modinfo->libstub; + const u32_le *entPos = (u32_le *)Memory::GetPointer(modinfo->libent); const u32_le *entEnd = (u32_le *)Memory::GetPointer(modinfo->libentend); + for (int m = 0; entPos < entEnd; ++m) { const PspLibEntEntry *ent = (const PspLibEntEntry *)entPos; entPos += ent->size; diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 8d1fbd206d..fe3da314ea 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -962,11 +962,10 @@ static bool decodePmpVideo(PSPPointer ringbuffer, u32 pmpctxA pmpframes = new H264Frames; // joint all blocks into H264Frames - SceMpegLLI lli; for (int i = 0; i < pmp_nBlocks; i++){ - Memory::ReadStructUnchecked(pmp_videoSource, &lli); + auto lli = PSPPointer::Create(pmp_videoSource); // add source block into pmpframes - pmpframes->add(Memory::GetPointer(lli.pSrc), lli.iSize); + pmpframes->add(Memory::GetPointer(lli->pSrc), lli->iSize); // get next block pmp_videoSource += sizeof(SceMpegLLI); } diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index 193a3e04c9..ae7e8af541 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -141,7 +141,7 @@ static void __ApctlState(u64 userdata, int cyclesLate) { SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error); if (waitID == 0 || error != 0) { - WARN_LOG(SCENET, "sceNetApctl State WaitID(%i) on Thread(%i) already woken up? (error: %d)", uid, threadID, error); + WARN_LOG(SCENET, "sceNetApctl State WaitID(%i) on Thread(%i) already woken up? (error: %08x)", uid, threadID, error); return; } @@ -151,7 +151,7 @@ static void __ApctlState(u64 userdata, int cyclesLate) { } __KernelResumeThreadFromWait(threadID, result); - DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %d) Result (%08x) of sceNetApctl - Event: %d, State: %d", waitID, error, (int)result, event, netApctlState); + DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %08x) Result (%08x) of sceNetApctl - Event: %d, State: %d", waitID, error, (int)result, event, netApctlState); } // Used to change Apctl State after a delay and before executing callback mipscall (since we don't have beforeAction) @@ -499,6 +499,12 @@ void __NetApctlCallbacks() break; case PSP_NET_APCTL_EVENT_SCAN_COMPLETE: + newState = PSP_NET_APCTL_STATE_DISCONNECTED; + if (error == 0) + apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_SCAN_STOP, 0 }); + break; + + case PSP_NET_APCTL_EVENT_SCAN_STOP: newState = PSP_NET_APCTL_STATE_DISCONNECTED; break; @@ -1158,7 +1164,7 @@ static int sceNetApctlGetState(u32 pStateAddr) { return hleLogSuccessI(SCENET, 0); } - return hleLogError(SCENET, -1, "apctl invalid arg"); + return hleLogError(SCENET, -1, "apctl invalid arg"); // 0x8002013A or ERROR_NET_WLAN_INVALID_ARG ? } int NetApctl_ScanUser() { @@ -1175,13 +1181,12 @@ static int sceNetApctlScanUser() { return NetApctl_ScanUser(); } -static int sceNetApctlGetBSSDescIDListUser(u32 sizeAddr, u32 bufAddr) { - WARN_LOG(SCENET, "UNTESTED %s(%08x, %08x)", __FUNCTION__, sizeAddr, bufAddr); - - const int userInfoSize = 8; - int entries = 1; - if (!Memory::IsValidAddress(sizeAddr)) - hleLogError(SCENET, -1, "apctl invalid arg"); +int NetApctl_GetBSSDescIDListUser(u32 sizeAddr, u32 bufAddr) { + const int userInfoSize = 8; // 8 bytes per entry (next address + entry id) + // Faking 4 entries, games like MGS:PW Recruit will need to have a different AP for each entry + int entries = 4; + if (!Memory::IsValidAddress(sizeAddr) || !Memory::IsValidAddress(bufAddr)) + return hleLogError(SCENET, -1, "apctl invalid arg"); // 0x8002013A or ERROR_NET_WLAN_INVALID_ARG ? int size = Memory::Read_U32(sizeAddr); // Return size required @@ -1195,10 +1200,10 @@ static int sceNetApctlGetBSSDescIDListUser(u32 sizeAddr, u32 bufAddr) { break; } - DEBUG_LOG(SCENET, "%s returning %d at %08x", __FUNCTION__, i, bufAddr + offset); + DEBUG_LOG(SCENET, "%s writing ID#%d to %08x", __FUNCTION__, i, bufAddr + offset); // Pointer to next Network structure in list - Memory::Write_U32((i+1)*userInfoSize + bufAddr, bufAddr + offset); + Memory::Write_U32((i + 1) * userInfoSize + bufAddr, bufAddr + offset); offset += 4; // Entry ID @@ -1210,30 +1215,69 @@ static int sceNetApctlGetBSSDescIDListUser(u32 sizeAddr, u32 bufAddr) { Memory::Write_U32(0, bufAddr + offset - userInfoSize); } - return hleLogWarning(SCENET, 0, "untested"); + return 0; } -static int sceNetApctlGetBSSDescEntryUser(int entryId, int infoId, u32 resultAddr) { - WARN_LOG(SCENET, "UNTESTED %s(%i, %i, %08x)", __FUNCTION__, entryId, infoId, resultAddr); +static int sceNetApctlGetBSSDescIDListUser(u32 sizeAddr, u32 bufAddr) { + WARN_LOG(SCENET, "UNTESTED %s(%08x, %08x)", __FUNCTION__, sizeAddr, bufAddr); + return NetApctl_GetBSSDescIDListUser(sizeAddr, bufAddr); +} +int NetApctl_GetBSSDescEntryUser(int entryId, int infoId, u32 resultAddr) { if (!Memory::IsValidAddress(resultAddr)) - hleLogError(SCENET, -1, "apctl invalid arg"); + return hleLogError(SCENET, -1, "apctl invalid arg"); // 0x8002013A or ERROR_NET_WLAN_INVALID_ARG ? + + // Generate an SSID name + char dummySSID[APCTL_SSID_MAXLEN] = "WifiAP0"; + dummySSID[6] += static_cast(entryId); switch (infoId) { case PSP_NET_APCTL_DESC_IBSS: // IBSS, 6 bytes - Memory::WriteStruct(resultAddr, &netApctlInfo.bssid); + if (entryId == 0) + Memory::WriteStruct(resultAddr, &netApctlInfo.bssid); + else { + // Generate a BSSID/MAC address + char dummyMAC[ETHER_ADDR_LEN]; + memset(dummyMAC, entryId, sizeof(dummyMAC)); + // Making sure the 1st 2-bits on the 1st byte of OUI are zero to prevent issue with some games (ie. Gran Turismo) + dummyMAC[0] &= 0xfc; + Memory::WriteStruct(resultAddr, &dummyMAC); + } break; case PSP_NET_APCTL_DESC_SSID_NAME: // Return 32 bytes - Memory::WriteStruct(resultAddr, &netApctlInfo.ssid); + if (entryId == 0) + Memory::WriteStruct(resultAddr, &netApctlInfo.ssid); + else { + Memory::WriteStruct(resultAddr, &dummySSID); + } break; case PSP_NET_APCTL_DESC_SSID_NAME_LENGTH: // Return one 32-bit value - Memory::WriteStruct(resultAddr, &netApctlInfo.ssidLength); + if (entryId == 0) + Memory::WriteStruct(resultAddr, &netApctlInfo.ssidLength); + else { + // Calculate the SSID length + Memory::Write_U32((u32)strlen(dummySSID), resultAddr); + } + break; + case PSP_NET_APCTL_DESC_CHANNEL: + // FIXME: Return one 1 byte value or may be 32-bit if this is not a channel? + if (entryId == 0) + Memory::WriteStruct(resultAddr, &netApctlInfo.channel); + else { + // Generate channel for testing purposes, not even sure whether this is channel or not, MGS:PW seems to treat the data as u8 + Memory::Write_U8(entryId, resultAddr); + } break; case PSP_NET_APCTL_DESC_SIGNAL_STRENGTH: // Return 1 byte - Memory::WriteStruct(resultAddr, &netApctlInfo.strength); + if (entryId == 0) + Memory::WriteStruct(resultAddr, &netApctlInfo.strength); + else { + // Randomize signal strength between 1%~99% since games like MGS:PW are using signal strength to determine the strength of the recruit + Memory::Write_U8((int)(((float)rand() / (float)RAND_MAX) * 99.0 + 1.0), resultAddr); + } break; case PSP_NET_APCTL_DESC_SECURITY: // Return one 32-bit value @@ -1243,20 +1287,38 @@ static int sceNetApctlGetBSSDescEntryUser(int entryId, int infoId, u32 resultAdd return hleLogError(SCENET, ERROR_NET_APCTL_INVALID_CODE, "unknown info id"); } - return hleLogWarning(SCENET, 0, "untested"); + return 0; +} + +static int sceNetApctlGetBSSDescEntryUser(int entryId, int infoId, u32 resultAddr) { + WARN_LOG(SCENET, "UNTESTED %s(%i, %i, %08x)", __FUNCTION__, entryId, infoId, resultAddr); + return NetApctl_GetBSSDescEntryUser(entryId, infoId, resultAddr); } static int sceNetApctlScanSSID2() { - ERROR_LOG(SCENET, "UNIMPL %s()", __FUNCTION__); + WARN_LOG(SCENET, "UNTESTED %s() at %08x", __FUNCTION__, currentMIPS->pc); return NetApctl_ScanUser(); } +/************** +* Arg1 = output buffer size being filled? (initially the same with Arg3 ?) +* Arg2 = output buffer? (linked list where the 1st 32-bit is the next address? followed by entry Id? ie. 8-bytes per entry?) +* Arg3 = max buffer size? (ie. 0x100 ?) +* Arg4 = input flag? (initially 0/1 ?) +***************/ static int sceNetApctlGetBSSDescIDList2(u32 Arg1, u32 Arg2, u32 Arg3, u32 Arg4) { - return hleLogError(SCENET, 0, "unimplemented"); + WARN_LOG(SCENET, "UNTESTED %s(%08x, %08x, %08x, %08x) at %08x", __FUNCTION__, Arg1, Arg2, Arg3, Arg4, currentMIPS->pc); + return NetApctl_GetBSSDescIDListUser(Arg1, Arg2); } -static int sceNetApctlGetBSSDescEntry2(u32 Arg1, u32 Arg2, u32 Arg3, u32 Arg4) { - return hleLogError(SCENET, 0, "unimplemented"); +/************** +* Arg1 = a value returned from sceNetApctlGetBSSDescIDList2 ? entryId? +* Arg2 = input field type within the entry desc (ie. PSP_NET_APCTL_DESC_SSID_NAME ?) +* Arg3 = output buffer for retrieved entry data? (max size = 32 bytes? ie. APCTL_SSID_MAXLEN ? or similar to SceNetApctlInfoInternal union ?) +***************/ +static int sceNetApctlGetBSSDescEntry2(int entryId, int infoId, u32 resultAddr) { + WARN_LOG(SCENET, "UNTESTED %s(%i, %i, %08x) at %08x", __FUNCTION__, entryId, infoId, resultAddr, currentMIPS->pc); + return NetApctl_GetBSSDescEntryUser(entryId, infoId, resultAddr); } static int sceNetResolverInit() @@ -1434,7 +1496,7 @@ const HLEFunction sceNetApctl[] = { {0XA3E77E13, &WrapI_V, "sceNetApctlScanSSID2", 'i', "" }, {0XE9B2E5E6, &WrapI_V, "sceNetApctlScanUser", 'i', "" }, {0XF25A5006, &WrapI_UUUU, "sceNetApctlGetBSSDescIDList2", 'i', "xxxx" }, - {0X2935C45B, &WrapI_UUUU, "sceNetApctlGetBSSDescEntry2", 'i', "xxxx" }, + {0X2935C45B, &WrapI_IIU, "sceNetApctlGetBSSDescEntry2", 'i', "iix" }, {0X04776994, &WrapI_IIU, "sceNetApctlGetBSSDescEntryUser", 'i', "iix" }, {0X6BDDCB8C, &WrapI_UU, "sceNetApctlGetBSSDescIDListUser", 'i', "xx" }, {0X7CFAB990, &WrapI_UU, "sceNetApctlAddInternalHandler", 'i', "xx" }, diff --git a/Core/HLE/sceNet.h b/Core/HLE/sceNet.h index a1ba8285c3..911b61e5eb 100644 --- a/Core/HLE/sceNet.h +++ b/Core/HLE/sceNet.h @@ -181,7 +181,8 @@ enum { PSP_NET_APCTL_EVENT_INFO = 7, PSP_NET_APCTL_EVENT_EAP_AUTH = 8, PSP_NET_APCTL_EVENT_KEY_EXCHANGE = 9, - PSP_NET_APCTL_EVENT_RECONNECT = 10 + PSP_NET_APCTL_EVENT_RECONNECT = 10, + PSP_NET_APCTL_EVENT_SCAN_STOP = 11 // FIXME: not sure what this is, MGS:PW seems to check this value within ApctlHandler during Recruit, related to sceNetApctlScanSSID2 ? }; #define PSP_NET_APCTL_INFO_PROFILE_NAME 0 @@ -204,17 +205,18 @@ enum { #define PSP_NET_APCTL_INFO_START_BROWSER 17 #define PSP_NET_APCTL_INFO_WIFISP 18 -#define PSP_NET_APCTL_INFO_SECURITY_TYPE_NONE 0 -#define PSP_NET_APCTL_INFO_SECURITY_TYPE_WEP 1 -#define PSP_NET_APCTL_INFO_SECURITY_TYPE_WPA 2 -#define PSP_NET_APCTL_INFO_SECURITY_TYPE_UNSUPPORTED 3 -#define PSP_NET_APCTL_INFO_SECURITY_TYPE_WPA_AES 4 +#define PSP_NET_APCTL_INFO_SECURITY_TYPE_NONE 0 +#define PSP_NET_APCTL_INFO_SECURITY_TYPE_WEP 1 +#define PSP_NET_APCTL_INFO_SECURITY_TYPE_WPA 2 +#define PSP_NET_APCTL_INFO_SECURITY_TYPE_UNSUPPORTED 3 +#define PSP_NET_APCTL_INFO_SECURITY_TYPE_WPA_AES 4 -#define PSP_NET_APCTL_DESC_IBSS 0 -#define PSP_NET_APCTL_DESC_SSID_NAME 1 -#define PSP_NET_APCTL_DESC_SSID_NAME_LENGTH 2 -#define PSP_NET_APCTL_DESC_SIGNAL_STRENGTH 4 -#define PSP_NET_APCTL_DESC_SECURITY 5 +#define PSP_NET_APCTL_DESC_IBSS 0 +#define PSP_NET_APCTL_DESC_SSID_NAME 1 +#define PSP_NET_APCTL_DESC_SSID_NAME_LENGTH 2 +#define PSP_NET_APCTL_DESC_CHANNEL 3 // FIXME: not sure what this 3 is, may be Security Type based on the order of SceNetApctlInfoInternal ? +#define PSP_NET_APCTL_DESC_SIGNAL_STRENGTH 4 +#define PSP_NET_APCTL_DESC_SECURITY 5 #ifdef _MSC_VER #pragma pack(push,1) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index c88a11dd60..a514e6bff0 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -250,7 +250,7 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) { SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error); if (waitID == 0 || error != 0) { - WARN_LOG(SCENET, "sceNetAdhocctl Socket WaitID(%i) on Thread(%i) already woken up? (error: %d)", uid, threadID, error); + WARN_LOG(SCENET, "sceNetAdhocctl Socket WaitID(%i) on Thread(%i) already woken up? (error: %08x)", uid, threadID, error); return; } @@ -269,6 +269,7 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) { packet.base.opcode = req.opcode; packet.group = req.group; + // Don't send any packets not in these cases (by setting the len to 0) switch (req.opcode) { case OPCODE_CONNECT: @@ -287,7 +288,8 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) { if (len > 0) { ret = SOCKET_ERROR; sockerr = EAGAIN; - if (IsSocketReady(metasocket, false, true) > 0) { + // Don't send anything yet if connection to Adhoc Server is still in progress + if (!isAdhocctlNeedLogin && IsSocketReady(metasocket, false, true) > 0) { ret = send(metasocket, (const char*)&packet, len, MSG_NOSIGNAL); sockerr = errno; // Successfully Sent or Connection has been closed or Connection failure occurred @@ -300,6 +302,7 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) { } } + // Retry until successfully sent. Login packet sent after successfully connected to Adhoc Server (indicated by networkInited), so we're not sending Login again here if ((req.opcode == OPCODE_LOGIN && !networkInited) || (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK))) { u64 now = (u64)(time_now_d() * 1000000.0); if (now - adhocctlStartTime <= static_cast(adhocDefaultTimeout) + 500) { @@ -316,7 +319,7 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) { u32 waitVal = __KernelGetWaitValue(threadID, error); __KernelResumeThreadFromWait(threadID, result); - DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %d) Result (%08x) of sceNetAdhocctl - Opcode: %d, State: %d", waitID, error, (int)result, waitVal, adhocctlState); + DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %08x) Result (%08x) of sceNetAdhocctl - Opcode: %d, State: %d", waitID, error, (int)result, waitVal, adhocctlState); // We are done with this request adhocctlRequests.erase(uid); @@ -332,7 +335,7 @@ static void __AdhocctlState(u64 userdata, int cyclesLate) { SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error); if (waitID == 0 || error != 0) { - WARN_LOG(SCENET, "sceNetAdhocctl State WaitID(%i) on Thread(%i) already woken up? (error: %d)", uid, threadID, error); + WARN_LOG(SCENET, "sceNetAdhocctl State WaitID(%i) on Thread(%i) already woken up? (error: %08x)", uid, threadID, error); return; } @@ -348,7 +351,7 @@ static void __AdhocctlState(u64 userdata, int cyclesLate) { } __KernelResumeThreadFromWait(threadID, result); - DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %d) Result (%08x) of sceNetAdhocctl - Event: %d, State: %d", waitID, error, (int)result, event, adhocctlState); + DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %08x) Result (%08x) of sceNetAdhocctl - Event: %d, State: %d", waitID, error, (int)result, event, adhocctlState); } // Used to simulate blocking on metasocket when send OP code to AdhocServer @@ -394,6 +397,10 @@ int StartGameModeScheduler(int bufSize) { int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) { auto sock = adhocSockets[req.id - 1]; + if (!sock) { + result = ERROR_NET_ADHOC_SOCKET_DELETED; + return 0; + } if (sock->flags & ADHOC_F_ALERTRECV) { result = ERROR_NET_ADHOC_SOCKET_ALERTED; sock->alerted_flags |= ADHOC_F_ALERTRECV; @@ -492,6 +499,10 @@ int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) { int DoBlockingPdpSend(int uid, AdhocSocketRequest& req, s64& result, AdhocSendTargets& targetPeers) { auto sock = adhocSockets[req.id - 1]; + if (!sock) { + result = ERROR_NET_ADHOC_SOCKET_DELETED; + return 0; + } auto& pdpsocket = sock->data.pdp; if (sock->flags & ADHOC_F_ALERTSEND) { result = ERROR_NET_ADHOC_SOCKET_ALERTED; @@ -541,6 +552,10 @@ int DoBlockingPdpSend(int uid, AdhocSocketRequest& req, s64& result, AdhocSendTa int DoBlockingPtpSend(int uid, AdhocSocketRequest& req, s64& result) { auto sock = adhocSockets[req.id - 1]; + if (!sock) { + result = ERROR_NET_ADHOC_SOCKET_DELETED; + return 0; + } auto& ptpsocket = sock->data.ptp; if (sock->flags & ADHOC_F_ALERTSEND) { result = ERROR_NET_ADHOC_SOCKET_ALERTED; @@ -590,6 +605,10 @@ int DoBlockingPtpSend(int uid, AdhocSocketRequest& req, s64& result) { int DoBlockingPtpRecv(int uid, AdhocSocketRequest& req, s64& result) { auto sock = adhocSockets[req.id - 1]; + if (!sock) { + result = ERROR_NET_ADHOC_SOCKET_DELETED; + return 0; + } auto& ptpsocket = sock->data.ptp; if (sock->flags & ADHOC_F_ALERTRECV) { result = ERROR_NET_ADHOC_SOCKET_ALERTED; @@ -642,6 +661,10 @@ int DoBlockingPtpRecv(int uid, AdhocSocketRequest& req, s64& result) { int DoBlockingPtpAccept(int uid, AdhocSocketRequest& req, s64& result) { auto sock = adhocSockets[req.id - 1]; + if (!sock) { + result = ERROR_NET_ADHOC_SOCKET_DELETED; + return 0; + } auto& ptpsocket = sock->data.ptp; if (sock->flags & ADHOC_F_ALERTACCEPT) { result = ERROR_NET_ADHOC_SOCKET_ALERTED; @@ -688,6 +711,10 @@ int DoBlockingPtpAccept(int uid, AdhocSocketRequest& req, s64& result) { int DoBlockingPtpConnect(int uid, AdhocSocketRequest& req, s64& result) { auto sock = adhocSockets[req.id - 1]; + if (!sock) { + result = ERROR_NET_ADHOC_SOCKET_DELETED; + return 0; + } auto& ptpsocket = sock->data.ptp; if (sock->flags & ADHOC_F_ALERTCONNECT) { result = ERROR_NET_ADHOC_SOCKET_ALERTED; @@ -739,6 +766,10 @@ int DoBlockingPtpConnect(int uid, AdhocSocketRequest& req, s64& result) { int DoBlockingPtpFlush(int uid, AdhocSocketRequest& req, s64& result) { auto sock = adhocSockets[req.id - 1]; + if (!sock) { + result = ERROR_NET_ADHOC_SOCKET_DELETED; + return 0; + } auto& ptpsocket = sock->data.ptp; if (sock->flags & ADHOC_F_ALERTFLUSH) { result = ERROR_NET_ADHOC_SOCKET_ALERTED; @@ -815,7 +846,7 @@ static void __AdhocSocketNotify(u64 userdata, int cyclesLate) { SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error); if (waitID == 0 || error != 0) { - WARN_LOG(SCENET, "sceNetAdhoc Socket WaitID(%i) on Thread(%i) already woken up? (error: %d)", uid, threadID, error); + WARN_LOG(SCENET, "sceNetAdhoc Socket WaitID(%i) on Thread(%i) already woken up? (error: %08x)", uid, threadID, error); return; } @@ -901,7 +932,7 @@ static void __AdhocSocketNotify(u64 userdata, int cyclesLate) { } __KernelResumeThreadFromWait(threadID, result); - DEBUG_LOG(SCENET, "Returning (ThreadId: %d, WaitID: %d, error: %d) Result (%08x) of sceNetAdhoc[%d] - SocketID: %d", threadID, waitID, error, (int)result, req.type, req.id); + DEBUG_LOG(SCENET, "Returning (ThreadId: %d, WaitID: %d, error: %08x) Result (%08x) of sceNetAdhoc[%d] - SocketID: %d", threadID, waitID, error, (int)result, req.type, req.id); // We are done with this socket adhocSocketRequests.erase(userdata); @@ -1056,6 +1087,8 @@ void __NetAdhocDoState(PointerWrap &p) { netAdhocMatchingInited = cur_netAdhocMatchingInited; netAdhocctlInited = cur_netAdhocctlInited; netAdhocInited = cur_netAdhocInited; + + isAdhocctlNeedLogin = false; } } @@ -1142,6 +1175,7 @@ static u32 sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr) { adhocctlEvents.clear(); netAdhocctlInited = true; //needed for cleanup during AdhocctlTerm even when it failed to connect to Adhoc Server (since it's being faked as success) + isAdhocctlNeedLogin = true; // Create fake PSP Thread for callback // TODO: Should use a separated threads for friendFinder, matchingEvent, and matchingInput and created on AdhocctlInit & AdhocMatchingStart instead of here @@ -1157,7 +1191,7 @@ static u32 sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr) { friendFinderThread = std::thread(friendFinder); } - // Need to make sure to be connected to adhoc server before returning to prevent GTA VCS failed to create/join a group and unable to see any game room + // Need to make sure to be connected to Adhoc Server (indicated by networkInited) before returning to prevent GTA VCS failed to create/join a group and unable to see any game room int us = adhocDefaultDelay; if (g_Config.bEnableWlan && !networkInited) { AdhocctlRequest dummyreq = { OPCODE_LOGIN, {0} }; @@ -1829,6 +1863,9 @@ int PollAdhocSocket(SceNetAdhocPollSd* sds, int count, int timeout, int nonblock // Fill in Socket ID if (sds[i].id > 0 && sds[i].id <= MAX_SOCKET && adhocSockets[sds[i].id - 1] != NULL) { auto sock = adhocSockets[sds[i].id - 1]; + if (!sock) { + return ERROR_NET_ADHOC_SOCKET_DELETED; + } if (sock->type == SOCK_PTP) { fd = sock->data.ptp.id; } @@ -2092,6 +2129,7 @@ int sceNetAdhocctlScan() { // Only scan when in Disconnected state, otherwise AdhocServer will kick you out if (adhocctlState == ADHOCCTL_STATE_DISCONNECTED && !isAdhocctlBusy) { isAdhocctlBusy = true; + isAdhocctlNeedLogin = true; adhocctlState = ADHOCCTL_STATE_SCANNING; adhocctlCurrentMode = ADHOCCTL_MODE_NORMAL; @@ -2101,24 +2139,12 @@ int sceNetAdhocctlScan() { networks = NULL; peerlock.unlock(); - // Prepare Scan Request Packet - uint8_t opcode = OPCODE_SCAN; - - // Send Scan Request Packet, may failed with socket error 10054/10053 if someone else with the same IP already connected to AdHoc Server (the server might need to be modified to differentiate MAC instead of IP) - int iResult = send(metasocket, (char *)&opcode, 1, MSG_NOSIGNAL); - int error = errno; - - if (iResult == SOCKET_ERROR) { - if (error != EAGAIN && error != EWOULDBLOCK) { - ERROR_LOG(SCENET, "Socket error (%i) when sending", error); - adhocctlState = ADHOCCTL_STATE_DISCONNECTED; - //if (error == ECONNABORTED || error == ECONNRESET || error == ENOTCONN) return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; // A case where it need to reconnect to AdhocServer - return hleLogError(SCENET, ERROR_NET_ADHOCCTL_BUSY, "busy"); - } - else if (friendFinderRunning) { - AdhocctlRequest req = { OPCODE_SCAN, {0} }; - return WaitBlockingAdhocctlSocket(req, us, "adhocctl scan"); - } + if (friendFinderRunning) { + AdhocctlRequest req = { OPCODE_SCAN, {0} }; + return WaitBlockingAdhocctlSocket(req, us, "adhocctl scan"); + } + else { + adhocctlState = ADHOCCTL_STATE_DISCONNECTED; } // Return Success and let friendFinder thread to notify the handler when scan completed @@ -2603,36 +2629,28 @@ int NetAdhocctl_Create(const char* groupName) { // Disconnected State if (adhocctlState == ADHOCCTL_STATE_DISCONNECTED && !isAdhocctlBusy) { isAdhocctlBusy = true; + isAdhocctlNeedLogin = true; // Set Network Name - if (groupNameStruct != NULL) parameter.group_name = *groupNameStruct; + if (groupNameStruct != NULL) + parameter.group_name = *groupNameStruct; // Reset Network Name - else memset(¶meter.group_name, 0, sizeof(parameter.group_name)); + else + memset(¶meter.group_name, 0, sizeof(parameter.group_name)); - // Prepare Connect Packet - SceNetAdhocctlConnectPacketC2S packet; + // Set HUD Connection Status + //setConnectionStatus(1); - // Clear Packet Memory - memset(&packet, 0, sizeof(packet)); - - // Set Packet Opcode - packet.base.opcode = OPCODE_CONNECT; - - // Set Target Group - if (groupNameStruct != NULL) packet.group = *groupNameStruct; - - // Acquire Network Lock - - // Send Packet - int iResult = send(metasocket, (const char*)&packet, sizeof(packet), MSG_NOSIGNAL); - int error = errno; - adhocctlStartTime = (u64)(time_now_d() * 1000000.0); - - if (iResult == SOCKET_ERROR && error != EAGAIN && error != EWOULDBLOCK) { - ERROR_LOG(SCENET, "Socket error (%i) when sending", error); - - //Faking success, to prevent Full Auto 2 from freezing while Initializing Network + // Wait for Status to be connected to prevent Ford Street Racing from Failed to create game session + int us = adhocDefaultDelay; + if (friendFinderRunning) { + AdhocctlRequest req = { OPCODE_CONNECT, parameter.group_name }; + return WaitBlockingAdhocctlSocket(req, us, "adhocctl connect"); + } + //Faking success, to prevent Full Auto 2 from freezing while Initializing Network + else { + adhocctlStartTime = (u64)(time_now_d() * 1000000.0); if (adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE) { adhocctlState = ADHOCCTL_STATE_GAMEMODE; notifyAdhocctlHandlers(ADHOCCTL_EVENT_GAME, 0); @@ -2645,19 +2663,6 @@ int NetAdhocctl_Create(const char* groupName) { } } - // Free Network Lock - - // Set HUD Connection Status - //setConnectionStatus(1); - - // Wait for Status to be connected to prevent Ford Street Racing from Failed to create game session - int us = adhocDefaultDelay; - if (adhocctlState != ADHOCCTL_STATE_CONNECTED && adhocctlState != ADHOCCTL_STATE_GAMEMODE && iResult == SOCKET_ERROR && friendFinderRunning) { - AdhocctlRequest req = { OPCODE_CONNECT, {0} }; - if (groupNameStruct != NULL) req.group = *groupNameStruct; - return WaitBlockingAdhocctlSocket(req, us, "adhocctl connect"); - } - hleEatMicro(us); // Return Success // FIXME: When tested using JPCSP + official prx files it seems sceNetAdhocctlCreate switching to a different thread for at least 100ms after returning success and before executing the next line. @@ -6001,8 +6006,8 @@ void broadcastHelloMessage(SceNetAdhocMatchingContext * context) if (context->hellolen > 0) memcpy(hello + 5, context->hello, context->hellolen); std::string hellohex; - DataToHexString(" ", 0, context->hello, context->hellolen, &hellohex); - DEBUG_LOG(SCENET, "HELLO Dump:\n%s", hellohex.c_str()); + DataToHexString(10, 0, context->hello, context->hellolen, &hellohex); + DEBUG_LOG(SCENET, "HELLO Dump (%d bytes):\n%s", context->hellolen, hellohex.c_str()); // Send Broadcast context->socketlock->lock(); @@ -6442,8 +6447,8 @@ void actOnHelloPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * se if (peer != NULL && peer->state != PSP_ADHOC_MATCHING_PEER_OUTGOING_REQUEST && peer->state != PSP_ADHOC_MATCHING_PEER_INCOMING_REQUEST) { std::string hellohex; - DataToHexString(" ", 0, (u8*)opt, optlen, &hellohex); - DEBUG_LOG(SCENET, "HELLO Dump:\n%s", hellohex.c_str()); + DataToHexString(10, 0, (u8*)opt, optlen, &hellohex); + DEBUG_LOG(SCENET, "HELLO Dump (%d bytes):\n%s", optlen, hellohex.c_str()); // Spawn Hello Event. FIXME: HELLO event should not be triggered in the middle of joining? This will cause Bleach 7 to Cancel the join request spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_HELLO, sendermac, optlen, opt); @@ -6943,7 +6948,7 @@ void actOnByePacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * send */ int matchingEventThread(int matchingId) { - setCurrentThreadName("MatchingEvent"); + SetCurrentThreadName("MatchingEvent"); // Multithreading Lock peerlock.lock(); // Cast Context @@ -7077,7 +7082,7 @@ int matchingEventThread(int matchingId) */ int matchingInputThread(int matchingId) // TODO: The MatchingInput thread is using sceNetAdhocPdpRecv & sceNetAdhocPdpSend functions so it might be better to run this on PSP thread instead of real thread { - setCurrentThreadName("MatchingInput"); + SetCurrentThreadName("MatchingInput"); auto n = GetI18NCategory("Networking"); // Multithreading Lock peerlock.lock(); @@ -7256,7 +7261,7 @@ int matchingInputThread(int matchingId) // TODO: The MatchingInput thread is usi } else if (recvresult == 0 && rxbuflen > 0) { WARN_LOG(SCENET, "InputLoop[%d]: Unknown Port[%s:%u] (Recved=%i, Length=%i)", matchingId, mac2str(&sendermac).c_str(), senderport, recvresult, rxbuflen); - host->NotifyUserMessage(std::string(n->T("Data from incorrect Port")) + std::string(" [") + mac2str(&sendermac) + std::string("]:") + std::to_string(senderport) + std::string(" -> ") + std::to_string(context->port), 1.0, 0x0080ff); + host->NotifyUserMessage(std::string(n->T("Data from incorrect Port")) + std::string(" [") + mac2str(&sendermac) + std::string("]:") + std::to_string(senderport) + std::string(" -> ") + std::to_string(context->port) + std::string(" (") + std::to_string(portOffset) + std::string(")"), 1.0, 0x0080ff); } // Handle Peer Timeouts @@ -7279,7 +7284,7 @@ int matchingInputThread(int matchingId) // TODO: The MatchingInput thread is usi // Iterate Message List int msg_count = 0; ThreadMessage* msg = context->input_stack; - for (; msg != NULL; msg = msg->next) + while (msg != NULL) { // Default Optional Data void* opt = NULL; diff --git a/Core/HLE/sceRtc.cpp b/Core/HLE/sceRtc.cpp index 5fc8d372ce..f2f317f7e4 100644 --- a/Core/HLE/sceRtc.cpp +++ b/Core/HLE/sceRtc.cpp @@ -320,94 +320,76 @@ static u64 sceRtcGetAccumulativeTime() return __RtcGetCurrentTick(); } -static u32 sceRtcGetCurrentClock(u32 pspTimePtr, int tz) -{ - DEBUG_LOG(SCERTC, "sceRtcGetCurrentClock(%08x, %d)", pspTimePtr, tz); +static u32 sceRtcGetCurrentClock(u32 pspTimePtr, int tz) { + auto pt = PSPPointer::Create(pspTimePtr); + PSPTimeval tv; __RtcTimeOfDay(&tv); - time_t sec = (time_t) tv.tv_sec; + time_t sec = (time_t)tv.tv_sec; tm *utc = gmtime(&sec); - if (!utc) - { - ERROR_LOG(SCERTC, "Date is too high/low to handle, pretending to work."); - return 0; + if (!utc) { + return hleLogError(SCERTC, 0, "Date is too high/low to handle, pretending to work"); } utc->tm_isdst = -1; utc->tm_min += tz; rtc_timegm(utc); // Return gmt time with timezone offset. - ScePspDateTime ret; - __RtcTmToPspTime(ret, utc); - ret.microsecond = tv.tv_usec; - - if (Memory::IsValidAddress(pspTimePtr)) - Memory::WriteStruct(pspTimePtr, &ret); + if (pt.IsValid()) { + __RtcTmToPspTime(*pt, utc); + pt->microsecond = tv.tv_usec; + } hleEatCycles(1900); hleReSchedule("rtc current clock"); - return 0; + return hleLogSuccessI(SCERTC, 0); } -static u32 sceRtcGetCurrentClockLocalTime(u32 pspTimePtr) -{ - DEBUG_LOG(SCERTC, "sceRtcGetCurrentClockLocalTime(%08x)", pspTimePtr); +static u32 sceRtcGetCurrentClockLocalTime(u32 pspTimePtr) { + auto pt = PSPPointer::Create(pspTimePtr); + PSPTimeval tv; __RtcTimeOfDay(&tv); - time_t sec = (time_t) tv.tv_sec; - tm *local = localtime(&sec); - if (!local) - { - ERROR_LOG(SCERTC, "Date is too high/low to handle, pretending to work."); - return 0; + time_t sec = (time_t)tv.tv_sec; + const tm *local = localtime(&sec); + if (!local) { + return hleLogError(SCERTC, 0, "Date is too high/low to handle, pretending to work"); } - ScePspDateTime ret; - __RtcTmToPspTime(ret, local); - ret.microsecond = tv.tv_usec; - - if (Memory::IsValidAddress(pspTimePtr)) - Memory::WriteStructUnchecked(pspTimePtr, &ret); + if (pt.IsValid()) { + __RtcTmToPspTime(*pt, local); + pt->microsecond = tv.tv_usec; + } hleEatCycles(2000); hleReSchedule("rtc current clock local"); - return 0; + return hleLogSuccessI(SCERTC, 0); } -static u32 sceRtcSetTick(u32 pspTimePtr, u32 tickPtr) -{ - DEBUG_LOG(SCERTC, "sceRtcSetTick(%08x, %08x)", pspTimePtr, tickPtr); - if (Memory::IsValidAddress(pspTimePtr) && Memory::IsValidAddress(tickPtr)) - { - u64 ticks = Memory::Read_U64(tickPtr); +static u32 sceRtcSetTick(u32 pspTimePtr, u32 tickPtr) { + auto pt = PSPPointer::Create(pspTimePtr); + auto tick = PSPPointer::Create(tickPtr); - ScePspDateTime ret; - __RtcTicksToPspTime(ret, ticks); - Memory::WriteStructUnchecked(pspTimePtr, &ret); - } - return 0; + if (!pt.IsValid() || !tick.IsValid()) + return hleLogError(SCERTC, 0, "bad address"); + + __RtcTicksToPspTime(*pt, *tick); + return hleLogSuccessI(SCERTC, 0); } -static u32 sceRtcGetTick(u32 pspTimePtr, u32 tickPtr) -{ - DEBUG_LOG(SCERTC, "sceRtcGetTick(%08x, %08x)", pspTimePtr, tickPtr); - ScePspDateTime pt; +static u32 sceRtcGetTick(u32 pspTimePtr, u32 tickPtr) { + auto pt = PSPPointer::Create(pspTimePtr); + auto tick = PSPPointer::Create(tickPtr); - if (Memory::IsValidAddress(pspTimePtr) && Memory::IsValidAddress(tickPtr)) - { - Memory::ReadStructUnchecked(pspTimePtr, &pt); + if (!pt.IsValid() || !tick.IsValid()) + return hleLogError(SCERTC, 0, "bad address"); + if (!__RtcValidatePspTime(*pt)) + return hleLogWarning(SCERTC, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid time"); - if (!__RtcValidatePspTime(pt)) - return SCE_KERNEL_ERROR_INVALID_VALUE; - - u64 result = __RtcPspTimeToTicks(pt); - - Memory::Write_U64(result, tickPtr); - } - - return 0; + *tick = __RtcPspTimeToTicks(*pt); + return hleLogSuccessI(SCERTC, 0); } static u32 sceRtcGetDayOfWeek(u32 year, u32 month, u32 day) @@ -541,190 +523,114 @@ static int sceRtcConvertUtcToLocalTime(u32 tickUTCPtr,u32 tickLocalPtr) return 0; } -static int sceRtcCheckValid(u32 datePtr) -{ - DEBUG_LOG(SCERTC, "sceRtcCheckValid(%d)", datePtr); +static int sceRtcCheckValid(u32 datePtr) { + auto pt = PSPPointer::Create(datePtr); - if (Memory::IsValidAddress(datePtr)) - { - ScePspDateTime pt; - Memory::ReadStructUnchecked(datePtr, &pt); - if (pt.year < 1 || pt.year > 9999) - { - return PSP_TIME_INVALID_YEAR; - } - else if (pt.month < 1 || pt.month > 12) - { - return PSP_TIME_INVALID_MONTH; - } - else if (pt.day < 1 || pt.day > 31) - { - return PSP_TIME_INVALID_DAY; - } - else if (pt.day > __RtcDaysInMonth((s16)pt.year, (s16)pt.month)) - { - return PSP_TIME_INVALID_DAY; - } - else if (pt.hour < 0 || pt.hour > 23) - { - return PSP_TIME_INVALID_HOUR; - } - else if (pt.minute < 0 || pt.minute > 59) - { - return PSP_TIME_INVALID_MINUTES; - } - else if (pt.second < 0 || pt.second > 59) - { - return PSP_TIME_INVALID_SECONDS; - } - else if (pt.microsecond >= 1000000UL) - { - return PSP_TIME_INVALID_MICROSECONDS; - } - else { - return 0; - } - } - else - { - return -1; - } + if (!pt.IsValid() ) + return hleLogError(SCERTC, -1, "bad address"); + + int result = 0; + if (pt->year < 1 || pt->year > 9999) + result = PSP_TIME_INVALID_YEAR; + else if (pt->month < 1 || pt->month > 12) + result = PSP_TIME_INVALID_MONTH; + else if (pt->day < 1 || pt->day > 31) + result = PSP_TIME_INVALID_DAY; + else if (pt->day > __RtcDaysInMonth((s16)pt->year, (s16)pt->month)) + result = PSP_TIME_INVALID_DAY; + else if (pt->hour < 0 || pt->hour > 23) + result = PSP_TIME_INVALID_HOUR; + else if (pt->minute < 0 || pt->minute > 59) + result = PSP_TIME_INVALID_MINUTES; + else if (pt->second < 0 || pt->second > 59) + result = PSP_TIME_INVALID_SECONDS; + else if (pt->microsecond >= 1000000UL) + result = PSP_TIME_INVALID_MICROSECONDS; + return hleLogSuccessI(SCERTC, result); } -static int sceRtcSetTime_t(u32 datePtr, u32 time) -{ - DEBUG_LOG(SCERTC, "sceRtcSetTime_t(%08x,%d)", datePtr, time); - if (Memory::IsValidAddress(datePtr)) - { - ScePspDateTime pt; - __RtcTicksToPspTime(pt, time*1000000ULL + rtcMagicOffset); - Memory::WriteStructUnchecked(datePtr, &pt); - } - else - { - return 1; - } - return 0; +static int sceRtcSetTime_t(u32 datePtr, u32 time) { + auto pt = PSPPointer::Create(datePtr); + if (!pt.IsValid()) + return hleLogError(SCERTC, 1, "bad address"); + + __RtcTicksToPspTime(*pt, time * 1000000ULL + rtcMagicOffset); + return hleLogSuccessI(SCERTC, 0); } -static int sceRtcSetTime64_t(u32 datePtr, u64 time) -{ - DEBUG_LOG(SCERTC, "sceRtcSetTime64_t(%08x,%lld)", datePtr, time); - if (Memory::IsValidAddress(datePtr)) - { - ScePspDateTime pt; - __RtcTicksToPspTime(pt, time*1000000ULL + rtcMagicOffset); - Memory::WriteStruct(datePtr, &pt); - } - else - { - return 1; - } - return 0; +static int sceRtcSetTime64_t(u32 datePtr, u64 time) { + auto pt = PSPPointer::Create(datePtr); + if (!pt.IsValid()) + return hleLogError(SCERTC, 1, "bad address"); + + __RtcTicksToPspTime(*pt, time * 1000000ULL + rtcMagicOffset); + return hleLogSuccessI(SCERTC, 0); } -static int sceRtcGetTime_t(u32 datePtr, u32 timePtr) -{ - DEBUG_LOG(SCERTC, "sceRtcGetTime_t(%08x,%08x)", datePtr, timePtr); - if (Memory::IsValidAddress(datePtr)&&Memory::IsValidAddress(timePtr)) - { - ScePspDateTime pt; - Memory::ReadStructUnchecked(datePtr, &pt); - u32 result = (u32) ((__RtcPspTimeToTicks(pt)-rtcMagicOffset)/1000000ULL); - Memory::Write_U32(result, timePtr); - } - else - { - return 1; - } - return 0; +static int sceRtcGetTime_t(u32 datePtr, u32 timePtr) { + auto pt = PSPPointer::Create(datePtr); + auto timep = PSPPointer::Create(timePtr); + if (!pt.IsValid() || !timep.IsValid()) + return hleLogError(SCERTC, 1, "bad address"); + + *timep = (u32)((__RtcPspTimeToTicks(*pt) - rtcMagicOffset) / 1000000ULL); + return hleLogSuccessI(SCERTC, 0); } -static int sceRtcGetTime64_t(u32 datePtr, u32 timePtr) -{ - DEBUG_LOG(SCERTC, "sceRtcGetTime64_t(%08x,%08x)", datePtr, timePtr); - if (Memory::IsValidAddress(datePtr)&&Memory::IsValidAddress(timePtr)) - { - ScePspDateTime pt; - Memory::ReadStructUnchecked(datePtr, &pt); - u64 result = (__RtcPspTimeToTicks(pt)-rtcMagicOffset)/1000000ULL; - Memory::Write_U64(result, timePtr); - } - else - { - return 1; - } - return 0; +static int sceRtcGetTime64_t(u32 datePtr, u32 timePtr) { + auto pt = PSPPointer::Create(datePtr); + auto timep = PSPPointer::Create(timePtr); + if (!pt.IsValid() || !timep.IsValid()) + return hleLogError(SCERTC, 1, "bad address"); + + *timep = (__RtcPspTimeToTicks(*pt) - rtcMagicOffset) / 1000000ULL; + return hleLogSuccessI(SCERTC, 0); } -static int sceRtcSetDosTime(u32 datePtr, u32 dosTime) -{ - DEBUG_LOG(SCERTC, "sceRtcSetDosTime(%d,%d)", datePtr, dosTime); - if (Memory::IsValidAddress(datePtr)) - { - ScePspDateTime pt; +static int sceRtcSetDosTime(u32 datePtr, u32 dosTime) { + auto pt = PSPPointer::Create(datePtr); + if (!pt.IsValid()) + return hleLogError(SCERTC, 1, "bad address"); - int hms = dosTime & 0xFFFF; - int ymd = dosTime >> 16; + int hms = dosTime & 0xFFFF; + int ymd = dosTime >> 16; - pt.year = 1980 + (ymd >> 9); - pt.month = (ymd >> 5) & 0xF; - pt.day = ymd & 0x1F; - pt.hour = hms >> 11; - pt.minute = (hms >> 5) & 0x3F; - pt.second = (hms << 1) & 0x3E; - pt.microsecond = 0; + pt->year = 1980 + (ymd >> 9); + pt->month = (ymd >> 5) & 0xF; + pt->day = ymd & 0x1F; + pt->hour = hms >> 11; + pt->minute = (hms >> 5) & 0x3F; + pt->second = (hms << 1) & 0x3E; + pt->microsecond = 0; - Memory::WriteStructUnchecked(datePtr, &pt); - } - else - { - return 1; - } - return 0; + return hleLogSuccessI(SCERTC, 0); } -static int sceRtcGetDosTime(u32 datePtr, u32 dosTime) -{ - int retValue = 0; - DEBUG_LOG(SCERTC, "sceRtcGetDosTime(%d,%d)", datePtr, dosTime); - if (Memory::IsValidAddress(datePtr)&&Memory::IsValidAddress(dosTime)) - { - ScePspDateTime pt; - Memory::ReadStructUnchecked(datePtr, &pt); +static int sceRtcGetDosTime(u32 datePtr, u32 dosTime) { + auto datep = PSPPointer::Create(datePtr); + auto dosp = PSPPointer::Create(dosTime); + if (!datep.IsValid() || !dosp.IsValid()) + return hleLogError(SCERTC, -1, "bad address"); - u32 result = 0; - if(pt.year < 1980) - { - result = 0; - retValue = -1; - } - else if(pt.year >= 2108) - { - result = 0xFF9FBF7D; - retValue = -1; - } - else - { - int year = ((pt.year - 1980) & 0x7F) << 9; - int month = ((pt.month) & 0xF ) << 5; - int hour = ((pt.hour) & 0x1F ) << 11; - int minute = ((pt.minute) & 0x3F ) << 5; - int day = (pt.day) & 0x1F; - int second = ((pt.second) >> 1) & 0x1F; - int ymd = year | month | day; - int hms = hour | minute | second; - result = (ymd << 16) | hms; - retValue = 0; - } + if (datep->year < 1980) { + *dosp = 0; + return hleLogWarning(SCERTC, -1, "invalid year"); + } else if (datep->year >= 2108) { + *dosp = 0xFF9FBF7D; + return hleLogWarning(SCERTC, -1, "invalid year"); + } - Memory::Write_U32(result, dosTime); - } - else - { - retValue = -1; - } - return retValue; + int year = ((datep->year - 1980) & 0x7F) << 9; + int month = (datep->month & 0xF) << 5; + int hour = (datep->hour & 0x1F) << 11; + int minute = (datep->minute & 0x3F) << 5; + int day = datep->day & 0x1F; + int second = (datep->second >> 1) & 0x1F; + int ymd = year | month | day; + int hms = hour | minute | second; + + *dosp = (ymd << 16) | hms; + return hleLogSuccessI(SCERTC, 0); } static int sceRtcSetWin32FileTime(u32 datePtr, u64 win32Time) @@ -1115,8 +1021,8 @@ const HLEFunction sceRtc[] = {0X3F7AD767, &WrapU_U, "sceRtcGetCurrentTick", 'x', "x" }, {0X011F03C1, &WrapU64_V, "sceRtcGetAccumulativeTime", 'X', "" }, {0X029CA3B3, &WrapU64_V, "sceRtcGetAccumlativeTime", 'X', "" }, - {0X4CFA57B0, &WrapU_UI, "sceRtcGetCurrentClock", 'x', "xi" }, - {0XE7C27D1B, &WrapU_U, "sceRtcGetCurrentClockLocalTime", 'x', "x" }, + {0X4CFA57B0, &WrapU_UI, "sceRtcGetCurrentClock", 'i', "xi" }, + {0XE7C27D1B, &WrapU_U, "sceRtcGetCurrentClockLocalTime", 'i', "x" }, {0X34885E0D, &WrapI_UU, "sceRtcConvertUtcToLocalTime", 'i', "xx" }, {0X779242A2, &WrapI_UU, "sceRtcConvertLocalTimeToUTC", 'i', "xx" }, {0X42307A17, &WrapU_U, "sceRtcIsLeapYear", 'x', "x" }, @@ -1124,13 +1030,13 @@ const HLEFunction sceRtc[] = {0X57726BC1, &WrapU_UUU, "sceRtcGetDayOfWeek", 'x', "xxx"}, {0X4B1B5E82, &WrapI_U, "sceRtcCheckValid", 'i', "x" }, {0X3A807CC8, &WrapI_UU, "sceRtcSetTime_t", 'i', "xx" }, - {0X27C4594C, &WrapI_UU, "sceRtcGetTime_t", 'i', "xx" }, + {0X27C4594C, &WrapI_UU, "sceRtcGetTime_t", 'i', "xp" }, {0XF006F264, &WrapI_UU, "sceRtcSetDosTime", 'i', "xx" }, - {0X36075567, &WrapI_UU, "sceRtcGetDosTime", 'i', "xx" }, + {0X36075567, &WrapI_UU, "sceRtcGetDosTime", 'i', "xp" }, {0X7ACE4C04, &WrapI_UU64, "sceRtcSetWin32FileTime", 'i', "xX" }, {0XCF561893, &WrapI_UU, "sceRtcGetWin32FileTime", 'i', "xx" }, - {0X7ED29E40, &WrapU_UU, "sceRtcSetTick", 'x', "xx" }, - {0X6FF40ACC, &WrapU_UU, "sceRtcGetTick", 'x', "xx" }, + {0X7ED29E40, &WrapU_UU, "sceRtcSetTick", 'x', "xP" }, + {0X6FF40ACC, &WrapU_UU, "sceRtcGetTick", 'i', "xP" }, {0X9ED0AE87, &WrapI_UU, "sceRtcCompareTick", 'i', "xx" }, {0X44F45E05, &WrapI_UUU64, "sceRtcTickAddTicks", 'i', "xxX"}, {0X26D25A5D, &WrapI_UUU64, "sceRtcTickAddMicroseconds", 'i', "xxX"}, @@ -1147,7 +1053,7 @@ const HLEFunction sceRtc[] = {0X27F98543, &WrapI_UU, "sceRtcFormatRFC3339LocalTime", 'i', "xx" }, {0XDFBC5F16, &WrapI_UU, "sceRtcParseDateTime", 'i', "xx" }, {0X28E1E988, nullptr, "sceRtcParseRFC3339", '?', "" }, - {0XE1C93E47, &WrapI_UU, "sceRtcGetTime64_t", 'i', "xx" }, + {0XE1C93E47, &WrapI_UU, "sceRtcGetTime64_t", 'i', "xP" }, {0X1909C99B, &WrapI_UU64, "sceRtcSetTime64_t", 'i', "xX" }, {0X62685E98, &WrapI_U, "sceRtcGetLastAdjustedTime", 'i', "x" }, {0X203CEB0D, &WrapI_U, "sceRtcGetLastReincarnatedTime", 'i', "x" }, diff --git a/Core/HLE/sceSas.cpp b/Core/HLE/sceSas.cpp index 1c1014252c..a0450efa5a 100644 --- a/Core/HLE/sceSas.cpp +++ b/Core/HLE/sceSas.cpp @@ -101,7 +101,7 @@ static SasThreadParams sasThreadParams; static int sasMixEvent = -1; int __SasThread() { - setCurrentThreadName("SAS"); + SetCurrentThreadName("SAS"); std::unique_lock guard(sasWakeMutex); while (sasThreadState != SasThreadState::DISABLED) { diff --git a/Core/HLE/sceUmd.cpp b/Core/HLE/sceUmd.cpp index 8f61204230..8ad8a5196a 100644 --- a/Core/HLE/sceUmd.cpp +++ b/Core/HLE/sceUmd.cpp @@ -40,9 +40,11 @@ #include "Core/FileSystems/ISOFileSystem.h" #include "Core/FileSystems/VirtualDiscFileSystem.h" -const u64 MICRO_DELAY_ACTIVATE = 4000; +static constexpr u64 MICRO_DELAY_ACTIVATE = 4000; +// Does not include PSP_UMD_CHANGED. +static constexpr uint32_t UMD_STAT_ALLOW_WAIT = PSP_UMD_NOT_PRESENT | PSP_UMD_PRESENT | PSP_UMD_NOT_READY | PSP_UMD_READY | PSP_UMD_READABLE; -static u8 umdActivated = 1; +static bool umdActivated = true; static u32 umdStatus = 0; static u32 umdErrorStat = 0; static int driveCBId = 0; @@ -60,18 +62,18 @@ struct PspUmdInfo { u32_le type; }; -void __UmdStatTimeout(u64 userdata, int cyclesLate); -void __UmdStatChange(u64 userdata, int cyclesLate); -void __UmdInsertChange(u64 userdata, int cyclesLate); -void __UmdBeginCallback(SceUID threadID, SceUID prevCallbackId); -void __UmdEndCallback(SceUID threadID, SceUID prevCallbackId); +static void __UmdStatTimeout(u64 userdata, int cyclesLate); +static void __UmdStatChange(u64 userdata, int cyclesLate); +static void __UmdInsertChange(u64 userdata, int cyclesLate); +static void __UmdBeginCallback(SceUID threadID, SceUID prevCallbackId); +static void __UmdEndCallback(SceUID threadID, SceUID prevCallbackId); void __UmdInit() { umdStatTimeoutEvent = CoreTiming::RegisterEvent("UmdTimeout", __UmdStatTimeout); umdStatChangeEvent = CoreTiming::RegisterEvent("UmdChange", __UmdStatChange); umdInsertChangeEvent = CoreTiming::RegisterEvent("UmdInsertChange", __UmdInsertChange); - umdActivated = 1; + umdActivated = true; umdStatus = 0; umdErrorStat = 0; driveCBId = 0; @@ -87,7 +89,9 @@ void __UmdDoState(PointerWrap &p) if (!s) return; + u8 activatedByte = umdActivated ? 1 : 0; Do(p, umdActivated); + umdActivated = activatedByte != 0; Do(p, umdStatus); Do(p, umdErrorStat); Do(p, driveCBId); @@ -113,8 +117,11 @@ void __UmdDoState(PointerWrap &p) CoreTiming::RestoreRegisterEvent(umdInsertChangeEvent, "UmdInsertChange", __UmdInsertChange); } -static u8 __KernelUmdGetState() -{ +static u8 __KernelUmdGetState() { + if (!UMDInserted) { + return PSP_UMD_NOT_PRESENT; + } + // Most games seem to expect the disc to be ready early on, active or not. // It seems like the PSP sets this state when the disc is "ready". u8 state = PSP_UMD_PRESENT | PSP_UMD_READY; @@ -124,16 +131,7 @@ static u8 __KernelUmdGetState() return state; } -void __UmdInsertChange(u64 userdata, int cyclesLate) -{ - UMDInserted = true; -} - -void __UmdStatChange(u64 userdata, int cyclesLate) -{ - // TODO: Why not a bool anyway? - umdActivated = userdata & 0xFF; - +static void UmdWakeThreads() { // Wake anyone waiting on this. for (size_t i = 0; i < umdWaitingThreads.size(); ++i) { const SceUID threadID = umdWaitingThreads[i]; @@ -142,18 +140,31 @@ void __UmdStatChange(u64 userdata, int cyclesLate) u32 stat = __KernelGetWaitValue(threadID, error); bool keep = false; if (HLEKernel::VerifyWait(threadID, WAITTYPE_UMD, 1)) { - if ((stat & __KernelUmdGetState()) != 0) - __KernelResumeThreadFromWait(threadID, 0); // Only if they are still waiting do we keep them in the list. - else - keep = true; + keep = (stat & __KernelUmdGetState()) == 0; + if (!keep) { + __KernelResumeThreadFromWait(threadID, 0); + } } - if (!keep) + if (!keep) { umdWaitingThreads.erase(umdWaitingThreads.begin() + i--); + } } } +static void __UmdStatChange(u64 userdata, int cyclesLate) { + umdActivated = userdata != 0; + + UmdWakeThreads(); +} + +static void __UmdInsertChange(u64 userdata, int cyclesLate) { + UMDInserted = true; + + UmdWakeThreads(); +} + static void __KernelUmdActivate() { u32 notifyArg = PSP_UMD_PRESENT | PSP_UMD_READABLE; @@ -179,7 +190,7 @@ static void __KernelUmdDeactivate() __UmdStatChange(0, 0); } -void __UmdBeginCallback(SceUID threadID, SceUID prevCallbackId) +static void __UmdBeginCallback(SceUID threadID, SceUID prevCallbackId) { SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; @@ -205,7 +216,7 @@ void __UmdBeginCallback(SceUID threadID, SceUID prevCallbackId) WARN_LOG_REPORT(SCEIO, "sceUmdWaitDriveStatCB: beginning callback with bad wait id?"); } -void __UmdEndCallback(SceUID threadID, SceUID prevCallbackId) +static void __UmdEndCallback(SceUID threadID, SceUID prevCallbackId) { SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; @@ -269,20 +280,16 @@ static u32 sceUmdGetDiscInfo(u32 infoAddr) return PSP_ERROR_UMD_INVALID_PARAM; } -static int sceUmdActivate(u32 mode, const char *name) -{ +static int sceUmdActivate(u32 mode, const char *name) { if (mode < 1 || mode > 2) - return PSP_ERROR_UMD_INVALID_PARAM; + return hleLogWarning(SCEIO, PSP_ERROR_UMD_INVALID_PARAM); __KernelUmdActivate(); - if (mode == 1) { - DEBUG_LOG(SCEIO, "0=sceUmdActivate(%d, %s)", mode, name); - } else { - ERROR_LOG(SCEIO, "UNTESTED 0=sceUmdActivate(%d, %s)", mode, name); + if (mode != 1) { + return hleLogError(SCEIO, 0, "UNTESTED"); } - - return 0; + return hleLogSuccessI(SCEIO, 0); } static int sceUmdDeactivate(u32 mode, const char *name) @@ -348,7 +355,7 @@ static u32 sceUmdGetDriveStat() return retVal; } -void __UmdStatTimeout(u64 userdata, int cyclesLate) +static void __UmdStatTimeout(u64 userdata, int cyclesLate) { SceUID threadID = (SceUID)userdata; @@ -379,22 +386,18 @@ static void __UmdWaitStat(u32 timeout) * @return < 0 on error * */ -static int sceUmdWaitDriveStat(u32 stat) -{ - if (stat == 0) { - DEBUG_LOG(SCEIO, "sceUmdWaitDriveStat(stat = %08x): bad status", stat); - return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT; +static int sceUmdWaitDriveStat(u32 stat) { + if ((stat & UMD_STAT_ALLOW_WAIT) == 0) { + return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT, "bad status"); } - if (!__KernelIsDispatchEnabled()) { - DEBUG_LOG(SCEIO, "sceUmdWaitDriveStat(stat = %08x): dispatch disabled", stat); - return SCE_KERNEL_ERROR_CAN_NOT_WAIT; + return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_CAN_NOT_WAIT, "dispatch disabled"); } if (__IsInInterrupt()) { - DEBUG_LOG(SCEIO, "sceUmdWaitDriveStat(stat = %08x): inside interrupt", stat); - return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT; + return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_ILLEGAL_CONTEXT, "inside interrupt"); } + hleEatCycles(520); if ((stat & __KernelUmdGetState()) == 0) { DEBUG_LOG(SCEIO, "sceUmdWaitDriveStat(stat = %08x): waiting", stat); umdWaitingThreads.push_back(__KernelGetCurThread()); @@ -402,26 +405,21 @@ static int sceUmdWaitDriveStat(u32 stat) return 0; } - DEBUG_LOG(SCEIO, "0=sceUmdWaitDriveStat(stat = %08x)", stat); - return 0; + return hleLogSuccessI(SCEIO, 0); } -static int sceUmdWaitDriveStatWithTimer(u32 stat, u32 timeout) -{ - if (stat == 0) { - DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatWithTimer(stat = %08x, timeout = %d): bad status", stat, timeout); - return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT; +static int sceUmdWaitDriveStatWithTimer(u32 stat, u32 timeout) { + if ((stat & UMD_STAT_ALLOW_WAIT) == 0) { + return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT, "bad status"); } - if (!__KernelIsDispatchEnabled()) { - DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatWithTimer(stat = %08x, timeout = %d): dispatch disabled", stat, timeout); - return SCE_KERNEL_ERROR_CAN_NOT_WAIT; + return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_CAN_NOT_WAIT, "dispatch disabled"); } if (__IsInInterrupt()) { - DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatWithTimer(stat = %08x, timeout = %d): inside interrupt", stat, timeout); - return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT; + return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_ILLEGAL_CONTEXT, "inside interrupt"); } + hleEatCycles(520); if ((stat & __KernelUmdGetState()) == 0) { DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatWithTimer(stat = %08x, timeout = %d): waiting", stat, timeout); __UmdWaitStat(timeout); @@ -432,34 +430,24 @@ static int sceUmdWaitDriveStatWithTimer(u32 stat, u32 timeout) hleReSchedule("umd stat checked"); } - DEBUG_LOG(SCEIO, "0=sceUmdWaitDriveStatWithTimer(stat = %08x, timeout = %d)", stat, timeout); - return 0; + return hleLogSuccessI(SCEIO, 0); } -static int sceUmdWaitDriveStatCB(u32 stat, u32 timeout) -{ - if (!UMDInserted) { - WARN_LOG(SCEIO, "sceUmdWaitDriveStatCB(stat = %08x, timeout = %d): UMD is taking out for switch UMD", stat, timeout); - return PSP_UMD_NOT_PRESENT; +static int sceUmdWaitDriveStatCB(u32 stat, u32 timeout) { + if ((stat & UMD_STAT_ALLOW_WAIT) == 0) { + return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT, "bad status"); } - - if (stat == 0) { - DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatCB(stat = %08x, timeout = %d): bad status", stat, timeout); - return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT; - } - if (!__KernelIsDispatchEnabled()) { - DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatCB(stat = %08x, timeout = %d): dispatch disabled", stat, timeout); - return SCE_KERNEL_ERROR_CAN_NOT_WAIT; + return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_CAN_NOT_WAIT, "dispatch disabled"); } if (__IsInInterrupt()) { - DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatCB(stat = %08x, timeout = %d): inside interrupt", stat, timeout); - return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT; + return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_ILLEGAL_CONTEXT, "inside interrupt"); } + hleEatCycles(520); hleCheckCurrentCallbacks(); if ((stat & __KernelUmdGetState()) == 0) { - DEBUG_LOG(SCEIO, "0=sceUmdWaitDriveStatCB(stat = %08x, timeout = %d): waiting", stat, timeout); + DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatCB(stat = %08x, timeout = %d): waiting", stat, timeout); if (timeout == 0) { timeout = 8000; } @@ -471,8 +459,7 @@ static int sceUmdWaitDriveStatCB(u32 stat, u32 timeout) hleReSchedule("umd stat waited"); } - DEBUG_LOG(SCEIO, "0=sceUmdWaitDriveStatCB(stat = %08x, timeout = %d)", stat, timeout); - return 0; + return hleLogSuccessI(SCEIO, 0); } static u32 sceUmdCancelWaitDriveStat() @@ -495,7 +482,7 @@ static u32 sceUmdGetErrorStat() return umdErrorStat; } -void __UmdReplace(std::string filepath) { +void __UmdReplace(Path filepath) { std::string error = ""; if (!UmdReplace(filepath, error)) { ERROR_LOG(SCEIO, "UMD Replace failed: %s", error.c_str()); @@ -503,6 +490,9 @@ void __UmdReplace(std::string filepath) { } UMDInserted = false; + // Wake any threads waiting for the disc to be removed. + UmdWakeThreads(); + CoreTiming::ScheduleEvent(usToCycles(200*1000), umdInsertChangeEvent, 0); // Wait sceUmdCheckMedium call // TODO Is this always correct if UMD was not activated? u32 notifyArg = PSP_UMD_PRESENT | PSP_UMD_READABLE | PSP_UMD_CHANGED; @@ -536,7 +526,7 @@ static u32 sceUmdReplacePermit() const HLEFunction sceUmdUser[] = { - {0XC6183D47, &WrapI_UC, "sceUmdActivate", 'i', "xs"}, + {0XC6183D47, &WrapI_UC, "sceUmdActivate", 'i', "is"}, {0X6B4A146C, &WrapU_V, "sceUmdGetDriveStat", 'x', "" }, {0X46EBB729, &WrapI_V, "sceUmdCheckMedium", 'i', "" }, {0XE83742BA, &WrapI_UC, "sceUmdDeactivate", 'i', "xs"}, diff --git a/Core/HLE/sceUmd.h b/Core/HLE/sceUmd.h index 1fdf4bbc28..91e9bcafd6 100644 --- a/Core/HLE/sceUmd.h +++ b/Core/HLE/sceUmd.h @@ -17,6 +17,8 @@ #pragma once +#include "Common/File/Path.h" + enum pspUmdState { PSP_UMD_INIT = 0x00, PSP_UMD_NOT_PRESENT = 0x01, @@ -41,7 +43,7 @@ enum pspUmdType { void __UmdInit(); void __UmdDoState(PointerWrap &p); -void __UmdReplace(std::string filepath); +void __UmdReplace(Path filepath); bool getUMDReplacePermit(); void Register_sceUmdUser(); diff --git a/Core/HLE/sceUsb.cpp b/Core/HLE/sceUsb.cpp index 14c017f8fa..d47192a995 100644 --- a/Core/HLE/sceUsb.cpp +++ b/Core/HLE/sceUsb.cpp @@ -212,6 +212,10 @@ static int sceUsbWaitStateCB(int state, u32 waitMode, u32 timeoutPtr) { return 0; } +static int sceUsbstorBootSetCapacity(u32 capacity) { + return hleReportError(HLE, 0, "unimplemented"); +} + const HLEFunction sceUsb[] = { {0XAE5DE6AF, &WrapI_CUU, "sceUsbStart", 'i', "sxx"}, @@ -233,7 +237,7 @@ const HLEFunction sceUsbstor[] = const HLEFunction sceUsbstorBoot[] = { - {0XE58818A8, nullptr, "sceUsbstorBootSetCapacity", '?', "" }, + {0XE58818A8, &WrapI_U,"sceUsbstorBootSetCapacity", 'i', "x" }, {0X594BBF95, nullptr, "sceUsbstorBootSetLoadAddr", '?', "" }, {0X6D865ECD, nullptr, "sceUsbstorBootGetDataSize", '?', "" }, {0XA1119F0D, nullptr, "sceUsbstorBootSetStatus", '?', "" }, diff --git a/Core/HLE/sceUtility.cpp b/Core/HLE/sceUtility.cpp index e0aa43ec83..f7a8d4e53f 100644 --- a/Core/HLE/sceUtility.cpp +++ b/Core/HLE/sceUtility.cpp @@ -123,14 +123,14 @@ static const ModuleLoadInfo moduleLoadInfo[] = { // Only a single dialog is allowed at a time. static UtilityDialogType currentDialogType; bool currentDialogActive; -static PSPSaveDialog saveDialog(UtilityDialogType::SAVEDATA); -static PSPMsgDialog msgDialog(UtilityDialogType::MSG); -static PSPOskDialog oskDialog(UtilityDialogType::OSK); -static PSPNetconfDialog netDialog(UtilityDialogType::NET); -static PSPScreenshotDialog screenshotDialog(UtilityDialogType::SCREENSHOT); -static PSPGamedataInstallDialog gamedataInstallDialog(UtilityDialogType::GAMEDATAINSTALL); +static PSPSaveDialog *saveDialog; +static PSPMsgDialog *msgDialog; +static PSPOskDialog *oskDialog; +static PSPNetconfDialog *netDialog; +static PSPScreenshotDialog *screenshotDialog; +static PSPGamedataInstallDialog *gamedataInstallDialog; -static int oldStatus = 100; //random value +static int oldStatus = -1; static std::map currentlyLoadedModules; static int volatileUnlockEvent = -1; static HLEHelperThread *accessThread = nullptr; @@ -146,6 +146,8 @@ static void ActivateDialog(UtilityDialogType type) { if (!currentDialogActive) { currentDialogType = type; currentDialogActive = true; + // So that we log the next one. + oldStatus = -1; } CleanupDialogThreads(); } @@ -157,17 +159,42 @@ static void DeactivateDialog() { CleanupDialogThreads(); } +static PSPDialog *CurrentDialog(UtilityDialogType type) { + switch (type) { + case UtilityDialogType::NONE: + break; + case UtilityDialogType::SAVEDATA: + return saveDialog; + case UtilityDialogType::MSG: + return msgDialog; + case UtilityDialogType::OSK: + return oskDialog; + case UtilityDialogType::NET: + return netDialog; + case UtilityDialogType::SCREENSHOT: + return screenshotDialog; + case UtilityDialogType::GAMESHARING: + break; + case UtilityDialogType::GAMEDATAINSTALL: + return gamedataInstallDialog; + } + return nullptr; +} + static void UtilityVolatileUnlock(u64 userdata, int cyclesLate) { - // There can only be one active, so just try each of them. - saveDialog.FinishVolatile(); - msgDialog.FinishVolatile(); - oskDialog.FinishVolatile(); - netDialog.FinishVolatile(); - screenshotDialog.FinishVolatile(); - gamedataInstallDialog.FinishVolatile(); + PSPDialog *dialog = CurrentDialog(currentDialogType); + if (dialog) + dialog->FinishVolatile(); } void __UtilityInit() { + saveDialog = new PSPSaveDialog(UtilityDialogType::SAVEDATA); + msgDialog = new PSPMsgDialog(UtilityDialogType::MSG); + oskDialog = new PSPOskDialog(UtilityDialogType::OSK); + netDialog = new PSPNetconfDialog(UtilityDialogType::NET); + screenshotDialog = new PSPScreenshotDialog(UtilityDialogType::SCREENSHOT); + gamedataInstallDialog = new PSPGamedataInstallDialog(UtilityDialogType::GAMEDATAINSTALL); + currentDialogType = UtilityDialogType::NONE; DeactivateDialog(); SavedataParam::Init(); @@ -183,12 +210,12 @@ void __UtilityDoState(PointerWrap &p) { Do(p, currentDialogType); Do(p, currentDialogActive); - saveDialog.DoState(p); - msgDialog.DoState(p); - oskDialog.DoState(p); - netDialog.DoState(p); - screenshotDialog.DoState(p); - gamedataInstallDialog.DoState(p); + saveDialog->DoState(p); + msgDialog->DoState(p); + oskDialog->DoState(p); + netDialog->DoState(p); + screenshotDialog->DoState(p); + gamedataInstallDialog->DoState(p); if (s >= 2) { Do(p, currentlyLoadedModules); @@ -225,17 +252,54 @@ void __UtilityDoState(PointerWrap &p) { } void __UtilityShutdown() { - saveDialog.Shutdown(true); - msgDialog.Shutdown(true); - oskDialog.Shutdown(true); - netDialog.Shutdown(true); - screenshotDialog.Shutdown(true); - gamedataInstallDialog.Shutdown(true); + saveDialog->Shutdown(true); + msgDialog->Shutdown(true); + oskDialog->Shutdown(true); + netDialog->Shutdown(true); + screenshotDialog->Shutdown(true); + gamedataInstallDialog->Shutdown(true); if (accessThread) { delete accessThread; accessThread = nullptr; } + + delete saveDialog; + delete msgDialog; + delete oskDialog; + delete netDialog; + delete screenshotDialog; + delete gamedataInstallDialog; +} + +void UtilityDialogInitialize(UtilityDialogType type, int delayUs, int priority) { + int partDelay = delayUs / 4; + const u32_le insts[] = { + // Make sure we don't discard/deadbeef 'em. + (u32_le)MIPS_MAKE_ORI(MIPS_REG_S0, MIPS_REG_A0, 0), + (u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityWorkUs"), + (u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_S0, 0), + (u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityWorkUs"), + (u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_S0, 0), + (u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityWorkUs"), + (u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_S0, 0), + (u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityWorkUs"), + + // Now actually lock the volatile memory. Maybe this should be earlier... + (u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_ZERO, 0), + (u32_le)MIPS_MAKE_ORI(MIPS_REG_A1, MIPS_REG_ZERO, 0), + (u32_le)MIPS_MAKE_ORI(MIPS_REG_A2, MIPS_REG_ZERO, 0), + (u32_le)MIPS_MAKE_SYSCALL("sceSuspendForUser", "sceKernelVolatileMemLock"), + + (u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_ZERO, (int)type), + (u32_le)MIPS_MAKE_JR_RA(), + (u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityInitDialog"), + }; + + CleanupDialogThreads(); + _assert_(accessThread == nullptr); + accessThread = new HLEHelperThread("ScePafJob", insts, (uint32_t)ARRAY_SIZE(insts), priority, 0x200); + accessThread->Start(partDelay, 0); } void UtilityDialogShutdown(UtilityDialogType type, int delayUs, int priority) { @@ -267,30 +331,26 @@ void UtilityDialogShutdown(UtilityDialogType type, int delayUs, int priority) { static int UtilityWorkUs(int us) { // This blocks, but other better priority threads can get time. // Simulate this by allowing a reschedule. + if (us > 1000) { + hleEatMicro(1000); + return hleDelayResult(0, "utility work", us - 1000); + } hleEatMicro(us); hleReSchedule("utility work"); return 0; } +static int UtilityInitDialog(int type) { + PSPDialog *dialog = CurrentDialog((UtilityDialogType)type); + if (dialog) + return hleLogSuccessI(SCEUTILITY, dialog->FinishInit()); + return hleLogError(SCEUTILITY, 0, "invalid dialog type?"); +} + static int UtilityFinishDialog(int type) { - switch ((UtilityDialogType)type) { - case UtilityDialogType::NONE: - break; - case UtilityDialogType::SAVEDATA: - return hleLogSuccessI(SCEUTILITY, saveDialog.FinishShutdown()); - case UtilityDialogType::MSG: - return hleLogSuccessI(SCEUTILITY, msgDialog.FinishShutdown()); - case UtilityDialogType::OSK: - return hleLogSuccessI(SCEUTILITY, oskDialog.FinishShutdown()); - case UtilityDialogType::NET: - return hleLogSuccessI(SCEUTILITY, netDialog.FinishShutdown()); - case UtilityDialogType::SCREENSHOT: - return hleLogSuccessI(SCEUTILITY, screenshotDialog.FinishShutdown()); - case UtilityDialogType::GAMESHARING: - return hleLogError(SCEUTILITY, -1, "unimplemented"); - case UtilityDialogType::GAMEDATAINSTALL: - return hleLogSuccessI(SCEUTILITY, gamedataInstallDialog.FinishShutdown()); - } + PSPDialog *dialog = CurrentDialog((UtilityDialogType)type); + if (dialog) + return hleLogSuccessI(SCEUTILITY, dialog->FinishShutdown()); return hleLogError(SCEUTILITY, 0, "invalid dialog type?"); } @@ -299,16 +359,12 @@ static int sceUtilitySavedataInitStart(u32 paramAddr) { if (PSP_CoreParameter().compat.flags().YugiohSaveFix) { WARN_LOG(SCEUTILITY, "Yugioh Savedata Correction"); } else { - WARN_LOG(SCEUTILITY, "sceUtilitySavedataInitStart(%08x): wrong dialog type", paramAddr); - return SCE_ERROR_UTILITY_WRONG_TYPE; + return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } } - oldStatus = 100; ActivateDialog(UtilityDialogType::SAVEDATA); - int ret = saveDialog.Init(paramAddr); - DEBUG_LOG(SCEUTILITY,"%08x=sceUtilitySavedataInitStart(%08x)",ret,paramAddr); - return ret; + return hleLogSuccessX(SCEUTILITY, saveDialog->Init(paramAddr)); } static int sceUtilitySavedataShutdownStart() { @@ -316,36 +372,33 @@ static int sceUtilitySavedataShutdownStart() { return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); DeactivateDialog(); - int ret = saveDialog.Shutdown(); + int ret = saveDialog->Shutdown(); hleEatCycles(30000); return hleLogSuccessX(SCEUTILITY, ret); } static int sceUtilitySavedataGetStatus() { if (currentDialogType != UtilityDialogType::SAVEDATA) { - DEBUG_LOG(SCEUTILITY, "sceUtilitySavedataGetStatus(): wrong dialog type"); hleEatCycles(200); - return SCE_ERROR_UTILITY_WRONG_TYPE; + return hleLogDebug(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } - int status = saveDialog.GetStatus(); - if (oldStatus != status) { - oldStatus = status; - DEBUG_LOG(SCEUTILITY, "%08x=sceUtilitySavedataGetStatus()", status); - } + int status = saveDialog->GetStatus(); hleEatCycles(200); CleanupDialogThreads(); - return status; + if (oldStatus != status) { + oldStatus = status; + return hleLogSuccessI(SCEUTILITY, status); + } + return hleLogSuccessVerboseI(SCEUTILITY, status); } static int sceUtilitySavedataUpdate(int animSpeed) { if (currentDialogType != UtilityDialogType::SAVEDATA) { - WARN_LOG(SCEUTILITY, "sceUtilitySavedataUpdate(%i): wrong dialog type", animSpeed); - return SCE_ERROR_UTILITY_WRONG_TYPE; + return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } - int result = saveDialog.Update(animSpeed); - DEBUG_LOG(SCEUTILITY,"%08x=sceUtilitySavedataUpdate(%i)", result, animSpeed); + int result = hleLogSuccessI(SCEUTILITY, saveDialog->Update(animSpeed)); if (result >= 0) return hleDelayResult(result, "savedata update", 300); return result; @@ -436,37 +489,28 @@ static u32 sceUtilityUnloadModule(u32 module) { static int sceUtilityMsgDialogInitStart(u32 paramAddr) { if (currentDialogActive && currentDialogType != UtilityDialogType::MSG) { - WARN_LOG(SCEUTILITY, "sceUtilityMsgDialogInitStart(%08x): wrong dialog type", paramAddr); - return SCE_ERROR_UTILITY_WRONG_TYPE; + return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } - oldStatus = 100; ActivateDialog(UtilityDialogType::MSG); - int ret = msgDialog.Init(paramAddr); - INFO_LOG(SCEUTILITY, "%08x=sceUtilityMsgDialogInitStart(%08x)", ret, paramAddr); - return ret; + return hleLogSuccessInfoX(SCEUTILITY, msgDialog->Init(paramAddr)); } static int sceUtilityMsgDialogShutdownStart() { if (currentDialogType != UtilityDialogType::MSG) { - WARN_LOG(SCEUTILITY, "sceUtilityMsgDialogShutdownStart(): wrong dialog type"); - return SCE_ERROR_UTILITY_WRONG_TYPE; + return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } DeactivateDialog(); - int ret = msgDialog.Shutdown(); - DEBUG_LOG(SCEUTILITY, "%08x=sceUtilityMsgDialogShutdownStart()", ret); - return ret; + return hleLogSuccessX(SCEUTILITY, msgDialog->Shutdown()); } static int sceUtilityMsgDialogUpdate(int animSpeed) { if (currentDialogType != UtilityDialogType::MSG) { - WARN_LOG(SCEUTILITY, "sceUtilityMsgDialogUpdate(%i): wrong dialog type", animSpeed); - return SCE_ERROR_UTILITY_WRONG_TYPE; + return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } - int ret = msgDialog.Update(animSpeed); - DEBUG_LOG(SCEUTILITY,"%08x=sceUtilityMsgDialogUpdate(%i)", ret, animSpeed); + int ret = hleLogSuccessX(SCEUTILITY, msgDialog->Update(animSpeed)); if (ret >= 0) return hleDelayResult(ret, "msgdialog update", 800); return ret; @@ -474,82 +518,66 @@ static int sceUtilityMsgDialogUpdate(int animSpeed) { static int sceUtilityMsgDialogGetStatus() { if (currentDialogType != UtilityDialogType::MSG) { - DEBUG_LOG(SCEUTILITY, "sceUtilityMsgDialogGetStatus(): wrong dialog type"); - return SCE_ERROR_UTILITY_WRONG_TYPE; + return hleLogDebug(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } - int status = msgDialog.GetStatus(); + int status = msgDialog->GetStatus(); + CleanupDialogThreads(); if (oldStatus != status) { oldStatus = status; - DEBUG_LOG(SCEUTILITY, "%08x=sceUtilityMsgDialogGetStatus()", status); + return hleLogSuccessI(SCEUTILITY, status); } - CleanupDialogThreads(); - return status; + return hleLogSuccessVerboseI(SCEUTILITY, status); } static int sceUtilityMsgDialogAbort() { if (currentDialogType != UtilityDialogType::MSG) { - WARN_LOG(SCEUTILITY, "sceUtilityMsgDialogAbort(): wrong dialog type"); - return SCE_ERROR_UTILITY_WRONG_TYPE; + return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } - int ret = msgDialog.Abort(); - DEBUG_LOG(SCEUTILITY, "%08x=sceUtilityMsgDialogAbort()", ret); - return ret; + return hleLogSuccessX(SCEUTILITY, msgDialog->Abort()); } // On screen keyboard static int sceUtilityOskInitStart(u32 oskPtr) { if (currentDialogActive && currentDialogType != UtilityDialogType::OSK) { - WARN_LOG(SCEUTILITY, "sceUtilityOskInitStart(%08x): wrong dialog type", oskPtr); - return SCE_ERROR_UTILITY_WRONG_TYPE; + return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } - oldStatus = 100; ActivateDialog(UtilityDialogType::OSK); - int ret = oskDialog.Init(oskPtr); - INFO_LOG(SCEUTILITY, "%08x=sceUtilityOskInitStart(%08x)", ret, oskPtr); - return ret; + return hleLogSuccessInfoX(SCEUTILITY, oskDialog->Init(oskPtr)); } static int sceUtilityOskShutdownStart() { - if (currentDialogType != UtilityDialogType::OSK) - { - WARN_LOG(SCEUTILITY, "sceUtilityOskShutdownStart(): wrong dialog type"); - return SCE_ERROR_UTILITY_WRONG_TYPE; + if (currentDialogType != UtilityDialogType::OSK) { + return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } DeactivateDialog(); - int ret = oskDialog.Shutdown(); - DEBUG_LOG(SCEUTILITY, "%08x=sceUtilityOskShutdownStart()",ret); - return ret; + return hleLogSuccessX(SCEUTILITY, oskDialog->Shutdown()); } static int sceUtilityOskUpdate(int animSpeed) { if (currentDialogType != UtilityDialogType::OSK) { - WARN_LOG(SCEUTILITY, "sceUtilityOskUpdate(%i): wrong dialog type", animSpeed); - return SCE_ERROR_UTILITY_WRONG_TYPE; + return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } - int ret = oskDialog.Update(animSpeed); - DEBUG_LOG(SCEUTILITY, "%08x=sceUtilityOskUpdate(%i)", ret, animSpeed); - return ret; + return hleLogSuccessX(SCEUTILITY, oskDialog->Update(animSpeed)); } static int sceUtilityOskGetStatus() { if (currentDialogType != UtilityDialogType::OSK) { - DEBUG_LOG(SCEUTILITY, "sceUtilityOskGetStatus(): wrong dialog type"); - return SCE_ERROR_UTILITY_WRONG_TYPE; + return hleLogDebug(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } - int status = oskDialog.GetStatus(); + int status = oskDialog->GetStatus(); + CleanupDialogThreads(); if (oldStatus != status) { oldStatus = status; - DEBUG_LOG(SCEUTILITY, "%08x=sceUtilityOskGetStatus()", status); + return hleLogSuccessI(SCEUTILITY, status); } - CleanupDialogThreads(); - return status; + return hleLogSuccessVerboseI(SCEUTILITY, status); } @@ -558,9 +586,8 @@ static int sceUtilityNetconfInitStart(u32 paramsAddr) { return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } - oldStatus = 100; ActivateDialog(UtilityDialogType::NET); - return hleLogSuccessInfoI(SCEUTILITY, netDialog.Init(paramsAddr)); + return hleLogSuccessInfoI(SCEUTILITY, netDialog->Init(paramsAddr)); } static int sceUtilityNetconfShutdownStart() { @@ -569,7 +596,7 @@ static int sceUtilityNetconfShutdownStart() { } DeactivateDialog(); - return hleLogSuccessI(SCEUTILITY, netDialog.Shutdown()); + return hleLogSuccessI(SCEUTILITY, netDialog->Shutdown()); } static int sceUtilityNetconfUpdate(int animSpeed) { @@ -577,7 +604,7 @@ static int sceUtilityNetconfUpdate(int animSpeed) { return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } - return hleLogSuccessI(SCEUTILITY, netDialog.Update(animSpeed)); + return hleLogSuccessI(SCEUTILITY, netDialog->Update(animSpeed)); } static int sceUtilityNetconfGetStatus() { @@ -586,12 +613,12 @@ static int sceUtilityNetconfGetStatus() { return hleLogDebug(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } - int status = netDialog.GetStatus(); + int status = netDialog->GetStatus(); + CleanupDialogThreads(); if (oldStatus != status) { oldStatus = status; return hleLogSuccessI(SCEUTILITY, status); } - CleanupDialogThreads(); return hleLogSuccessVerboseI(SCEUTILITY, status); } @@ -608,123 +635,96 @@ static int sceUtilityCheckNetParam(int id) //but it requires more investigation static int sceUtilityScreenshotInitStart(u32 paramAddr) { if (currentDialogActive && currentDialogType != UtilityDialogType::SCREENSHOT) { - WARN_LOG(SCEUTILITY, "sceUtilityScreenshotInitStart(%08x): wrong dialog type", paramAddr); - return SCE_ERROR_UTILITY_WRONG_TYPE; + return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } - oldStatus = 100; ActivateDialog(UtilityDialogType::SCREENSHOT); - u32 retval = screenshotDialog.Init(paramAddr); - WARN_LOG_REPORT(SCEUTILITY, "%08x=sceUtilityScreenshotInitStart(%08x)", retval, paramAddr); - return retval; + return hleReportWarning(SCEUTILITY, screenshotDialog->Init(paramAddr)); } static int sceUtilityScreenshotShutdownStart() { if (currentDialogType != UtilityDialogType::SCREENSHOT) { - WARN_LOG(SCEUTILITY, "sceUtilityScreenshotShutdownStart(): wrong dialog type"); - return SCE_ERROR_UTILITY_WRONG_TYPE; + return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } DeactivateDialog(); - int ret = screenshotDialog.Shutdown(); - WARN_LOG(SCEUTILITY, "%08x=sceUtilityScreenshotShutdownStart()", ret); - return ret; + return hleLogWarning(SCEUTILITY, screenshotDialog->Shutdown()); } static int sceUtilityScreenshotUpdate(u32 animSpeed) { if (currentDialogType != UtilityDialogType::SCREENSHOT) { - WARN_LOG(SCEUTILITY, "sceUtilityScreenshotUpdate(%i): wrong dialog type", animSpeed); - return SCE_ERROR_UTILITY_WRONG_TYPE; + return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } - int ret = screenshotDialog.Update(animSpeed); - WARN_LOG(SCEUTILITY, "%08x=sceUtilityScreenshotUpdate(%i)", ret, animSpeed); - return ret; + return hleLogWarning(SCEUTILITY, screenshotDialog->Update(animSpeed)); } static int sceUtilityScreenshotGetStatus() { if (currentDialogType != UtilityDialogType::SCREENSHOT) { - DEBUG_LOG(SCEUTILITY, "sceUtilityScreenshotGetStatus(): wrong dialog type"); - return SCE_ERROR_UTILITY_WRONG_TYPE; + return hleLogDebug(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } - int status = screenshotDialog.GetStatus(); + int status = screenshotDialog->GetStatus(); + CleanupDialogThreads(); if (oldStatus != status) { oldStatus = status; - WARN_LOG(SCEUTILITY, "%08x=sceUtilityScreenshotGetStatus()", status); + return hleLogWarning(SCEUTILITY, status); } - CleanupDialogThreads(); - return status; + return hleLogSuccessVerboseI(SCEUTILITY, status); } static int sceUtilityScreenshotContStart(u32 paramAddr) { if (currentDialogType != UtilityDialogType::SCREENSHOT) { - WARN_LOG(SCEUTILITY, "sceUtilityScreenshotContStart(): wrong dialog type"); - return SCE_ERROR_UTILITY_WRONG_TYPE; + return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } - int ret = screenshotDialog.ContStart(); - WARN_LOG(SCEUTILITY, "%08x=sceUtilityScreenshotContStart(%08x)", ret, paramAddr); - return ret; + return hleLogWarning(SCEUTILITY, screenshotDialog->ContStart()); } static int sceUtilityGamedataInstallInitStart(u32 paramsAddr) { if (currentDialogActive && currentDialogType != UtilityDialogType::GAMEDATAINSTALL) { - WARN_LOG(SCEUTILITY, "sceUtilityGamedataInstallInitStart(%08x): wrong dialog type", paramsAddr); - return SCE_ERROR_UTILITY_WRONG_TYPE; + return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } ActivateDialog(UtilityDialogType::GAMEDATAINSTALL); - int ret = gamedataInstallDialog.Init(paramsAddr); - INFO_LOG(SCEUTILITY, "%08x=sceUtilityGamedataInstallInitStart(%08x)",ret,paramsAddr); - return ret; + return hleLogSuccessInfoX(SCEUTILITY, gamedataInstallDialog->Init(paramsAddr)); } static int sceUtilityGamedataInstallShutdownStart() { if (currentDialogType != UtilityDialogType::GAMEDATAINSTALL) { - WARN_LOG(SCEUTILITY, "sceUtilityGamedataInstallShutdownStart(): wrong dialog type"); - return SCE_ERROR_UTILITY_WRONG_TYPE; + return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } DeactivateDialog(); - DEBUG_LOG(SCEUTILITY, "sceUtilityGamedataInstallShutdownStart()"); - return gamedataInstallDialog.Shutdown(); + return hleLogSuccessX(SCEUTILITY, gamedataInstallDialog->Shutdown()); } static int sceUtilityGamedataInstallUpdate(int animSpeed) { if (currentDialogType != UtilityDialogType::GAMEDATAINSTALL) { - WARN_LOG(SCEUTILITY, "sceUtilityGamedataInstallUpdate(%i): wrong dialog type", animSpeed); - return SCE_ERROR_UTILITY_WRONG_TYPE; + return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } - int ret = gamedataInstallDialog.Update(animSpeed); - DEBUG_LOG(SCEUTILITY, "%08x=sceUtilityGamedataInstallUpdate(%i)", ret, animSpeed); - return ret; + return hleLogSuccessX(SCEUTILITY, gamedataInstallDialog->Update(animSpeed)); } static int sceUtilityGamedataInstallGetStatus() { if (currentDialogType != UtilityDialogType::GAMEDATAINSTALL) { // This is called incorrectly all the time by some games. So let's not bother warning. - // WARN_LOG(SCEUTILITY, "sceUtilityGamedataInstallGetStatus(): wrong dialog type"); - return SCE_ERROR_UTILITY_WRONG_TYPE; + return hleLogDebug(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } - int status = gamedataInstallDialog.GetStatus(); - DEBUG_LOG(SCEUTILITY, "%08x=sceUtilityGamedataInstallGetStatus()", status); + int status = gamedataInstallDialog->GetStatus(); CleanupDialogThreads(); - return status; + return hleLogSuccessI(SCEUTILITY, status); } static int sceUtilityGamedataInstallAbort() { if (currentDialogType != UtilityDialogType::GAMEDATAINSTALL) { - WARN_LOG(SCEUTILITY, "sceUtilityGamedataInstallAbort(): wrong dialog type"); - return SCE_ERROR_UTILITY_WRONG_TYPE; + return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); } DeactivateDialog(); - int ret = gamedataInstallDialog.Abort(); - DEBUG_LOG(SCEUTILITY, "%08x=sceUtilityGamedataInstallDialogAbort",ret); - return ret; + return hleLogSuccessX(SCEUTILITY, gamedataInstallDialog->Abort()); } //TODO: should save to config file @@ -1084,6 +1084,7 @@ const HLEFunction sceUtility[] = // Fake functions for PPSSPP's use. {0xC0DE0001, &WrapI_I, "__UtilityFinishDialog", 'i', "i" }, {0xC0DE0002, &WrapI_I, "__UtilityWorkUs", 'i', "i" }, + {0xC0DE0003, &WrapI_I, "__UtilityInitDialog", 'i', "i" }, }; void Register_sceUtility() diff --git a/Core/HLE/sceUtility.h b/Core/HLE/sceUtility.h index 1cc4798a0a..510b22ea72 100644 --- a/Core/HLE/sceUtility.h +++ b/Core/HLE/sceUtility.h @@ -94,6 +94,7 @@ void __UtilityInit(); void __UtilityDoState(PointerWrap &p); void __UtilityShutdown(); +void UtilityDialogInitialize(UtilityDialogType type, int delayUs, int priority); void UtilityDialogShutdown(UtilityDialogType type, int delayUs, int priority); void Register_sceUtility(); diff --git a/Core/HW/Camera.cpp b/Core/HW/Camera.cpp index 0339fe4d67..8c8d61d82b 100644 --- a/Core/HW/Camera.cpp +++ b/Core/HW/Camera.cpp @@ -223,7 +223,7 @@ cont: } void *v4l_loop(void *data) { - setCurrentThreadName("v4l_loop"); + SetCurrentThreadName("v4l_loop"); while (v4l_fd >= 0) { struct v4l2_buffer buf; memset(&buf, 0, sizeof(buf)); diff --git a/Core/HW/MemoryStick.cpp b/Core/HW/MemoryStick.cpp index f2520085bd..50f46a9321 100644 --- a/Core/HW/MemoryStick.cpp +++ b/Core/HW/MemoryStick.cpp @@ -16,6 +16,9 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include +#include +#include #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" #include "Core/CoreTiming.h" @@ -31,8 +34,20 @@ static bool memStickNeedsAssign = false; static u64 memStickInsertedAt = 0; static uint64_t memstickInitialFree = 0; -const u64 normalMemstickSize = 9ULL * 1024 * 1024 * 1024; -const u64 smallMemstickSize = 1ULL * 1024 * 1024 * 1024; +enum FreeCalcStatus { + NONE, + RUNNING, + DONE, + CLEANED_UP, +}; + +static std::thread freeCalcThread; +static std::condition_variable freeCalcCond; +static std::mutex freeCalcMutex; +static FreeCalcStatus freeCalcStatus = FreeCalcStatus::NONE; + +static const u64 normalMemstickSize = 9ULL * 1024 * 1024 * 1024; +static const u64 smallMemstickSize = 1ULL * 1024 * 1024 * 1024; void MemoryStick_DoState(PointerWrap &p) { auto s = p.Section("MemoryStick", 1, 5); @@ -75,7 +90,31 @@ u64 MemoryStick_SectorSize() { return 32 * 1024; // 32KB } +static void MemoryStick_CalcInitialFree() { + std::unique_lock guard(freeCalcMutex); + freeCalcStatus = FreeCalcStatus::RUNNING; + freeCalcThread = std::thread([] { + memstickInitialFree = pspFileSystem.FreeSpace("ms0:/") + pspFileSystem.getDirSize("ms0:/PSP/SAVEDATA/"); + + std::unique_lock guard(freeCalcMutex); + freeCalcStatus = FreeCalcStatus::DONE; + freeCalcCond.notify_all(); + }); +} + +static void MemoryStick_WaitInitialFree() { + std::unique_lock guard(freeCalcMutex); + while (freeCalcStatus == FreeCalcStatus::RUNNING) { + freeCalcCond.wait(guard); + } + if (freeCalcStatus == FreeCalcStatus::DONE) + freeCalcThread.join(); + freeCalcStatus = FreeCalcStatus::CLEANED_UP; +} + u64 MemoryStick_FreeSpace() { + MemoryStick_WaitInitialFree(); + const CompatFlags &flags = PSP_CoreParameter().compat.flags(); u64 realFreeSpace = pspFileSystem.FreeSpace("ms0:/"); @@ -135,5 +174,9 @@ void MemoryStick_Init() { } memStickNeedsAssign = false; - memstickInitialFree = pspFileSystem.FreeSpace("ms0:/") + pspFileSystem.getDirSize("ms0:/PSP/SAVEDATA/"); + MemoryStick_CalcInitialFree(); +} + +void MemoryStick_Shutdown() { + MemoryStick_WaitInitialFree(); } diff --git a/Core/HW/MemoryStick.h b/Core/HW/MemoryStick.h index f8bd295aba..481acf57b8 100644 --- a/Core/HW/MemoryStick.h +++ b/Core/HW/MemoryStick.h @@ -41,6 +41,7 @@ enum MemStickDriverState { }; void MemoryStick_Init(); +void MemoryStick_Shutdown(); void MemoryStick_DoState(PointerWrap &p); MemStickState MemoryStick_State(); MemStickFatState MemoryStick_FatState(); diff --git a/Core/HW/SasAudio.cpp b/Core/HW/SasAudio.cpp index b6f359845d..90ed9eed8a 100644 --- a/Core/HW/SasAudio.cpp +++ b/Core/HW/SasAudio.cpp @@ -149,6 +149,8 @@ void VagDecoder::GetSamples(s16 *outSamples, int numSamples) { } if (readp > origp) { + if (MemBlockInfoDetailed()) + NotifyMemInfo(MemBlockFlags::READ, read_, readp - origp, "SasVagDecoder"); read_ += readp - origp; } } @@ -426,7 +428,7 @@ void SasVoice::ReadSamples(s16 *output, int numSamples) { pcmIndex = 0; break; } - Memory::Memcpy(out, pcmAddr + pcmIndex * sizeof(s16), size * sizeof(s16)); + Memory::Memcpy(out, pcmAddr + pcmIndex * sizeof(s16), size * sizeof(s16), "SasVoicePCM"); pcmIndex += size; needed -= size; out += size; @@ -581,6 +583,11 @@ void SasInstance::Mix(u32 outAddr, u32 inAddr, int leftVol, int rightVol) { if (outputMode == PSP_SAS_OUTPUTMODE_MIXED) { // Okay, apply effects processing to the Send buffer. WriteMixedOutput(outp, inp, leftVol, rightVol); + if (MemBlockInfoDetailed()) { + if (inp) + NotifyMemInfo(MemBlockFlags::READ, inAddr, grainSize * sizeof(u16) * 2, "SasMix"); + NotifyMemInfo(MemBlockFlags::WRITE, outAddr, grainSize * sizeof(u16) * 2, "SasMix"); + } } else { s16 *outpL = outp + grainSize * 0; s16 *outpR = outp + grainSize * 1; @@ -593,6 +600,7 @@ void SasInstance::Mix(u32 outAddr, u32 inAddr, int leftVol, int rightVol) { *outpSendL++ = clamp_s16(sendBuffer[i + 0]); *outpSendR++ = clamp_s16(sendBuffer[i + 1]); } + NotifyMemInfo(MemBlockFlags::WRITE, outAddr, grainSize * sizeof(u16) * 4, "SasMix"); } memset(mixBuffer, 0, grainSize * sizeof(int) * 2); memset(sendBuffer, 0, grainSize * sizeof(int) * 2); diff --git a/Core/Instance.cpp b/Core/Instance.cpp index a36aca0049..e8a5590dbd 100644 --- a/Core/Instance.cpp +++ b/Core/Instance.cpp @@ -25,14 +25,13 @@ #include #endif -#include "Common/Log.h" - #if PPSSPP_PLATFORM(WINDOWS) - #include "Common/CommonWindows.h" - #endif +#include "Common/Log.h" +#include "Common/SysError.h" + #include uint8_t PPSSPP_ID = 0; @@ -65,7 +64,8 @@ static bool UpdateInstanceCounter(void (*callback)(volatile InstanceInfo *)) { sizeof(InstanceInfo)); if (!buf) { - ERROR_LOG(SCENET, "Could not map view of file %s (%08x)", ID_SHM_NAME, (uint32_t)GetLastError()); + auto err = GetLastError(); + ERROR_LOG(SCENET, "Could not map view of file %s, %08x %s", ID_SHM_NAME, (uint32_t)err, GetStringErrorMsg(err).c_str()); return false; } @@ -137,7 +137,7 @@ void InitInstanceCounter() { DWORD lasterr = GetLastError(); if (!hIDMapFile) { - ERROR_LOG(SCENET, "Could not create %s file mapping object (%08x)", ID_SHM_NAME, (uint32_t)lasterr); + ERROR_LOG(SCENET, "Could not create %s file mapping object, %08x %s", ID_SHM_NAME, (uint32_t)lasterr, GetStringErrorMsg(lasterr).c_str()); PPSSPP_ID = 1; return; } diff --git a/Core/KeyMap.cpp b/Core/KeyMap.cpp index f127336228..5ddfd361dd 100644 --- a/Core/KeyMap.cpp +++ b/Core/KeyMap.cpp @@ -15,8 +15,9 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include #include +#include +#include #if defined(SDL) #include @@ -49,6 +50,7 @@ struct DefMappingStruct { KeyMapping g_controllerMap; int g_controllerMapGeneration = 0; std::set g_seenPads; +static std::set g_seenDeviceIds; bool g_swapped_keys = false; @@ -356,6 +358,13 @@ void UpdateNativeMenuKeys() { SetDPadKeys(upKeys, downKeys, leftKeys, rightKeys); SetConfirmCancelKeys(confirmKeys, cancelKeys); SetTabLeftRightKeys(tabLeft, tabRight); + + std::unordered_map flipYByDeviceId; + for (int deviceId : g_seenDeviceIds) { + auto analogs = MappedAxesForDevice(deviceId); + flipYByDeviceId[deviceId] = analogs.leftY.direction; + } + SetAnalogFlipY(flipYByDeviceId); } static void SetDefaultKeyMap(int deviceId, const DefMappingStruct *array, size_t count, bool replace) { @@ -365,6 +374,7 @@ static void SetDefaultKeyMap(int deviceId, const DefMappingStruct *array, size_t else SetAxisMapping(array[i].pspKey, deviceId, array[i].key, array[i].direction, replace); } + g_seenDeviceIds.insert(deviceId); } void SetDefaultKeyMap(DefaultMaps dmap, bool replace) { @@ -853,6 +863,38 @@ bool AxisFromPspButton(int btn, int *deviceId, int *axisId, int *direction) { return false; } +MappedAnalogAxes MappedAxesForDevice(int deviceId) { + MappedAnalogAxes result{}; + + // Find the axisId mapped for a specific virtual button. + auto findAxisId = [&](int btn) -> MappedAnalogAxis { + MappedAnalogAxis info{ -1 }; + for (const auto &key : g_controllerMap[btn]) { + if (key.deviceId == deviceId) { + info.axisId = TranslateKeyCodeToAxis(key.keyCode, info.direction); + return info; + } + } + return info; + }; + + // Find the axisId of a pair of opposing buttons. + auto findAxisIdPair = [&](int minBtn, int maxBtn) -> MappedAnalogAxis { + MappedAnalogAxis foundMin = findAxisId(minBtn); + MappedAnalogAxis foundMax = findAxisId(maxBtn); + if (foundMin.axisId == foundMax.axisId) { + return foundMax; + } + return MappedAnalogAxis{ -1 }; + }; + + result.leftX = findAxisIdPair(VIRTKEY_AXIS_X_MIN, VIRTKEY_AXIS_X_MAX); + result.leftY = findAxisIdPair(VIRTKEY_AXIS_Y_MIN, VIRTKEY_AXIS_Y_MAX); + result.rightX = findAxisIdPair(VIRTKEY_AXIS_RIGHT_X_MIN, VIRTKEY_AXIS_RIGHT_X_MAX); + result.rightY = findAxisIdPair(VIRTKEY_AXIS_RIGHT_Y_MIN, VIRTKEY_AXIS_RIGHT_Y_MAX); + return result; +} + void RemoveButtonMapping(int btn) { for (auto iter = g_controllerMap.begin(); iter != g_controllerMap.end(); ++iter) { if (iter->first == btn) { @@ -878,6 +920,7 @@ void SetKeyMapping(int btn, KeyDef key, bool replace) { } g_controllerMapGeneration++; + g_seenDeviceIds.insert(key.deviceId); UpdateNativeMenuKeys(); } @@ -942,6 +985,7 @@ void LoadFromIni(IniFile &file) { int keyCode = atoi(parts[1].c_str()); SetKeyMapping(psp_button_names[i].key, KeyDef(deviceId, keyCode), false); + g_seenDeviceIds.insert(deviceId); } } diff --git a/Core/KeyMap.h b/Core/KeyMap.h index a94f603417..f54dda7855 100644 --- a/Core/KeyMap.h +++ b/Core/KeyMap.h @@ -79,6 +79,18 @@ const float AXIS_BIND_THRESHOLD = 0.75f; const float AXIS_BIND_THRESHOLD_MOUSE = 0.01f; typedef std::map> KeyMapping; +struct MappedAnalogAxis { + int axisId; + int direction; +}; + +struct MappedAnalogAxes { + MappedAnalogAxis leftX; + MappedAnalogAxis leftY; + MappedAnalogAxis rightX; + MappedAnalogAxis rightY; +}; + // KeyMap // A translation layer for key assignment. Provides // integration with Core's config state. @@ -128,7 +140,7 @@ namespace KeyMap { bool AxisToPspButton(int deviceId, int axisId, int direction, std::vector *pspKeys); bool AxisFromPspButton(int btn, int *deviceId, int *axisId, int *direction); - std::string NamePspButtonFromAxis(int deviceId, int axisId, int direction); + MappedAnalogAxes MappedAxesForDevice(int deviceId); void LoadFromIni(IniFile &iniFile); void SaveToIni(IniFile &iniFile); diff --git a/Core/Loaders.cpp b/Core/Loaders.cpp index f61b499cdd..4ad17643d4 100644 --- a/Core/Loaders.cpp +++ b/Core/Loaders.cpp @@ -19,6 +19,7 @@ #include #include "Common/File/FileUtil.h" +#include "Common/File/Path.h" #include "Common/StringUtils.h" #include "Core/FileLoaders/CachingFileLoader.h" #include "Core/FileLoaders/DiskCachingFileLoader.h" @@ -39,8 +40,8 @@ void RegisterFileLoaderFactory(std::string prefix, std::unique_ptrConstructFileLoader(filename); } } @@ -63,8 +64,8 @@ IdentifiedFileType Identify_File(FileLoader *fileLoader) { ERROR_LOG(LOADER, "Invalid fileLoader"); return IdentifiedFileType::ERROR_IDENTIFYING; } - if (fileLoader->Path().size() == 0) { - ERROR_LOG(LOADER, "Invalid filename %s", fileLoader->Path().c_str()); + if (fileLoader->GetPath().size() == 0) { + ERROR_LOG(LOADER, "Invalid filename %s", fileLoader->GetPath().c_str()); return IdentifiedFileType::ERROR_IDENTIFYING; } @@ -72,8 +73,8 @@ IdentifiedFileType Identify_File(FileLoader *fileLoader) { return IdentifiedFileType::ERROR_IDENTIFYING; } - std::string extension = fileLoader->Extension(); - if (!strcasecmp(extension.c_str(), ".iso")) { + std::string extension = fileLoader->GetPath().GetFileExtension(); + if (extension == ".iso") { // may be a psx iso, they have 2352 byte sectors. You never know what some people try to open if ((fileLoader->FileSize() % 2352) == 0) { unsigned char sync[12]; @@ -87,11 +88,11 @@ IdentifiedFileType Identify_File(FileLoader *fileLoader) { // maybe it also just happened to have that size, } return IdentifiedFileType::PSP_ISO; - } else if (!strcasecmp(extension.c_str(), ".cso")) { + } else if (extension == ".cso") { return IdentifiedFileType::PSP_ISO; - } else if (!strcasecmp(extension.c_str(), ".ppst")) { + } else if (extension == ".ppst") { return IdentifiedFileType::PPSSPP_SAVESTATE; - } else if (!strcasecmp(extension.c_str(), ".ppdmp")) { + } else if (extension == ".ppdmp") { char data[8]{}; fileLoader->ReadAt(0, 8, data); if (memcmp(data, "PPSSPPGE", 8) == 0) { @@ -101,20 +102,20 @@ IdentifiedFileType Identify_File(FileLoader *fileLoader) { // First, check if it's a directory with an EBOOT.PBP in it. if (fileLoader->IsDirectory()) { - std::string filename = fileLoader->Path(); + Path filename = fileLoader->GetPath(); if (filename.size() > 4) { // Check for existence of EBOOT.PBP, as required for "Directory games". - if (File::Exists((filename + "/EBOOT.PBP").c_str())) { + if (File::Exists(filename / "EBOOT.PBP")) { return IdentifiedFileType::PSP_PBP_DIRECTORY; } // check if it's a disc directory - if (File::Exists((filename + "/PSP_GAME").c_str())) { + if (File::Exists(filename / "PSP_GAME")) { return IdentifiedFileType::PSP_DISC_DIRECTORY; } // Not that, okay, let's guess it's a savedata directory if it has a param.sfo... - if (File::Exists((filename + "/PARAM.SFO").c_str())) { + if (File::Exists(filename / "PARAM.SFO")) { return IdentifiedFileType::PSP_SAVEDATA_DIRECTORY; } } @@ -142,16 +143,14 @@ IdentifiedFileType Identify_File(FileLoader *fileLoader) { } if (id == 'FLE\x7F') { - std::string filename = fileLoader->Path(); + Path filename = fileLoader->GetPath(); // There are a few elfs misnamed as pbp (like Trig Wars), accept that. - if (!strcasecmp(extension.c_str(), ".plf") || strstr(filename.c_str(),"BOOT.BIN") || - !strcasecmp(extension.c_str(), ".elf") || !strcasecmp(extension.c_str(), ".prx") || - !strcasecmp(extension.c_str(), ".pbp")) { + if (extension == ".plf" || strstr(filename.GetFilename().c_str(), "BOOT.BIN") || + extension == ".elf" || extension == ".prx" || extension == ".pbp") { return IdentifiedFileType::PSP_ELF; } return IdentifiedFileType::UNKNOWN_ELF; - } - else if (id == 'PBP\x00') { + } else if (id == 'PBP\x00') { // Do this PS1 eboot check FIRST before checking other eboot types. // It seems like some are malformed and slip through the PSAR check below. PBPReader pbp(fileLoader); @@ -177,28 +176,25 @@ IdentifiedFileType Identify_File(FileLoader *fileLoader) { // Let's check if we got pointed to a PBP within such a directory. // If so we just move up and return the directory itself as the game. - std::string path = File::GetDir(fileLoader->Path()); // If loading from memstick... - size_t pos = path.find("/PSP/GAME/"); - if (pos != std::string::npos) { + if (fileLoader->GetPath().FilePathContains("PSP/GAME/")) { return IdentifiedFileType::PSP_PBP_DIRECTORY; } return IdentifiedFileType::PSP_PBP; - } - else if (!strcasecmp(extension.c_str(),".pbp")) { + } else if (extension == ".pbp") { ERROR_LOG(LOADER, "A PBP with the wrong magic number?"); return IdentifiedFileType::PSP_PBP; - } else if (!strcasecmp(extension.c_str(),".bin")) { + } else if (extension == ".bin") { return IdentifiedFileType::UNKNOWN_BIN; - } else if (!strcasecmp(extension.c_str(),".zip")) { + } else if (extension == ".zip") { return IdentifiedFileType::ARCHIVE_ZIP; - } else if (!strcasecmp(extension.c_str(),".rar")) { + } else if (extension == ".rar") { return IdentifiedFileType::ARCHIVE_RAR; - } else if (!strcasecmp(extension.c_str(),".r00")) { + } else if (extension == ".r00") { return IdentifiedFileType::ARCHIVE_RAR; - } else if (!strcasecmp(extension.c_str(),".r01")) { + } else if (extension == ".r01") { return IdentifiedFileType::ARCHIVE_RAR; - } else if (!extension.empty() && !strcasecmp(extension.substr(1).c_str(), ".7z")) { + } else if (extension == ".7z") { return IdentifiedFileType::ARCHIVE_7Z; } return IdentifiedFileType::UNKNOWN; @@ -207,8 +203,8 @@ IdentifiedFileType Identify_File(FileLoader *fileLoader) { FileLoader *ResolveFileLoaderTarget(FileLoader *fileLoader) { IdentifiedFileType type = Identify_File(fileLoader); if (type == IdentifiedFileType::PSP_PBP_DIRECTORY) { - const std::string ebootFilename = ResolvePBPFile(fileLoader->Path()); - if (ebootFilename != fileLoader->Path()) { + const Path ebootFilename = ResolvePBPFile(fileLoader->GetPath()); + if (ebootFilename != fileLoader->GetPath()) { // Switch fileLoader to the actual EBOOT. delete fileLoader; fileLoader = ConstructFileLoader(ebootFilename); @@ -217,28 +213,20 @@ FileLoader *ResolveFileLoaderTarget(FileLoader *fileLoader) { return fileLoader; } -std::string ResolvePBPDirectory(const std::string &filename) { - bool hasPBP = endsWith(filename, "/EBOOT.PBP"); -#ifdef _WIN32 - hasPBP = hasPBP || endsWith(filename, "\\EBOOT.PBP"); -#endif - - if (hasPBP) { - return filename.substr(0, filename.length() - strlen("/EBOOT.PBP")); +Path ResolvePBPDirectory(const Path &filename) { + if (filename.GetFilename() == "EBOOT.PBP") { + return filename.NavigateUp(); + } else { + return filename; } - return filename; } -std::string ResolvePBPFile(const std::string &filename) { - bool hasPBP = endsWith(filename, "/EBOOT.PBP"); -#ifdef _WIN32 - hasPBP = hasPBP || endsWith(filename, "\\EBOOT.PBP"); -#endif - - if (!hasPBP) { - return filename + "/EBOOT.PBP"; +Path ResolvePBPFile(const Path &filename) { + if (filename.GetFilename() != "EBOOT.PBP") { + return filename / "EBOOT.PBP"; + } else { + return filename; } - return filename; } bool LoadFile(FileLoader **fileLoaderPtr, std::string *error_string) { @@ -262,11 +250,12 @@ bool LoadFile(FileLoader **fileLoaderPtr, std::string *error_string) { coreState = CORE_BOOT_ERROR; return false; } - std::string path = fileLoader->Path(); - size_t pos = path.find("/PSP/GAME/"); + + std::string dir = fileLoader->GetPath().GetDirectory(); + size_t pos = dir.find("PSP/GAME/"); if (pos != std::string::npos) { - path = ResolvePBPDirectory(path); - pspFileSystem.SetStartingDirectory("ms0:" + path.substr(pos)); + dir = ResolvePBPDirectory(Path(dir)).ToString(); + pspFileSystem.SetStartingDirectory("ms0:/" + dir.substr(pos)); } return Load_PSP_ELF_PBP(fileLoader, error_string); } else { @@ -357,7 +346,7 @@ bool LoadFile(FileLoader **fileLoaderPtr, std::string *error_string) { return false; } -bool UmdReplace(std::string filepath, std::string &error) { +bool UmdReplace(const Path &filepath, std::string &error) { IFileSystem* currentUMD = pspFileSystem.GetSystem("disc0:"); if (!currentUMD) { @@ -369,7 +358,7 @@ bool UmdReplace(std::string filepath, std::string &error) { if (!loadedFile->Exists()) { delete loadedFile; - error = loadedFile->Path() + " doesn't exist"; + error = loadedFile->GetPath().ToVisualString() + " doesn't exist"; return false; } UpdateLoadedFile(loadedFile); diff --git a/Core/Loaders.h b/Core/Loaders.h index fecb941a6d..cbf4329547 100644 --- a/Core/Loaders.h +++ b/Core/Loaders.h @@ -21,6 +21,7 @@ #include #include "Common/CommonTypes.h" +#include "Common/File/Path.h" enum class IdentifiedFileType { ERROR_IDENTIFYING, @@ -74,24 +75,15 @@ public: } virtual bool IsDirectory() = 0; virtual s64 FileSize() = 0; - virtual std::string Path() const = 0; - virtual std::string Extension() { - const std::string filename = Path(); - size_t pos = filename.find_last_of('.'); - if (pos == filename.npos) { - return ""; - } else { - return filename.substr(pos); - } - } + virtual Path GetPath() const = 0; + virtual size_t ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags = Flags::NONE) = 0; virtual size_t ReadAt(s64 absolutePos, size_t bytes, void *data, Flags flags = Flags::NONE) { return ReadAt(absolutePos, 1, bytes, data, flags); } // Cancel any operations that might block, if possible. - virtual void Cancel() { - } + virtual void Cancel() {} virtual std::string LatestError() const { return ""; @@ -121,8 +113,8 @@ public: s64 FileSize() override { return backend_->FileSize(); } - std::string Path() const override { - return backend_->Path(); + Path GetPath() const override { + return backend_->GetPath(); } void Cancel() override { backend_->Cancel(); @@ -145,23 +137,23 @@ inline u32 operator & (const FileLoader::Flags &a, const FileLoader::Flags &b) { return (u32)a & (u32)b; } -FileLoader *ConstructFileLoader(const std::string &filename); +FileLoader *ConstructFileLoader(const Path &filename); // Resolve to the target binary, ISO, or other file (e.g. from a directory.) FileLoader *ResolveFileLoaderTarget(FileLoader *fileLoader); -std::string ResolvePBPDirectory(const std::string &filename); -std::string ResolvePBPFile(const std::string &filename); +Path ResolvePBPDirectory(const Path &filename); +Path ResolvePBPFile(const Path &filename); IdentifiedFileType Identify_File(FileLoader *fileLoader); class FileLoaderFactory { public: virtual ~FileLoaderFactory() {} - virtual FileLoader *ConstructFileLoader(const std::string &filename) = 0; + virtual FileLoader *ConstructFileLoader(const Path &filename) = 0; }; -void RegisterFileLoaderFactory(std::string name, std::unique_ptr factory); +void RegisterFileLoaderFactory(std::string prefix, std::unique_ptr factory); // Can modify the string filename, as it calls IdentifyFile above. bool LoadFile(FileLoader **fileLoaderPtr, std::string *error_string); -bool UmdReplace(std::string filepath, std::string &error); +bool UmdReplace(const Path &filepath, std::string &error); diff --git a/Core/MIPS/ARM/ArmCompBranch.cpp b/Core/MIPS/ARM/ArmCompBranch.cpp index 05e0fa01f9..57bb9c4703 100644 --- a/Core/MIPS/ARM/ArmCompBranch.cpp +++ b/Core/MIPS/ARM/ArmCompBranch.cpp @@ -113,6 +113,7 @@ void ArmJit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely) } MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rt, rs); CONDITIONAL_NICE_DELAYSLOT; @@ -231,6 +232,7 @@ void ArmJit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool like } MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs); CONDITIONAL_NICE_DELAYSLOT; @@ -334,6 +336,7 @@ void ArmJit::BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely) u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceFPU(op, delaySlotOp); CONDITIONAL_NICE_DELAYSLOT; if (!likely && delaySlotIsNice) @@ -392,6 +395,7 @@ void ArmJit::BranchVFPUFlag(MIPSOpcode op, CCFlags cc, bool likely) u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); // Sometimes there's a VFPU branch in a delay slot (Disgaea 2: Dark Hero Days, Zettai Hero Project, La Pucelle) // The behavior is undefined - the CPU may take the second branch even if the first one passes. @@ -530,6 +534,7 @@ void ArmJit::Comp_JumpReg(MIPSOpcode op) bool andLink = (op & 0x3f) == 9 && rd != MIPS_REG_ZERO; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs); if (andLink && rs == rd) delaySlotIsNice = false; diff --git a/Core/MIPS/ARM/ArmCompVFPU.cpp b/Core/MIPS/ARM/ArmCompVFPU.cpp index d9056c8238..639230db84 100644 --- a/Core/MIPS/ARM/ArmCompVFPU.cpp +++ b/Core/MIPS/ARM/ArmCompVFPU.cpp @@ -2206,7 +2206,7 @@ namespace MIPSComp u8 dregs[4]; u8 dregs2[4]; - u32 nextOp = GetOffsetInstruction(1).encoding; + MIPSOpcode nextOp = GetOffsetInstruction(1); int vd2 = -1; int imm2 = -1; if ((nextOp >> 26) == 60 && ((nextOp >> 21) & 0x1F) == 29 && _VS == MIPS_GET_VS(nextOp)) { @@ -2246,7 +2246,7 @@ namespace MIPSComp // If the negsin setting differs between the two joint invocations, we need to flip the second one. bool negSin2 = (imm2 & 0x10) ? true : false; CompVrotShuffle(dregs2, imm2, sz, negSin1 != negSin2); - js.compilerPC += 4; + EatInstruction(nextOp); } fpr.ReleaseSpillLocksAndDiscardTemps(); diff --git a/Core/MIPS/ARM64/Arm64CompBranch.cpp b/Core/MIPS/ARM64/Arm64CompBranch.cpp index 73d7b85722..c1218607b8 100644 --- a/Core/MIPS/ARM64/Arm64CompBranch.cpp +++ b/Core/MIPS/ARM64/Arm64CompBranch.cpp @@ -113,6 +113,7 @@ void Arm64Jit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely) } MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rt, rs); CONDITIONAL_NICE_DELAYSLOT; @@ -249,6 +250,7 @@ void Arm64Jit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool li } MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs); CONDITIONAL_NICE_DELAYSLOT; @@ -351,6 +353,7 @@ void Arm64Jit::BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely) { u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceFPU(op, delaySlotOp); CONDITIONAL_NICE_DELAYSLOT; if (!likely && delaySlotIsNice) @@ -408,6 +411,7 @@ void Arm64Jit::BranchVFPUFlag(MIPSOpcode op, CCFlags cc, bool likely) { u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); // Sometimes there's a VFPU branch in a delay slot (Disgaea 2: Dark Hero Days, Zettai Hero Project, La Pucelle) // The behavior is undefined - the CPU may take the second branch even if the first one passes. @@ -545,6 +549,7 @@ void Arm64Jit::Comp_JumpReg(MIPSOpcode op) bool andLink = (op & 0x3f) == 9 && rd != MIPS_REG_ZERO; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs); if (andLink && rs == rd) delaySlotIsNice = false; diff --git a/Core/MIPS/ARM64/Arm64CompVFPU.cpp b/Core/MIPS/ARM64/Arm64CompVFPU.cpp index 1307baee29..b03d7ef3a9 100644 --- a/Core/MIPS/ARM64/Arm64CompVFPU.cpp +++ b/Core/MIPS/ARM64/Arm64CompVFPU.cpp @@ -1922,7 +1922,7 @@ namespace MIPSComp { u8 dregs[4]; u8 dregs2[4]; - u32 nextOp = GetOffsetInstruction(1).encoding; + MIPSOpcode nextOp = GetOffsetInstruction(1); int vd2 = -1; int imm2 = -1; if ((nextOp >> 26) == 60 && ((nextOp >> 21) & 0x1F) == 29 && _VS == MIPS_GET_VS(nextOp)) { @@ -1958,7 +1958,7 @@ namespace MIPSComp { // If the negsin setting differs between the two joint invocations, we need to flip the second one. bool negSin2 = (imm2 & 0x10) ? true : false; CompVrotShuffle(dregs2, imm2, sz, negSin1 != negSin2); - js.compilerPC += 4; + EatInstruction(nextOp); } fpr.ReleaseSpillLocksAndDiscardTemps(); diff --git a/Core/MIPS/IR/IRCompBranch.cpp b/Core/MIPS/IR/IRCompBranch.cpp index 0f8bbc4ff2..79d170031a 100644 --- a/Core/MIPS/IR/IRCompBranch.cpp +++ b/Core/MIPS/IR/IRCompBranch.cpp @@ -65,6 +65,7 @@ void IRFrontend::BranchRSRTComp(MIPSOpcode op, IRComparison cc, bool likely) { u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rt, rs); // Often, div/divu are followed by a likely "break" if the divisor was zero. @@ -121,6 +122,7 @@ void IRFrontend::BranchRSZeroComp(MIPSOpcode op, IRComparison cc, bool andLink, u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs); MIPSGPReg lhs = rs; @@ -241,6 +243,7 @@ void IRFrontend::BranchVFPUFlag(MIPSOpcode op, IRComparison cc, bool likely) { u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); ir.Write(IROp::VfpuCtrlToReg, IRTEMP_LHS, VFPU_CTRL_CC); // Sometimes there's a VFPU branch in a delay slot (Disgaea 2: Dark Hero Days, Zettai Hero Project, La Pucelle) @@ -343,6 +346,7 @@ void IRFrontend::Comp_JumpReg(MIPSOpcode op) { bool andLink = (op & 0x3f) == 9 && rd != MIPS_REG_ZERO; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs); if (andLink && rs == rd) delaySlotIsNice = false; diff --git a/Core/MIPS/MIPS.cpp b/Core/MIPS/MIPS.cpp index 37210c8c5d..cc9715a088 100644 --- a/Core/MIPS/MIPS.cpp +++ b/Core/MIPS/MIPS.cpp @@ -313,6 +313,10 @@ int MIPSState::RunLoopUntil(u64 globalTicks) { switch (PSP_CoreParameter().cpuCore) { case CPUCore::JIT: case CPUCore::IR_JIT: + while (inDelaySlot) { + // We must get out of the delay slot before going into jit. + SingleStep(); + } MIPSComp::jit->RunLoopUntil(globalTicks); break; diff --git a/Core/MIPS/MIPSAnalyst.cpp b/Core/MIPS/MIPSAnalyst.cpp index 6e88bbf63f..6a4f6d0353 100644 --- a/Core/MIPS/MIPSAnalyst.cpp +++ b/Core/MIPS/MIPSAnalyst.cpp @@ -77,7 +77,7 @@ namespace std { static std::unordered_set hashMap; -static std::string hashmapFileName; +static Path hashmapFileName; #define MIPSTABLE_IMM_MASK 0xFC000000 @@ -983,7 +983,8 @@ skip: // We assume the furthest jumpback is within the func. u32 furthestJumpbackAddr = INVALIDTARGET; - for (u32 ahead = fromAddr; ahead < fromAddr + MAX_AHEAD_SCAN; ahead += 4) { + const u32 scanEnd = fromAddr + Memory::ValidSize(fromAddr, MAX_AHEAD_SCAN); + for (u32 ahead = fromAddr; ahead < scanEnd; ahead += 4) { MIPSOpcode aheadOp = Memory::Read_Instruction(ahead, true); u32 target = GetBranchTargetNoRA(ahead, aheadOp); if (target == INVALIDTARGET && ((aheadOp & 0xFC000000) == 0x08000000)) { @@ -1184,7 +1185,7 @@ skip: void FinalizeScan(bool insertSymbols) { HashFunctions(); - std::string hashMapFilename = GetSysDirectory(DIRECTORY_SYSTEM) + "knownfuncs.ini"; + Path hashMapFilename = GetSysDirectory(DIRECTORY_SYSTEM) / "knownfuncs.ini"; if (g_Config.bFuncHashMap || g_Config.bFuncReplacements) { LoadBuiltinHashMap(); if (g_Config.bFuncHashMap) { @@ -1310,12 +1311,12 @@ skip: void SetHashMapFilename(const std::string& filename) { if (filename.empty()) - hashmapFileName = GetSysDirectory(DIRECTORY_SYSTEM) + "knownfuncs.ini"; + hashmapFileName = GetSysDirectory(DIRECTORY_SYSTEM) / "knownfuncs.ini"; else - hashmapFileName = filename; + hashmapFileName = Path(filename); } - void StoreHashMap(std::string filename) { + void StoreHashMap(Path filename) { if (filename.empty()) filename = hashmapFileName; @@ -1380,7 +1381,7 @@ skip: } } - void LoadHashMap(const std::string& filename) { + void LoadHashMap(const Path &filename) { FILE *file = File::OpenCFile(filename, "rt"); if (!file) { WARN_LOG(LOADER, "Could not load hash map: %s", filename.c_str()); diff --git a/Core/MIPS/MIPSAnalyst.h b/Core/MIPS/MIPSAnalyst.h index 746fa26f90..a00e2e89ad 100644 --- a/Core/MIPS/MIPSAnalyst.h +++ b/Core/MIPS/MIPSAnalyst.h @@ -21,6 +21,7 @@ #include #include "Common/CommonTypes.h" +#include "Common/File/Path.h" #include "Core/MIPS/MIPS.h" class DebugInterface; @@ -114,8 +115,8 @@ namespace MIPSAnalyst void SetHashMapFilename(const std::string& filename = ""); void LoadBuiltinHashMap(); - void LoadHashMap(const std::string& filename); - void StoreHashMap(std::string filename = ""); + void LoadHashMap(const Path &filename); + void StoreHashMap(Path filename = Path()); const char *LookupHash(u64 hash, u32 funcSize); void ReplaceFunctions(); diff --git a/Core/MIPS/MIPSAsm.cpp b/Core/MIPS/MIPSAsm.cpp index 0e59e62da1..618b0c35b2 100644 --- a/Core/MIPS/MIPSAsm.cpp +++ b/Core/MIPS/MIPSAsm.cpp @@ -35,7 +35,7 @@ public: if (!Memory::IsValidAddress((u32)(address+length-1))) return false; - Memory::Memcpy((u32)address,data,(u32)length); + Memory::Memcpy((u32)address, data, (u32)length, "Debugger"); // In case this is a delay slot or combined instruction, clear cache above it too. if (MIPSComp::jit) diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index ca8487e423..166f0cc969 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -452,7 +452,7 @@ const MIPSInstruction tableCop1S[64] = // 010001 10000 ..... ..... ..... xxxxxx INSTR("add.s", JITFUNC(Comp_FPU3op), Dis_FPU3op, Int_FPU3op, OUT_FD|IN_FS|IN_FT|IS_FPU), INSTR("sub.s", JITFUNC(Comp_FPU3op), Dis_FPU3op, Int_FPU3op, OUT_FD|IN_FS|IN_FT|IS_FPU), INSTR("mul.s", JITFUNC(Comp_FPU3op), Dis_FPU3op, Int_FPU3op, OUT_FD|IN_FS|IN_FT|IS_FPU), - INSTR("div.s", JITFUNC(Comp_FPU3op), Dis_FPU3op, Int_FPU3op, OUT_FD|IN_FS|IN_FT|IS_FPU), + INSTR("div.s", JITFUNC(Comp_FPU3op), Dis_FPU3op, Int_FPU3op, MIPSInfo(OUT_FD|IN_FS|IN_FT|IS_FPU, 29)), INSTR("sqrt.s", JITFUNC(Comp_FPU2op), Dis_FPU2op, Int_FPU2op, OUT_FD|IN_FS|IS_FPU), INSTR("abs.s", JITFUNC(Comp_FPU2op), Dis_FPU2op, Int_FPU2op, OUT_FD|IN_FS|IS_FPU), INSTR("mov.s", JITFUNC(Comp_FPU2op), Dis_FPU2op, Int_FPU2op, OUT_FD|IN_FS|IS_FPU), @@ -521,8 +521,8 @@ const MIPSInstruction tableCop1W[64] = // 010001 10100 ..... ..... ..... xxxxxx const MIPSInstruction tableVFPU0[8] = // 011000 xxx ....... . ....... . ....... { - INSTR("vadd", JITFUNC(Comp_VecDo3), Dis_VectorSet3, Int_VecDo3, IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX), - INSTR("vsub", JITFUNC(Comp_VecDo3), Dis_VectorSet3, Int_VecDo3, IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX), + INSTR("vadd", JITFUNC(Comp_VecDo3), Dis_VectorSet3, Int_VecDo3, MIPSInfo(IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX, 2)), + INSTR("vsub", JITFUNC(Comp_VecDo3), Dis_VectorSet3, Int_VecDo3, MIPSInfo(IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX, 2)), // TODO: Disasm is wrong. INSTR("vsbn", JITFUNC(Comp_Generic), Dis_VectorSet3, Int_Vsbn, IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX), INVALID, INVALID, INVALID, INVALID, @@ -991,10 +991,10 @@ int MIPSInterpret_RunUntil(u64 globalTicks) bool wasInDelaySlot = curMips->inDelaySlot; MIPSInterpret(op); + curMips->downcount -= MIPSGetInstructionCycleEstimate(op); if (curMips->inDelaySlot) { - curMips->downcount -= 1; // The reason we have to check this is the delay slot hack in Int_Syscall. if (wasInDelaySlot) { @@ -1006,7 +1006,6 @@ int MIPSInterpret_RunUntil(u64 globalTicks) } } - curMips->downcount -= 1; if (CoreTiming::GetTicks() > globalTicks) { // DEBUG_LOG(CPU, "Hit the max ticks, bailing 1 : %llu, %llu", globalTicks, CoreTiming::GetTicks()); @@ -1051,10 +1050,7 @@ MIPSInterpretFunc MIPSGetInterpretFunc(MIPSOpcode op) int MIPSGetInstructionCycleEstimate(MIPSOpcode op) { MIPSInfo info = MIPSGetInfo(op); - if (info & DELAYSLOT) - return 2; - else - return 1; + return info.cycles; } const char *MIPSDisasmAt(u32 compilerPC) { diff --git a/Core/MIPS/MIPSTables.h b/Core/MIPS/MIPSTables.h index 3987aa3e87..fd12c43a96 100644 --- a/Core/MIPS/MIPSTables.h +++ b/Core/MIPS/MIPSTables.h @@ -20,21 +20,6 @@ #include "Common/CommonTypes.h" #include "Core/MIPS/MIPS.h" -struct MIPSInfo { - MIPSInfo() { - value = 0; - } - - explicit MIPSInfo(u64 v) : value(v) { - } - - u64 operator & (const u64 &arg) const { - return value & arg; - } - - u64 value; -}; - #define CONDTYPE_MASK 0x00000007 #define CONDTYPE_EQ 0x00000001 #define CONDTYPE_NE 0x00000002 @@ -108,6 +93,28 @@ struct MIPSInfo { #define CDECL #endif +struct MIPSInfo { + MIPSInfo() { + value = 0; + cycles = 0; + } + + explicit MIPSInfo(u64 v, u16 c = 0) : value(v), cycles(c) { + if (c == 0) { + cycles = 1; + if (v & IS_VFPU) + cycles++; + } + } + + u64 operator & (const u64 &arg) const { + return value & arg; + } + + u64 value : 48; + u64 cycles : 16; +}; + typedef void (CDECL *MIPSDisFunc)(MIPSOpcode opcode, char *out); typedef void (CDECL *MIPSInterpretFunc)(MIPSOpcode opcode); @@ -125,6 +132,3 @@ MIPSInterpretFunc MIPSGetInterpretFunc(MIPSOpcode op); int MIPSGetInstructionCycleEstimate(MIPSOpcode op); const char *MIPSGetName(MIPSOpcode op); const char *MIPSDisasmAt(u32 compilerPC); - -void FillMIPSTables(); - diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index e3d0e5ce14..ab058bd935 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -29,6 +29,11 @@ #define V(i) (currentMIPS->v[voffset[i]]) #define VI(i) (currentMIPS->vi[voffset[i]]) +union float2int { + uint32_t i; + float f; +}; + void GetVectorRegs(u8 regs[4], VectorSize N, int vectorReg) { int mtx = (vectorReg >> 2) & 7; int col = vectorReg & 3; @@ -610,10 +615,7 @@ bool GetVFPUCtrlMask(int reg, u32 *mask) { float Float16ToFloat32(unsigned short l) { - union float2int { - unsigned int i; - float f; - } float2int; + float2int f2i; unsigned short float16 = l; unsigned int sign = (float16 >> VFPU_SH_FLOAT16_SIGN) & VFPU_MASK_FLOAT16_SIGN; @@ -623,10 +625,10 @@ float Float16ToFloat32(unsigned short l) float f; if (exponent == VFPU_FLOAT16_EXP_MAX) { - float2int.i = sign << 31; - float2int.i |= 255 << 23; - float2int.i |= fraction; - f = float2int.f; + f2i.i = sign << 31; + f2i.i |= 255 << 23; + f2i.i |= fraction; + f = f2i.f; } else if (exponent == 0 && fraction == 0) { @@ -647,10 +649,10 @@ float Float16ToFloat32(unsigned short l) } /* Convert to 32-bit single-precision IEEE754. */ - float2int.i = sign << 31; - float2int.i |= (exponent + 112) << 23; - float2int.i |= fraction << 13; - f=float2int.f; + f2i.i = sign << 31; + f2i.i |= (exponent + 112) << 23; + f2i.i |= fraction << 13; + f=f2i.f; } return f; } @@ -674,10 +676,6 @@ static int32_t get_sign(uint32_t x) { float vfpu_dot(float a[4], float b[4]) { static const int EXTRA_BITS = 2; - union float2int { - uint32_t i; - float f; - }; float2int result; float2int src[2]; @@ -791,31 +789,27 @@ float vfpu_dot(float a[4], float b[4]) { // TODO: This is still not completely accurate compared to the PSP's vsqrt. float vfpu_sqrt(float a) { - union float2int { - uint32_t u; - float f; - }; float2int val; val.f = a; - if ((val.u & 0xff800000) == 0x7f800000) { - if ((val.u & 0x007fffff) != 0) { - val.u = 0x7f800001; + if ((val.i & 0xff800000) == 0x7f800000) { + if ((val.i & 0x007fffff) != 0) { + val.i = 0x7f800001; } return val.f; } - if ((val.u & 0x7f800000) == 0) { + if ((val.i & 0x7f800000) == 0) { // Kill any sign. - val.u = 0; + val.i = 0; return val.f; } - if (val.u & 0x80000000) { - val.u = 0x7f800001; + if (val.i & 0x80000000) { + val.i = 0x7f800001; return val.f; } - int k = get_exp(val.u); - uint32_t sp = get_mant(val.u); + int k = get_exp(val.i); + uint32_t sp = get_mant(val.i); int less_bits = k & 1; k >>= 1; @@ -826,9 +820,9 @@ float vfpu_sqrt(float a) { z = (z >> 1) + (uint32_t)(halfsp / z); } - val.u = ((k + 127) << 23) | ((z << less_bits) & 0x007FFFFF); + val.i = ((k + 127) << 23) | ((z << less_bits) & 0x007FFFFF); // The lower two bits never end up set on the PSP, it seems like. - val.u &= 0xFFFFFFFC; + val.i &= 0xFFFFFFFC; return val.f; } @@ -842,31 +836,27 @@ static inline uint32_t mant_mul(uint32_t a, uint32_t b) { } float vfpu_rsqrt(float a) { - union float2int { - uint32_t u; - float f; - }; float2int val; val.f = a; - if (val.u == 0x7f800000) { + if (val.i == 0x7f800000) { return 0.0f; } - if ((val.u & 0x7fffffff) > 0x7f800000) { - val.u = (val.u & 0x80000000) | 0x7f800001; + if ((val.i & 0x7fffffff) > 0x7f800000) { + val.i = (val.i & 0x80000000) | 0x7f800001; return val.f; } - if ((val.u & 0x7f800000) == 0) { - val.u = (val.u & 0x80000000) | 0x7f800000; + if ((val.i & 0x7f800000) == 0) { + val.i = (val.i & 0x80000000) | 0x7f800000; return val.f; } - if (val.u & 0x80000000) { - val.u = 0xff800001; + if (val.i & 0x80000000) { + val.i = 0xff800001; return val.f; } - int k = get_exp(val.u); - uint32_t sp = get_mant(val.u); + int k = get_exp(val.i); + uint32_t sp = get_mant(val.i); int less_bits = k & 1; k = -(k >> 1); @@ -889,91 +879,182 @@ float vfpu_rsqrt(float a) { z >>= less_bits; - val.u = ((k + 127) << 23) | (z & 0x007FFFFF); - val.u &= 0xFFFFFFFC; + val.i = ((k + 127) << 23) | (z & 0x007FFFFF); + val.i &= 0xFFFFFFFC; return val.f; } -float vfpu_sin_single(float angle) { - angle -= floorf(angle * 0.25f) * 4.f; - if (angle == 0.0f || angle == 2.0f) { - return 0.0f; - } else if (angle == 1.0f) { - return 1.0f; - } else if (angle == 3.0f) { - return -1.0f; +float vfpu_sin(float a) { + float2int val; + val.f = a; + + int32_t k = get_uexp(val.i); + if (k == 255) { + val.i = (val.i & 0xFF800001) | 1; + return val.f; } - angle *= (float)M_PI_2; - return sinf(angle); + + if (k < 0x68) { + val.i &= 0x80000000; + return val.f; + } + + // Okay, now modulus by 4 to begin with (identical wave every 4.) + int32_t mantissa = get_mant(val.i); + if (k > 0x80) { + const uint8_t over = k & 0x1F; + mantissa = (mantissa << over) & 0x00FFFFFF; + k = 0x80; + } + // This subtracts off the 2. If we do, flip sign to inverse the wave. + if (k == 0x80 && mantissa >= (1 << 23)) { + val.i ^= 0x80000000; + mantissa -= 1 << 23; + } + + int8_t norm_shift = mantissa == 0 ? 32 : (int8_t)clz32_nonzero(mantissa) - 8; + mantissa <<= norm_shift; + k -= norm_shift; + + if (k <= 0 || mantissa == 0) { + val.i &= 0x80000000; + return val.f; + } + + // This is the value with modulus applied. + val.i = (val.i & 0x80000000) | (k << 23) | (mantissa & ~(1 << 23)); + val.f = (float)sin((double)val.f * M_PI_2); + val.i &= 0xFFFFFFFC; + return val.f; } -float vfpu_cos_single(float angle) { - angle -= floorf(angle * 0.25f) * 4.f; - if (angle == 1.0f || angle == 3.0f) { - return 0.0f; - } else if (angle == 0.0f) { - return 1.0f; - } else if (angle == 2.0f) { - return -1.0f; +float vfpu_cos(float a) { + float2int val; + val.f = a; + bool negate = false; + + int32_t k = get_uexp(val.i); + if (k == 255) { + // Note: unlike sin, cos always returns +NAN. + val.i = (val.i & 0x7F800001) | 1; + return val.f; } - angle *= (float)M_PI_2; - return cosf(angle); + + if (k < 0x68) + return 1.0f; + + // Okay, now modulus by 4 to begin with (identical wave every 4.) + int32_t mantissa = get_mant(val.i); + if (k > 0x80) { + const uint8_t over = k & 0x1F; + mantissa = (mantissa << over) & 0x00FFFFFF; + k = 0x80; + } + // This subtracts off the 2. If we do, negate the result value. + if (k == 0x80 && mantissa >= (1 << 23)) { + mantissa -= 1 << 23; + negate = true; + } + + int8_t norm_shift = mantissa == 0 ? 32 : (int8_t)clz32_nonzero(mantissa) - 8; + mantissa <<= norm_shift; + k -= norm_shift; + + if (k <= 0 || mantissa == 0) + return negate ? -1.0f : 1.0f; + + // This is the value with modulus applied. + val.i = (val.i & 0x80000000) | (k << 23) | (mantissa & ~(1 << 23)); + if (val.f == 1.0f || val.f == -1.0f) { + return negate ? 0.0f : -0.0f; + } + val.f = (float)cos((double)val.f * M_PI_2); + val.i &= 0xFFFFFFFC; + return negate ? -val.f : val.f; } -void vfpu_sincos_single(float angle, float &sine, float &cosine) { - angle -= floorf(angle * 0.25f) * 4.f; - if (angle == 0.0f) { - sine = 0.0f; - cosine = 1.0f; - } else if (angle == 1.0f) { - sine = 1.0f; - cosine = 0.0f; - } else if (angle == 2.0f) { - sine = 0.0f; - cosine = -1.0f; - } else if (angle == 3.0f) { - sine = -1.0f; - cosine = 0.0f; +void vfpu_sincos(float a, float &s, float &c) { + float2int val; + val.f = a; + // For sin, negate the input, for cos negate the output. + bool negate = false; + + int32_t k = get_uexp(val.i); + if (k == 255) { + val.i = (val.i & 0xFF800001) | 1; + s = val.f; + val.i &= 0x7F800001; + c = val.f; + return; + } + + if (k < 0x68) { + val.i &= 0x80000000; + s = val.f; + c = 1.0f; + return; + } + + // Okay, now modulus by 4 to begin with (identical wave every 4.) + int32_t mantissa = get_mant(val.i); + if (k > 0x80) { + const uint8_t over = k & 0x1F; + mantissa = (mantissa << over) & 0x00FFFFFF; + k = 0x80; + } + // This subtracts off the 2. If we do, flip signs. + if (k == 0x80 && mantissa >= (1 << 23)) { + mantissa -= 1 << 23; + negate = true; + } + + int8_t norm_shift = mantissa == 0 ? 32 : (int8_t)clz32_nonzero(mantissa) - 8; + mantissa <<= norm_shift; + k -= norm_shift; + + if (k <= 0 || mantissa == 0) { + val.i &= 0x80000000; + if (negate) + val.i ^= 0x80000000; + s = val.f; + c = negate ? -1.0f : 1.0f; + return; + } + + // This is the value with modulus applied. + val.i = (val.i & 0x80000000) | (k << 23) | (mantissa & ~(1 << 23)); + float2int i_sine, i_cosine; + if (val.f == 1.0f) { + i_sine.f = negate ? -1.0f : 1.0f; + i_cosine.f = negate ? 0.0f : -0.0f; + } else if (val.f == -1.0f) { + i_sine.f = negate ? 1.0f : -1.0f; + i_cosine.f = negate ? 0.0f : -0.0f; + } else if (negate) { + i_sine.f = (float)sin((double)-val.f * M_PI_2); + i_cosine.f = -(float)cos((double)val.f * M_PI_2); } else { - angle *= (float)M_PI_2; + double angle = (double)val.f * M_PI_2; #if defined(__linux__) - sincosf(angle, &sine, &cosine); + double d_sine; + double d_cosine; + sincos(angle, &d_sine, &d_cosine); + i_sine.f = (float)d_sine; + i_cosine.f = (float)d_cosine; #else - sine = sinf(angle); - cosine = cosf(angle); + i_sine.f = (float)sin(angle); + i_cosine.f = (float)cos(angle); #endif } + + i_sine.i &= 0xFFFFFFFC; + i_cosine.i &= 0xFFFFFFFC; + s = i_sine.f; + c = i_cosine.f; + return ; } -float vfpu_sin_double(float angle) { - return (float)sin((double)angle * M_PI_2); -} - -float vfpu_cos_double(float angle) { - return (float)cos((double)angle * M_PI_2); -} - -void vfpu_sincos_double(float angle_f, float &sine, float &cosine) { - double angle = (double)angle_f * M_PI_2; -#if defined(__linux__) - double d_sine; - double d_cosine; - sincos(angle, &d_sine, &d_cosine); - sine = (float)d_sine; - cosine = (float)d_cosine; -#else - sine = (float)sin(angle); - cosine = (float)cos(angle); -#endif -} - -float (*vfpu_sin)(float); -float (*vfpu_cos)(float); -void (*vfpu_sincos)(float, float&, float&); - -void InitVFPUSinCos(bool useDoublePrecision) { - vfpu_sin = useDoublePrecision ? vfpu_sin_double : vfpu_sin_single; - vfpu_cos = useDoublePrecision ? vfpu_cos_double : vfpu_cos_single; - vfpu_sincos = useDoublePrecision ? vfpu_sincos_double : vfpu_sincos_single; +void InitVFPUSinCos() { + // TODO: Could prepare a CORDIC table here. } diff --git a/Core/MIPS/MIPSVFPUUtils.h b/Core/MIPS/MIPSVFPUUtils.h index b89335a257..8200e8c87d 100644 --- a/Core/MIPS/MIPSVFPUUtils.h +++ b/Core/MIPS/MIPSVFPUUtils.h @@ -50,9 +50,9 @@ inline int Xpose(int v) { // // Messing around with the modulo functions? try https://www.desmos.com/calculator. -extern float (*vfpu_sin)(float); -extern float (*vfpu_cos)(float); -extern void (*vfpu_sincos)(float, float&, float&); +extern float vfpu_sin(float); +extern float vfpu_cos(float); +extern void vfpu_sincos(float, float&, float&); inline float vfpu_asin(float angle) { return asinf(angle) / M_PI_2; @@ -215,4 +215,4 @@ int GetVectorOverlap(int reg1, VectorSize size1, int reg2, VectorSize size2); bool GetVFPUCtrlMask(int reg, u32 *mask); float Float16ToFloat32(unsigned short l); -void InitVFPUSinCos(bool useDoublePrecision); +void InitVFPUSinCos(); diff --git a/Core/MIPS/x86/CompBranch.cpp b/Core/MIPS/x86/CompBranch.cpp index fef35e87d5..4941eddbab 100644 --- a/Core/MIPS/x86/CompBranch.cpp +++ b/Core/MIPS/x86/CompBranch.cpp @@ -365,6 +365,7 @@ void Jit::BranchRSRTComp(MIPSOpcode op, Gen::CCFlags cc, bool likely) } MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rt, rs); CONDITIONAL_NICE_DELAYSLOT; @@ -446,6 +447,7 @@ void Jit::BranchRSZeroComp(MIPSOpcode op, Gen::CCFlags cc, bool andLink, bool li } MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs); CONDITIONAL_NICE_DELAYSLOT; @@ -517,6 +519,7 @@ void Jit::BranchFPFlag(MIPSOpcode op, Gen::CCFlags cc, bool likely) u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceFPU(op, delaySlotOp); CONDITIONAL_NICE_DELAYSLOT; if (!likely && delaySlotIsNice) @@ -557,6 +560,7 @@ void Jit::BranchVFPUFlag(MIPSOpcode op, Gen::CCFlags cc, bool likely) u32 targetAddr = GetCompilerPC() + offset + 4; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); // Sometimes there's a VFPU branch in a delay slot (Disgaea 2: Dark Hero Days, Zettai Hero Project, La Pucelle) // The behavior is undefined - the CPU may take the second branch even if the first one passes. @@ -685,6 +689,7 @@ void Jit::Comp_JumpReg(MIPSOpcode op) bool andLink = (op & 0x3f) == 9 && rd != MIPS_REG_ZERO; MIPSOpcode delaySlotOp = GetOffsetInstruction(1); + js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp); bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs); if (andLink && rs == rd) delaySlotIsNice = false; diff --git a/Core/MIPS/x86/CompVFPU.cpp b/Core/MIPS/x86/CompVFPU.cpp index fd2f9ea3d4..eda1056563 100644 --- a/Core/MIPS/x86/CompVFPU.cpp +++ b/Core/MIPS/x86/CompVFPU.cpp @@ -3541,7 +3541,7 @@ void Jit::Comp_VRot(MIPSOpcode op) { u8 dregs[4]; u8 dregs2[4]; - u32 nextOp = GetOffsetInstruction(1).encoding; + MIPSOpcode nextOp = GetOffsetInstruction(1); int vd2 = -1; int imm2 = -1; if ((nextOp >> 26) == 60 && ((nextOp >> 21) & 0x1F) == 29 && _VS == MIPS_GET_VS(nextOp)) { @@ -3588,7 +3588,7 @@ void Jit::Comp_VRot(MIPSOpcode op) { // If the negsin setting differs between the two joint invocations, we need to flip the second one. bool negSin2 = (imm2 & 0x10) ? true : false; CompVrotShuffle(dregs2, imm2, n, negSin1 != negSin2); - js.compilerPC += 4; + EatInstruction(nextOp); } fpr.ReleaseSpillLocks(); } diff --git a/Core/MIPS/x86/Jit.cpp b/Core/MIPS/x86/Jit.cpp index 2409af9e8e..44e95ca2e5 100644 --- a/Core/MIPS/x86/Jit.cpp +++ b/Core/MIPS/x86/Jit.cpp @@ -458,7 +458,7 @@ bool Jit::DescribeCodePtr(const u8 *ptr, std::string &name) { name = "UnknownOrDeletedBlock"; } else if (jitAddr != (u32)-1) { char temp[1024]; - const std::string label = g_symbolMap->GetDescription(jitAddr); + const std::string label = g_symbolMap ? g_symbolMap->GetDescription(jitAddr) : ""; if (!label.empty()) snprintf(temp, sizeof(temp), "%08x_%s", jitAddr, label.c_str()); else diff --git a/Core/MemMap.cpp b/Core/MemMap.cpp index 521c241e3c..ab674e569c 100644 --- a/Core/MemMap.cpp +++ b/Core/MemMap.cpp @@ -30,20 +30,20 @@ #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" -#include "Core/MemMap.h" -#include "Core/MemFault.h" -#include "Core/HDRemaster.h" -#include "Core/MIPS/MIPS.h" -#include "Core/HLE/HLE.h" - #include "Core/Core.h" -#include "Core/Debugger/SymbolMap.h" -#include "Core/Debugger/MemBlockInfo.h" #include "Core/Config.h" #include "Core/ConfigValues.h" +#include "Core/Debugger/SymbolMap.h" +#include "Core/Debugger/MemBlockInfo.h" +#include "Core/HDRemaster.h" +#include "Core/HLE/HLE.h" #include "Core/HLE/ReplaceTables.h" +#include "Core/MemMap.h" +#include "Core/MemFault.h" +#include "Core/MIPS/MIPS.h" #include "Core/MIPS/JitCommon/JitBlockCache.h" #include "Core/MIPS/JitCommon/JitCommon.h" +#include "Core/ThreadPools.h" #include "UI/OnScreenDisplay.h" namespace Memory { @@ -59,26 +59,17 @@ u8 *m_pPhysicalScratchPad; u8 *m_pUncachedScratchPad; // 64-bit: Pointers to high-mem mirrors // 32-bit: Same as above -u8 *m_pPhysicalRAM; -u8 *m_pUncachedRAM; -u8 *m_pKernelRAM; // RAM mirrored up to "kernel space". Fully accessible at all times currently. -u8 *m_pPhysicalRAM2; -u8 *m_pUncachedRAM2; -u8 *m_pKernelRAM2; -u8 *m_pPhysicalRAM3; -u8 *m_pUncachedRAM3; -u8 *m_pKernelRAM3; +u8 *m_pPhysicalRAM[3]; +u8 *m_pUncachedRAM[3]; +u8 *m_pKernelRAM[3]; // RAM mirrored up to "kernel space". Fully accessible at all times currently. +// Technically starts at 0xA0000000, which we don't properly support (but we don't really support kernel code.) +// This matches how we handle 32-bit masking. +u8 *m_pUncachedKernelRAM[3]; // VRAM is mirrored 4 times. The second and fourth mirrors are swizzled. // In practice, a game accessing the mirrors most likely is deswizzling the depth buffer. -u8 *m_pPhysicalVRAM1; -u8 *m_pPhysicalVRAM2; -u8 *m_pPhysicalVRAM3; -u8 *m_pPhysicalVRAM4; -u8 *m_pUncachedVRAM1; -u8 *m_pUncachedVRAM2; -u8 *m_pUncachedVRAM3; -u8 *m_pUncachedVRAM4; +u8 *m_pPhysicalVRAM[4]; +u8 *m_pUncachedVRAM[4]; // Holds the ending address of the PSP's user space. // Required for HD Remasters to work properly. @@ -94,25 +85,28 @@ static MemoryView views[] = { {&m_pPhysicalScratchPad, 0x00010000, SCRATCHPAD_SIZE, 0}, {&m_pUncachedScratchPad, 0x40010000, SCRATCHPAD_SIZE, MV_MIRROR_PREVIOUS}, - {&m_pPhysicalVRAM1, 0x04000000, 0x00200000, 0}, - {&m_pPhysicalVRAM2, 0x04200000, 0x00200000, MV_MIRROR_PREVIOUS}, - {&m_pPhysicalVRAM3, 0x04400000, 0x00200000, MV_MIRROR_PREVIOUS}, - {&m_pPhysicalVRAM4, 0x04600000, 0x00200000, MV_MIRROR_PREVIOUS}, - {&m_pUncachedVRAM1, 0x44000000, 0x00200000, MV_MIRROR_PREVIOUS}, - {&m_pUncachedVRAM2, 0x44200000, 0x00200000, MV_MIRROR_PREVIOUS}, - {&m_pUncachedVRAM3, 0x44400000, 0x00200000, MV_MIRROR_PREVIOUS}, - {&m_pUncachedVRAM4, 0x44600000, 0x00200000, MV_MIRROR_PREVIOUS}, - {&m_pPhysicalRAM, 0x08000000, g_MemorySize, MV_IS_PRIMARY_RAM}, // only from 0x08800000 is it usable (last 24 megs) - {&m_pUncachedRAM, 0x48000000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_PRIMARY_RAM}, - {&m_pKernelRAM, 0x88000000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_PRIMARY_RAM | MV_KERNEL}, + {&m_pPhysicalVRAM[0], 0x04000000, 0x00200000, 0}, + {&m_pPhysicalVRAM[1], 0x04200000, 0x00200000, MV_MIRROR_PREVIOUS}, + {&m_pPhysicalVRAM[2], 0x04400000, 0x00200000, MV_MIRROR_PREVIOUS}, + {&m_pPhysicalVRAM[3], 0x04600000, 0x00200000, MV_MIRROR_PREVIOUS}, + {&m_pUncachedVRAM[0], 0x44000000, 0x00200000, MV_MIRROR_PREVIOUS}, + {&m_pUncachedVRAM[1], 0x44200000, 0x00200000, MV_MIRROR_PREVIOUS}, + {&m_pUncachedVRAM[2], 0x44400000, 0x00200000, MV_MIRROR_PREVIOUS}, + {&m_pUncachedVRAM[3], 0x44600000, 0x00200000, MV_MIRROR_PREVIOUS}, + {&m_pPhysicalRAM[0], 0x08000000, g_MemorySize, MV_IS_PRIMARY_RAM}, // only from 0x08800000 is it usable (last 24 megs) + {&m_pUncachedRAM[0], 0x48000000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_PRIMARY_RAM}, + {&m_pKernelRAM[0], 0x88000000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_PRIMARY_RAM | MV_KERNEL}, + {&m_pUncachedKernelRAM[0],0xC0000000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_PRIMARY_RAM | MV_KERNEL}, // Starts at memory + 31 MB. - {&m_pPhysicalRAM2, 0x09F00000, g_MemorySize, MV_IS_EXTRA1_RAM}, - {&m_pUncachedRAM2, 0x49F00000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_EXTRA1_RAM}, - {&m_pKernelRAM2, 0x89F00000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_EXTRA1_RAM | MV_KERNEL}, + {&m_pPhysicalRAM[1], 0x09F00000, g_MemorySize, MV_IS_EXTRA1_RAM}, + {&m_pUncachedRAM[1], 0x49F00000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_EXTRA1_RAM}, + {&m_pKernelRAM[1], 0x89F00000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_EXTRA1_RAM | MV_KERNEL}, + {&m_pUncachedKernelRAM[1],0xC9F00000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_EXTRA1_RAM | MV_KERNEL}, // Starts at memory + 31 * 2 MB. - {&m_pPhysicalRAM3, 0x0BE00000, g_MemorySize, MV_IS_EXTRA2_RAM}, - {&m_pUncachedRAM3, 0x4BE00000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_EXTRA2_RAM}, - {&m_pKernelRAM3, 0x8BE00000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_EXTRA2_RAM | MV_KERNEL}, + {&m_pPhysicalRAM[2], 0x0BE00000, g_MemorySize, MV_IS_EXTRA2_RAM}, + {&m_pUncachedRAM[2], 0x4BE00000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_EXTRA2_RAM}, + {&m_pKernelRAM[2], 0x8BE00000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_EXTRA2_RAM | MV_KERNEL}, + {&m_pUncachedKernelRAM[2],0xCBE00000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_EXTRA2_RAM | MV_KERNEL}, // TODO: There are a few swizzled mirrors of VRAM, not sure about the best way to // implement those. @@ -314,6 +308,34 @@ void Reinit() { Core_NotifyLifecycle(CoreLifecycle::MEMORY_REINITED); } +static void DoMemoryVoid(PointerWrap &p, uint32_t start, uint32_t size) { + uint8_t *d = GetPointer(start); + uint8_t *&storage = *p.ptr; + + // We only handle aligned data and sizes. + if ((size & 0x3F) != 0 || ((uintptr_t)d & 0x3F) != 0) + return p.DoVoid(d, size); + + switch (p.mode) { + case PointerWrap::MODE_READ: + GlobalThreadPool::Memcpy(d, storage, size); + break; + case PointerWrap::MODE_WRITE: + GlobalThreadPool::Memcpy(storage, d, size); + break; + case PointerWrap::MODE_MEASURE: + // Nothing to do here. + break; + case PointerWrap::MODE_VERIFY: + GlobalThreadPool::Loop([&](int l, int h) { + for (int i = l; i < h; i++) + _dbg_assert_msg_(d[i] == storage[i], "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", d[i], d[i], &d[i], storage[i], storage[i], &storage[i]); + }, 0, size); + break; + } + storage += size; +} + void DoState(PointerWrap &p) { auto s = p.Section("Memory", 1, 3); if (!s) @@ -346,10 +368,10 @@ void DoState(PointerWrap &p) { } } - DoArray(p, GetPointer(PSP_GetKernelMemoryBase()), g_MemorySize); + DoMemoryVoid(p, PSP_GetKernelMemoryBase(), g_MemorySize); p.DoMarker("RAM"); - DoArray(p, m_pPhysicalVRAM1, VRAM_SIZE); + DoMemoryVoid(p, PSP_GetVidMemBase(), VRAM_SIZE); p.DoMarker("VRAM"); DoArray(p, m_pPhysicalScratchPad, SCRATCHPAD_SIZE); p.DoMarker("ScratchPad"); @@ -363,15 +385,6 @@ void Shutdown() { DEBUG_LOG(MEMMAP, "Memory system shut down."); } -void Clear() { - if (m_pPhysicalRAM) - memset(GetPointerUnchecked(PSP_GetKernelMemoryBase()), 0, g_MemorySize); - if (m_pPhysicalScratchPad) - memset(m_pPhysicalScratchPad, 0, SCRATCHPAD_SIZE); - if (m_pPhysicalVRAM1) - memset(m_pPhysicalVRAM1, 0, VRAM_SIZE); -} - bool IsActive() { return base != nullptr; } diff --git a/Core/MemMap.h b/Core/MemMap.h index 465802fb32..49e67f3aa0 100644 --- a/Core/MemMap.h +++ b/Core/MemMap.h @@ -63,21 +63,6 @@ namespace Memory { // so be sure to load it into a 64-bit register. extern u8 *base; -// These are guaranteed to point to "low memory" addresses (sub-32-bit). -// 64-bit: Pointers to low-mem (sub-0x10000000) mirror -// 32-bit: Same as the corresponding physical/virtual pointers. -// Broken into three chunks to workaround 32-bit mmap() limits. -extern u8 *m_pRAM; -extern u8 *m_pRAM2; -extern u8 *m_pRAM3; -extern u8 *m_pScratchPad; -extern u8 *m_pVRAM; - -// 64-bit: Pointers to high-mem mirrors -// 32-bit: Same as above -extern u8 *m_pPhysicalRAM; -extern u8 *m_pUncachedRAM; - // This replaces RAM_NORMAL_SIZE at runtime. extern u32 g_MemorySize; extern u32 g_PSPModel; @@ -301,8 +286,8 @@ inline bool IsValidAddress(const u32 address) { return true; } else if ((address & 0x3F800000) == 0x04000000) { return true; - } else if ((address & 0xBFFF0000) == 0x00010000) { - return (address & 0x0000FFFF) < SCRATCHPAD_SIZE; + } else if ((address & 0xBFFFC000) == 0x00010000) { + return true; } else if ((address & 0x3F000000) >= 0x08000000 && (address & 0x3F000000) < 0x08000000 + g_MemorySize) { return true; } else { @@ -313,13 +298,13 @@ inline bool IsValidAddress(const u32 address) { inline u32 ValidSize(const u32 address, const u32 requested_size) { u32 max_size; if ((address & 0x3E000000) == 0x08000000) { - max_size = 0x08000000 + g_MemorySize - (address & 0x3E000000); + max_size = 0x08000000 + g_MemorySize - (address & 0x3FFFFFFF); } else if ((address & 0x3F800000) == 0x04000000) { - max_size = 0x04800000 - (address & 0x3F800000); - } else if ((address & 0xBFFF0000) == 0x00010000) { - max_size = 0x00014000 - (address & 0xBFFF0000); + max_size = 0x04800000 - (address & 0x3FFFFFFF); + } else if ((address & 0xBFFFC000) == 0x00010000) { + max_size = 0x00014000 - (address & 0x3FFFFFFF); } else if ((address & 0x3F000000) >= 0x08000000 && (address & 0x3F000000) < 0x08000000 + g_MemorySize) { - max_size = 0x08000000 + g_MemorySize - (address & 0x3F000000); + max_size = 0x08000000 + g_MemorySize - (address & 0x3FFFFFFF); } else { max_size = 0; } @@ -459,22 +444,22 @@ struct PSPPointer }; -inline u32 PSP_GetScratchpadMemoryBase() { return 0x00010000;} -inline u32 PSP_GetScratchpadMemoryEnd() { return 0x00014000;} +constexpr u32 PSP_GetScratchpadMemoryBase() { return 0x00010000;} +constexpr u32 PSP_GetScratchpadMemoryEnd() { return 0x00014000;} -inline u32 PSP_GetKernelMemoryBase() { return 0x08000000;} +constexpr u32 PSP_GetKernelMemoryBase() { return 0x08000000;} inline u32 PSP_GetUserMemoryEnd() { return PSP_GetKernelMemoryBase() + Memory::g_MemorySize;} -inline u32 PSP_GetKernelMemoryEnd() { return 0x08400000;} +constexpr u32 PSP_GetKernelMemoryEnd() { return 0x08400000;} // "Volatile" RAM is between 0x08400000 and 0x08800000, can be requested by the // game through sceKernelVolatileMemTryLock. -inline u32 PSP_GetVolatileMemoryStart() { return 0x08400000; } -inline u32 PSP_GetVolatileMemoryEnd() { return 0x08800000; } +constexpr u32 PSP_GetVolatileMemoryStart() { return 0x08400000; } +constexpr u32 PSP_GetVolatileMemoryEnd() { return 0x08800000; } -inline u32 PSP_GetUserMemoryBase() { return 0x08800000;} -inline u32 PSP_GetDefaultLoadAddress() { return 0;} -inline u32 PSP_GetVidMemBase() { return 0x04000000;} -inline u32 PSP_GetVidMemEnd() { return 0x04800000;} +constexpr u32 PSP_GetUserMemoryBase() { return 0x08800000; } +constexpr u32 PSP_GetDefaultLoadAddress() { return 0; } +constexpr u32 PSP_GetVidMemBase() { return 0x04000000; } +constexpr u32 PSP_GetVidMemEnd() { return 0x04800000; } template inline bool operator==(const PSPPointer &lhs, const PSPPointer &rhs) { diff --git a/Core/MemMapFunctions.cpp b/Core/MemMapFunctions.cpp index 36903cdabb..85ccb7e55c 100644 --- a/Core/MemMapFunctions.cpp +++ b/Core/MemMapFunctions.cpp @@ -35,7 +35,7 @@ u8 *GetPointer(const u32 address) { } else if ((address & 0x3F800000) == 0x04000000) { // VRAM return GetPointerUnchecked(address); - } else if ((address & 0xBFFF0000) == 0x00010000 && (address & 0x0000FFFF) < SCRATCHPAD_SIZE) { + } else if ((address & 0xBFFFC000) == 0x00010000) { // Scratchpad return GetPointerUnchecked(address); } else if ((address & 0x3F000000) >= 0x08000000 && (address & 0x3F000000) < 0x08000000 + g_MemorySize) { @@ -61,7 +61,7 @@ inline void ReadFromHardware(T &var, const u32 address) { } else if ((address & 0x3F800000) == 0x04000000) { // VRAM var = *((const T*)GetPointerUnchecked(address)); - } else if ((address & 0xBFFF0000) == 0x00010000 && (address & 0x0000FFFF) < SCRATCHPAD_SIZE) { + } else if ((address & 0xBFFFC000) == 0x00010000) { // Scratchpad var = *((const T*)GetPointerUnchecked(address)); } else if ((address & 0x3F000000) >= 0x08000000 && (address & 0x3F000000) < 0x08000000 + g_MemorySize) { @@ -86,7 +86,7 @@ inline void WriteToHardware(u32 address, const T data) { } else if ((address & 0x3F800000) == 0x04000000) { // VRAM *(T*)GetPointerUnchecked(address) = data; - } else if ((address & 0xBFFF0000) == 0x00010000 && (address & 0x0000FFFF) < SCRATCHPAD_SIZE) { + } else if ((address & 0xBFFFC000) == 0x00010000) { // Scratchpad *(T*)GetPointerUnchecked(address) = data; } else if ((address & 0x3F000000) >= 0x08000000 && (address & 0x3F000000) < 0x08000000 + g_MemorySize) { @@ -113,7 +113,7 @@ bool IsRAMAddress(const u32 address) { } bool IsScratchpadAddress(const u32 address) { - return (address & 0xBFFF0000) == 0x00010000 && (address & 0x0000FFFF) < SCRATCHPAD_SIZE; + return (address & 0xBFFFC000) == 0x00010000; } u8 Read_U8(const u32 address) { diff --git a/Core/MemMapHelpers.h b/Core/MemMapHelpers.h index f67ab9aa86..2b5bf1637d 100644 --- a/Core/MemMapHelpers.h +++ b/Core/MemMapHelpers.h @@ -30,31 +30,76 @@ extern MIPSState *currentMIPS; namespace Memory { -inline void Memcpy(const u32 to_address, const void *from_data, const u32 len, const char *tag = "Memcpy") { +inline void Memcpy(const u32 to_address, const void *from_data, const u32 len, const char *tag, size_t tagLen) { u8 *to = GetPointer(to_address); if (to) { memcpy(to, from_data, len); - size_t tagLen = strlen(tag); + if (!tag) { + tag = "Memcpy"; + tagLen = sizeof("Memcpy") - 1; + } NotifyMemInfo(MemBlockFlags::WRITE, to_address, len, tag, tagLen); } // if not, GetPointer will log. } -inline void Memcpy(void *to_data, const u32 from_address, const u32 len, const char *tag = "Memcpy") { +inline void Memcpy(void *to_data, const u32 from_address, const u32 len, const char *tag, size_t tagLen) { const u8 *from = GetPointer(from_address); if (from) { memcpy(to_data, from, len); - size_t tagLen = strlen(tag); + if (!tag) { + tag = "Memcpy"; + tagLen = sizeof("Memcpy") - 1; + } NotifyMemInfo(MemBlockFlags::READ, from_address, len, tag, tagLen); } // if not, GetPointer will log. } -inline void Memcpy(const u32 to_address, const u32 from_address, const u32 len, const char *tag = "Memcpy") { - Memcpy(GetPointer(to_address), from_address, len); - size_t tagLen = strlen(tag); - NotifyMemInfo(MemBlockFlags::READ, from_address, len, tag, tagLen); - NotifyMemInfo(MemBlockFlags::WRITE, to_address, len, tag, tagLen); +inline void Memcpy(const u32 to_address, const u32 from_address, const u32 len, const char *tag, size_t tagLen) { + u8 *to = GetPointer(to_address); + if (to) { + const u8 *from = GetPointer(from_address); + if (from) { + memcpy(to, from, len); + char tagData[128]; + if (!tag) { + const std::string srcTag = GetMemWriteTagAt(from_address, len); + tag = tagData; + tagLen = snprintf(tagData, sizeof(tagData), "Memcpy/%s", srcTag.c_str()); + } + NotifyMemInfo(MemBlockFlags::READ, from_address, len, tag, tagLen); + NotifyMemInfo(MemBlockFlags::WRITE, to_address, len, tag, tagLen); + } + } + // if not, GetPointer will log. +} + +template +inline void Memcpy(const u32 to_address, const void *from_data, const u32 len, const char(&tag)[tagLen]) { + Memcpy(to_address, from_data, len, tag, tagLen); +} + +template +inline void Memcpy(void *to_data, const u32 from_address, const u32 len, const char(&tag)[tagLen]) { + Memcpy(to_data, from_address, len, tag, tagLen); +} + +template +inline void Memcpy(const u32 to_address, const u32 from_address, const u32 len, const char(&tag)[tagLen]) { + Memcpy(to_address, from_address, len, tag, tagLen); +} + +inline void Memcpy(const u32 to_address, const void *from_data, const u32 len) { + Memcpy(to_address, from_data, len, nullptr, 0); +} + +inline void Memcpy(void *to_data, const u32 from_address, const u32 len) { + Memcpy(to_data, from_address, len, nullptr, 0); +} + +inline void Memcpy(const u32 to_address, const u32 from_address, const u32 len) { + Memcpy(to_address, from_address, len, nullptr, 0); } void Memset(const u32 _Address, const u8 _Data, const u32 _iLength, const char *tag = "Memset"); diff --git a/Core/PSPLoaders.cpp b/Core/PSPLoaders.cpp index ae718cddce..869c6f8492 100644 --- a/Core/PSPLoaders.cpp +++ b/Core/PSPLoaders.cpp @@ -84,7 +84,7 @@ void InitMemoryForGameISO(FileLoader *fileLoader) { IFileSystem *blockSystem = nullptr; if (fileLoader->IsDirectory()) { - fileSystem = new VirtualDiscFileSystem(&pspFileSystem, fileLoader->Path()); + fileSystem = new VirtualDiscFileSystem(&pspFileSystem, fileLoader->GetPath()); blockSystem = fileSystem; } else { auto bd = constructBlockDevice(fileLoader); @@ -152,7 +152,7 @@ bool ReInitMemoryForGameISO(FileLoader *fileLoader) { IFileSystem *blockSystem = nullptr; if (fileLoader->IsDirectory()) { - fileSystem = new VirtualDiscFileSystem(&pspFileSystem, fileLoader->Path()); + fileSystem = new VirtualDiscFileSystem(&pspFileSystem, fileLoader->GetPath()); blockSystem = fileSystem; } else { auto bd = constructBlockDevice(fileLoader); @@ -194,13 +194,15 @@ void InitMemoryForGamePBP(FileLoader *fileLoader) { } std::string discID = paramSFO.GetValueString("DISC_ID"); + std::string systemVer = paramSFO.GetValueString("PSP_SYSTEM_VER"); // Homebrew typically always leave this zero. bool discTotalCheck = paramSFO.GetValueInt("DISC_TOTAL") != 0; // A lot of homebrew reuse real game disc IDs - avoid. bool formatCheck = discID.substr(0, 2) != "NP" && discID.substr(0, 2) != "UL" && discID.substr(0, 2) != "UC"; char region = discID.size() > 3 ? discID[2] : '\0'; bool regionCheck = region != 'A' && region != 'E' && region != 'H' && region != 'I' && region != 'J' && region != 'K' && region != 'U' && region != 'X'; - if (formatCheck || regionCheck || discTotalCheck) { + bool systemVerCheck = !systemVer.empty() && systemVer[0] >= '5'; + if ((formatCheck || regionCheck || discTotalCheck || systemVerCheck) && !discID.empty()) { g_paramSFO.SetValue("DISC_ID", discID, (int)discID.size()); std::string ver = paramSFO.GetValueString("DISC_VERSION"); if (ver.empty()) @@ -313,7 +315,7 @@ bool Load_PSP_ISO(FileLoader *fileLoader, std::string *error_string) { // To do something deterministically when the game starts, disabling this thread won't be enough. // Instead: Use Core_ListenLifecycle() or watch coreState. loadingThread = std::thread([bootpath] { - setCurrentThreadName("ExecLoader"); + SetCurrentThreadName("ExecLoader"); PSP_LoadingLock guard; if (coreState != CORE_POWERUP) return; @@ -326,15 +328,20 @@ bool Load_PSP_ISO(FileLoader *fileLoader, std::string *error_string) { } else { coreState = CORE_BOOT_ERROR; // TODO: This is a crummy way to communicate the error... - PSP_CoreParameter().fileToStart = ""; + PSP_CoreParameter().fileToStart.clear(); } }); return true; } -static std::string NormalizePath(const std::string &path) { +static Path NormalizePath(const Path &path) { + if (path.Type() != PathType::NATIVE) { + // Nothing to do - these can't be non-normalized. + return path; + } + #ifdef _WIN32 - std::wstring wpath = ConvertUTF8ToWString(path); + std::wstring wpath = path.ToWString(); std::wstring buf; buf.resize(512); size_t sz = GetFullPathName(wpath.c_str(), (DWORD)buf.size(), &buf[0], nullptr); @@ -346,12 +353,12 @@ static std::string NormalizePath(const std::string &path) { // This should truncate off the null terminator. buf.resize(sz); } - return ConvertWStringToUTF8(buf); + return Path(buf); #else char buf[PATH_MAX + 1]; - if (realpath(path.c_str(), buf) == NULL) - return ""; - return buf; + if (!realpath(path.c_str(), buf)) + return Path(); + return Path(buf); #endif } @@ -368,52 +375,48 @@ bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string *error_string) { pspFileSystem.Mount("umd:", blockSystem); } } + Path full_path = fileLoader->GetPath(); + std::string path = full_path.GetDirectory(); + std::string extension = full_path.GetFileExtension(); + std::string file = full_path.GetFilename(); - std::string full_path = fileLoader->Path(); - std::string path, file, extension; - SplitPath(ReplaceAll(full_path, "\\", "/"), &path, &file, &extension); - - size_t pos = path.find("/PSP/GAME/"); + size_t pos = path.find("PSP/GAME/"); std::string ms_path; if (pos != std::string::npos) { - ms_path = "ms0:" + path.substr(pos); + ms_path = "ms0:/" + path.substr(pos) + "/"; } else { // This is wrong, but it's better than not having a working directory at all. // Note that umd0:/ is actually the writable containing directory, in this case. ms_path = "umd0:/"; } -#ifdef _WIN32 - // Turn the slashes back to the Windows way. - path = ReplaceAll(path, "/", "\\"); -#endif - if (!PSP_CoreParameter().mountRoot.empty()) { // We don't want to worry about .. and cwd and such. - const std::string rootNorm = NormalizePath(PSP_CoreParameter().mountRoot + "/"); - const std::string pathNorm = NormalizePath(path + "/"); + const Path rootNorm = NormalizePath(PSP_CoreParameter().mountRoot); + const Path pathNorm = NormalizePath(Path(path)); // If root is not a subpath of path, we can't boot the game. - if (!startsWith(pathNorm, rootNorm)) { + if (!pathNorm.StartsWith(rootNorm)) { *error_string = "Cannot boot ELF located outside mountRoot."; coreState = CORE_BOOT_ERROR; return false; } - const std::string filepath = ReplaceAll(pathNorm.substr(rootNorm.size()), "\\", "/"); + // TODO(scoped): This won't work! + const std::string filepath = ReplaceAll(pathNorm.ToString().substr(rootNorm.ToString().size()), "\\", "/"); file = filepath + "/" + file; - path = rootNorm + "/"; + path = rootNorm.ToString() + "/"; pspFileSystem.SetStartingDirectory(filepath); } else { pspFileSystem.SetStartingDirectory(ms_path); } - DirectoryFileSystem *fs = new DirectoryFileSystem(&pspFileSystem, path, FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD); + DirectoryFileSystem *fs = new DirectoryFileSystem(&pspFileSystem, Path(path), FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD); pspFileSystem.Mount("umd0:", fs); - std::string finalName = ms_path + file + extension; + std::string finalName = ms_path + file; - std::string homebrewName = PSP_CoreParameter().fileToStart; + std::string homebrewName = PSP_CoreParameter().fileToStart.ToVisualString(); std::size_t lslash = homebrewName.find_last_of("/"); if (lslash != homebrewName.npos) homebrewName = homebrewName.substr(lslash + 1); @@ -429,26 +432,26 @@ bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string *error_string) { host->SetWindowTitle(title.c_str()); // Migrate old save states from old versions of fake game IDs. - const std::string savestateDir = GetSysDirectory(DIRECTORY_SAVESTATE); + const Path savestateDir = GetSysDirectory(DIRECTORY_SAVESTATE); for (int i = 0; i < 5; ++i) { - std::string newPrefix = StringFromFormat("%s%s_%s_%d", savestateDir.c_str(), discID.c_str(), discVersion.c_str(), i); - std::string oldNamePrefix = StringFromFormat("%s%s_%d", savestateDir.c_str(), homebrewName.c_str(), i); - std::string oldIDPrefix = StringFromFormat("%s%s_1.00_%d", savestateDir.c_str(), madeUpID.c_str(), i); + Path newPrefix = savestateDir / StringFromFormat("%s_%s_%d", discID.c_str(), discVersion.c_str(), i); + Path oldNamePrefix = savestateDir / StringFromFormat("%s_%d", homebrewName.c_str(), i); + Path oldIDPrefix = savestateDir / StringFromFormat("%s_1.00_%d", madeUpID.c_str(), i); - if (oldIDPrefix != newPrefix && File::Exists(oldIDPrefix + ".ppst")) - File::Rename(oldIDPrefix + ".ppst", newPrefix + ".ppst"); - else if (File::Exists(oldNamePrefix + ".ppst")) - File::Rename(oldNamePrefix + ".ppst", newPrefix + ".ppst"); - if (oldIDPrefix != newPrefix && File::Exists(oldIDPrefix + ".jpg")) - File::Rename(oldIDPrefix + ".jpg", newPrefix + ".jpg"); - else if (File::Exists(oldNamePrefix + ".jpg")) - File::Rename(oldNamePrefix + ".jpg", newPrefix + ".jpg"); + if (oldIDPrefix != newPrefix && File::Exists(oldIDPrefix.WithExtraExtension(".ppst"))) + File::Rename(oldIDPrefix.WithExtraExtension(".ppst"), newPrefix.WithExtraExtension(".ppst")); + else if (File::Exists(oldNamePrefix.WithExtraExtension(".ppst"))) + File::Rename(oldNamePrefix.WithExtraExtension(".ppst"), newPrefix.WithExtraExtension(".ppst")); + if (oldIDPrefix != newPrefix && File::Exists(oldIDPrefix.WithExtraExtension(".jpg"))) + File::Rename(oldIDPrefix.WithExtraExtension(".jpg"), newPrefix.WithExtraExtension(".jpg")); + else if (File::Exists(oldNamePrefix.WithExtraExtension(".jpg"))) + File::Rename(oldNamePrefix.WithExtraExtension(".jpg"), newPrefix.WithExtraExtension(".jpg")); } PSPLoaders_Shutdown(); // Note: See Load_PSP_ISO for notes about this thread. loadingThread = std::thread([finalName] { - setCurrentThreadName("ExecLoader"); + SetCurrentThreadName("ExecLoader"); PSP_LoadingLock guard; if (coreState != CORE_POWERUP) return; @@ -459,7 +462,7 @@ bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string *error_string) { } else { coreState = CORE_BOOT_ERROR; // TODO: This is a crummy way to communicate the error... - PSP_CoreParameter().fileToStart = ""; + PSP_CoreParameter().fileToStart.clear(); } }); return true; @@ -472,7 +475,7 @@ bool Load_PSP_GE_Dump(FileLoader *fileLoader, std::string *error_string) { PSPLoaders_Shutdown(); // Note: See Load_PSP_ISO for notes about this thread. loadingThread = std::thread([] { - setCurrentThreadName("ExecLoader"); + SetCurrentThreadName("ExecLoader"); PSP_LoadingLock guard; if (coreState != CORE_POWERUP) return; @@ -483,7 +486,7 @@ bool Load_PSP_GE_Dump(FileLoader *fileLoader, std::string *error_string) { } else { coreState = CORE_BOOT_ERROR; // TODO: This is a crummy way to communicate the error... - PSP_CoreParameter().fileToStart = ""; + PSP_CoreParameter().fileToStart.clear(); } }); return true; diff --git a/Core/Replay.cpp b/Core/Replay.cpp index eaf1d5a959..93b0968c25 100644 --- a/Core/Replay.cpp +++ b/Core/Replay.cpp @@ -169,7 +169,7 @@ void ReplayExecuteBlob(const std::vector &data) { INFO_LOG(SYSTEM, "Executing replay with %lld items", (long long)replayItems.size()); } -bool ReplayExecuteFile(const std::string &filename) { +bool ReplayExecuteFile(const Path &filename) { ReplayAbort(); FILE *fp = File::OpenCFile(filename, "rb"); @@ -269,7 +269,7 @@ void ReplayFlushBlob(std::vector *data) { replayItems.clear(); } -bool ReplayFlushFile(const std::string &filename) { +bool ReplayFlushFile(const Path &filename) { FILE *fp = File::OpenCFile(filename, replaySaveWroteHeader ? "ab" : "wb"); if (!fp) { ERROR_LOG(SYSTEM, "Failed to open replay file: %s", filename.c_str()); diff --git a/Core/Replay.h b/Core/Replay.h index bcbf7dc6e0..2229a6dee8 100644 --- a/Core/Replay.h +++ b/Core/Replay.h @@ -21,6 +21,8 @@ #include #include +#include "Common/File/Path.h" + // Be careful about changing these values (used in file data.) enum class ReplayAction : uint8_t { BUTTONS = 0x00, @@ -49,7 +51,7 @@ struct PSPFileInfo; // Replay from data in memory. Does not manipulate base time / RNG state. void ReplayExecuteBlob(const std::vector &data); // Replay from data in a file. Returns false if invalid. -bool ReplayExecuteFile(const std::string &filename); +bool ReplayExecuteFile(const Path &filename); // Returns whether there are unexected events to replay. bool ReplayHasMoreEvents(); @@ -60,7 +62,7 @@ void ReplayBeginSave(); void ReplayFlushBlob(std::vector *data); // Flush buffered events to file. Continues recording (next call will receive new events only.) // Do not call with a different filename before ReplayAbort(). -bool ReplayFlushFile(const std::string &filename); +bool ReplayFlushFile(const Path &filename); // Abort any execute or record operation in progress. void ReplayAbort(); diff --git a/Core/Reporting.cpp b/Core/Reporting.cpp index 350713dffd..72edac0ed6 100644 --- a/Core/Reporting.cpp +++ b/Core/Reporting.cpp @@ -101,14 +101,14 @@ namespace Reporting static std::mutex crcLock; static std::condition_variable crcCond; - static std::string crcFilename; - static std::map crcResults; + static Path crcFilename; + static std::map crcResults; static volatile bool crcPending = false; static volatile bool crcCancel = false; static std::thread crcThread; static int CalculateCRCThread() { - setCurrentThreadName("ReportCRC"); + SetCurrentThreadName("ReportCRC"); // TODO: Use the blockDevice from pspFileSystem? FileLoader *fileLoader = ConstructFileLoader(crcFilename); @@ -126,11 +126,10 @@ namespace Reporting crcResults[crcFilename] = crc; crcPending = false; crcCond.notify_one(); - return 0; } - void QueueCRC(const std::string &gamePath) { + void QueueCRC(const Path &gamePath) { std::lock_guard guard(crcLock); auto it = crcResults.find(gamePath); @@ -151,12 +150,12 @@ namespace Reporting crcThread = std::thread(CalculateCRCThread); } - bool HasCRC(const std::string &gamePath) { + bool HasCRC(const Path &gamePath) { std::lock_guard guard(crcLock); return crcResults.find(gamePath) != crcResults.end(); } - uint32_t RetrieveCRC(const std::string &gamePath) { + uint32_t RetrieveCRC(const Path &gamePath) { QueueCRC(gamePath); std::unique_lock guard(crcLock); @@ -171,7 +170,7 @@ namespace Reporting return it->second; } - static uint32_t RetrieveCRCUnlessPowerSaving(const std::string &gamePath) { + static uint32_t RetrieveCRCUnlessPowerSaving(const Path &gamePath) { // It's okay to use it if we have it already. if (Core_GetPowerSaving() && !HasCRC(gamePath)) { return 0; @@ -261,9 +260,12 @@ namespace Reporting bool SendReportRequest(const char *uri, const std::string &data, const std::string &mimeType, Buffer *output = NULL) { http::Client http; - Buffer theVoid; + http::RequestProgress progress(&pendingMessagesDone); + Buffer theVoid = Buffer::Void(); - if (output == NULL) + http.SetUserAgent(StringFromFormat("PPSSPP/%s", PPSSPP_GIT_VERSION)); + + if (output == nullptr) output = &theVoid; const char *serverHost = ServerHostname(); @@ -272,7 +274,7 @@ namespace Reporting if (http.Resolve(serverHost, ServerPort())) { http.Connect(); - int result = http.POST(uri, data, mimeType, output); + int result = http.POST(uri, data, mimeType, output, &progress); http.Disconnect(); return result >= 200 && result < 300; @@ -447,10 +449,10 @@ namespace Reporting postdata.Add("savestate_used", SaveState::HasLoadedState()); } - void AddScreenshotData(MultipartFormDataEncoder &postdata, std::string filename) + void AddScreenshotData(MultipartFormDataEncoder &postdata, const Path &filename) { std::string data; - if (!filename.empty() && readFileToString(false, filename.c_str(), data)) + if (!filename.empty() && File::ReadFileToString(false, filename, data)) postdata.Add("screenshot", data, "screenshot.jpg", "image/jpeg"); const std::string iconFilename = "disc0:/PSP_GAME/ICON0.PNG"; @@ -462,7 +464,7 @@ namespace Reporting int Process(int pos) { - setCurrentThreadName("Report"); + SetCurrentThreadName("Report"); Payload &payload = payloadBuffer[pos]; Buffer output; @@ -499,7 +501,7 @@ namespace Reporting postdata.Add("gameplay", StringFromFormat("%d", payload.int3)); postdata.Add("crc", StringFromFormat("%08x", RetrieveCRCUnlessPowerSaving(PSP_CoreParameter().fileToStart))); postdata.Add("suggestions", payload.string1 != "perfect" && payload.string1 != "playable" ? "1" : "0"); - AddScreenshotData(postdata, payload.string2); + AddScreenshotData(postdata, Path(payload.string2)); payload.string1.clear(); payload.string2.clear(); @@ -540,14 +542,19 @@ namespace Reporting // Don't allow builds without version info from git. They're useless for reporting. if (strcmp(PPSSPP_GIT_VERSION, "unknown") == 0) return false; + // Don't report from games without a version ID (i.e. random hashed homebrew IDs.) + // The problem is, these aren't useful because the hashes end up different for different people. + // TODO: Should really hash the ELF instead of the path, but then that affects savestates/cheats. + if (g_paramSFO.GetValueString("DISC_VERSION").empty()) + return false; // Some users run the exe from a zip or something, and don't have fonts. // This breaks things, but let's not report it since it's confusing. #if defined(USING_WIN_UI) || defined(APPLE) - if (!File::Exists(g_Config.flash0Directory + "/font/jpn0.pgf")) + if (!File::Exists(g_Config.flash0Directory / "font/jpn0.pgf")) return false; #else - FileInfo fo; + File::FileInfo fo; if (!VFSGetFileInfo("flash0/font/jpn0.pgf", &fo)) return false; #endif @@ -611,7 +618,7 @@ namespace Reporting } int ProcessPending() { - setCurrentThreadName("Report"); + SetCurrentThreadName("Report"); std::unique_lock guard(pendingMessageLock); while (!pendingMessagesDone) { diff --git a/Core/Reporting.h b/Core/Reporting.h index 3aa20e4eb9..a1b04ab86f 100644 --- a/Core/Reporting.h +++ b/Core/Reporting.h @@ -21,6 +21,7 @@ #include #include "Common/CommonTypes.h" +#include "Common/File/Path.h" #include "Common/Log.h" #define DEBUG_LOG_REPORT(t,...) do { DEBUG_LOG(t, __VA_ARGS__); Reporting::ReportMessage(__VA_ARGS__); } while (false) @@ -88,14 +89,14 @@ namespace Reporting std::vector CompatibilitySuggestions(); // Queues game for CRC hash if needed. - void QueueCRC(const std::string &gamePath); + void QueueCRC(const Path &gamePath); // Returns true if the hash is available, does not queue if not. - bool HasCRC(const std::string &gamePath); + bool HasCRC(const Path &gamePath); // Blocks until the CRC hash is available for game, and returns it. // To avoid stalling, call HasCRC() in update() or similar and call this if it returns true. - uint32_t RetrieveCRC(const std::string &gamePath); + uint32_t RetrieveCRC(const Path &gamePath); // Returns true if that identifier has not been logged yet. bool ShouldLogNTimes(const char *identifier, int n); diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index d7381f53f9..6152af3293 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -75,13 +75,13 @@ namespace SaveState struct Operation { // The slot number is for visual purposes only. Set to -1 for operations where we don't display a message for example. - Operation(OperationType t, const std::string &f, int slot_, Callback cb, void *cbUserData_) + Operation(OperationType t, const Path &f, int slot_, Callback cb, void *cbUserData_) : type(t), filename(f), callback(cb), slot(slot_), cbUserData(cbUserData_) { } OperationType type; - std::string filename; + Path filename; Callback callback; int slot; void *cbUserData; @@ -161,7 +161,7 @@ namespace SaveState if (compressThread_.joinable()) compressThread_.join(); compressThread_ = std::thread([=]{ - setCurrentThreadName("SaveStateCompress"); + SetCurrentThreadName("SaveStateCompress"); Compress(*result, *state, *base); }); } @@ -323,27 +323,27 @@ namespace SaveState Core_UpdateSingleStep(); } - void Load(const std::string &filename, int slot, Callback callback, void *cbUserData) + void Load(const Path &filename, int slot, Callback callback, void *cbUserData) { Enqueue(Operation(SAVESTATE_LOAD, filename, slot, callback, cbUserData)); } - void Save(const std::string &filename, int slot, Callback callback, void *cbUserData) + void Save(const Path &filename, int slot, Callback callback, void *cbUserData) { Enqueue(Operation(SAVESTATE_SAVE, filename, slot, callback, cbUserData)); } void Verify(Callback callback, void *cbUserData) { - Enqueue(Operation(SAVESTATE_VERIFY, std::string(""), -1, callback, cbUserData)); + Enqueue(Operation(SAVESTATE_VERIFY, Path(), -1, callback, cbUserData)); } void Rewind(Callback callback, void *cbUserData) { - Enqueue(Operation(SAVESTATE_REWIND, std::string(""), -1, callback, cbUserData)); + Enqueue(Operation(SAVESTATE_REWIND, Path(), -1, callback, cbUserData)); } - void SaveScreenshot(const std::string &filename, Callback callback, void *cbUserData) + void SaveScreenshot(const Path &filename, Callback callback, void *cbUserData) { Enqueue(Operation(SAVESTATE_SAVE_SCREENSHOT, filename, -1, callback, cbUserData)); } @@ -398,22 +398,22 @@ namespace SaveState return title + " (" + filename + ")"; } - std::string GetTitle(const std::string &filename) { + std::string GetTitle(const Path &filename) { std::string title; if (CChunkFileReader::GetFileTitle(filename, &title) == CChunkFileReader::ERROR_NONE) { if (title.empty()) { - return File::GetFilename(filename); + return filename.GetFilename(); } - return AppendSlotTitle(filename, title); + return AppendSlotTitle(filename.GetFilename(), title); } // The file can't be loaded - let's note that. auto sy = GetI18NCategory("System"); - return File::GetFilename(filename) + " " + sy->T("(broken)"); + return filename.GetFilename() + " " + sy->T("(broken)"); } - std::string GenerateSaveSlotFilename(const std::string &gameFilename, int slot, const char *extension) + Path GenerateSaveSlotFilename(const Path &gameFilename, int slot, const char *extension) { std::string discId = g_paramSFO.GetValueString("DISC_ID"); std::string discVer = g_paramSFO.GetValueString("DISC_VERSION"); @@ -425,7 +425,7 @@ namespace SaveState fullDiscId = StringFromFormat("%s_%s", discId.c_str(), discVer.c_str()); std::string filename = StringFromFormat("%s_%d.%s", fullDiscId.c_str(), slot, extension); - return GetSysDirectory(DIRECTORY_SAVESTATE) + filename; + return GetSysDirectory(DIRECTORY_SAVESTATE) / filename; } int GetCurrentSlot() @@ -438,9 +438,9 @@ namespace SaveState g_Config.iCurrentStateSlot = (g_Config.iCurrentStateSlot + 1) % NUM_SLOTS; } - void LoadSlot(const std::string &gameFilename, int slot, Callback callback, void *cbUserData) + void LoadSlot(const Path &gameFilename, int slot, Callback callback, void *cbUserData) { - std::string fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION); + Path fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION); if (!fn.empty()) { Load(fn, slot, callback, cbUserData); } else { @@ -450,21 +450,21 @@ namespace SaveState } } - static void DeleteIfExists(const std::string &fn) { + static void DeleteIfExists(const Path &fn) { // Just avoiding error messages. if (File::Exists(fn)) { File::Delete(fn); } } - static void RenameIfExists(const std::string &from, const std::string &to) { + static void RenameIfExists(const Path &from, const Path &to) { if (File::Exists(from)) { File::Rename(from, to); } } - static void SwapIfExists(const std::string &from, const std::string &to) { - std::string temp = from + ".tmp"; + static void SwapIfExists(const Path &from, const Path &to) { + Path temp = from.WithExtraExtension(".tmp"); if (File::Exists(from)) { File::Rename(from, temp); File::Rename(to, from); @@ -472,12 +472,12 @@ namespace SaveState } } - void SaveSlot(const std::string &gameFilename, int slot, Callback callback, void *cbUserData) + void SaveSlot(const Path &gameFilename, int slot, Callback callback, void *cbUserData) { - std::string fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION); - std::string shot = GenerateSaveSlotFilename(gameFilename, slot, SCREENSHOT_EXTENSION); - std::string fnUndo = GenerateSaveSlotFilename(gameFilename, slot, UNDO_STATE_EXTENSION); - std::string shotUndo = GenerateSaveSlotFilename(gameFilename, slot, UNDO_SCREENSHOT_EXTENSION); + Path fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION); + Path shot = GenerateSaveSlotFilename(gameFilename, slot, SCREENSHOT_EXTENSION); + Path fnUndo = GenerateSaveSlotFilename(gameFilename, slot, UNDO_STATE_EXTENSION); + Path shotUndo = GenerateSaveSlotFilename(gameFilename, slot, UNDO_SCREENSHOT_EXTENSION); if (!fn.empty()) { auto renameCallback = [=](Status status, const std::string &message, void *data) { if (status != Status::FAILURE) { @@ -487,7 +487,7 @@ namespace SaveState } else { DeleteIfExists(fn); } - File::Rename(fn + ".tmp", fn); + File::Rename(fn.WithExtraExtension(".tmp"), fn); } if (callback) { callback(status, message, data); @@ -499,7 +499,7 @@ namespace SaveState RenameIfExists(shot, shotUndo); } SaveScreenshot(shot, Callback(), 0); - Save(fn + ".tmp", slot, renameCallback, cbUserData); + Save(fn.WithExtraExtension(".tmp"), slot, renameCallback, cbUserData); } else { auto sy = GetI18NCategory("System"); if (callback) @@ -507,39 +507,38 @@ namespace SaveState } } - bool UndoSaveSlot(const std::string &gameFilename, int slot) { - std::string fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION); - std::string shot = GenerateSaveSlotFilename(gameFilename, slot, SCREENSHOT_EXTENSION); - std::string fnUndo = GenerateSaveSlotFilename(gameFilename, slot, UNDO_STATE_EXTENSION); - std::string shotUndo = GenerateSaveSlotFilename(gameFilename, slot, UNDO_SCREENSHOT_EXTENSION); + bool UndoSaveSlot(const Path &gameFilename, int slot) { + Path fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION); + Path shot = GenerateSaveSlotFilename(gameFilename, slot, SCREENSHOT_EXTENSION); + Path fnUndo = GenerateSaveSlotFilename(gameFilename, slot, UNDO_STATE_EXTENSION); + Path shotUndo = GenerateSaveSlotFilename(gameFilename, slot, UNDO_SCREENSHOT_EXTENSION); // Do nothing if there's no undo. if (File::Exists(fnUndo)) { // Swap them so they can undo again to redo. Mistakes happen. SwapIfExists(shotUndo, shot); SwapIfExists(fnUndo, fn); - return true; } return false; } - bool HasSaveInSlot(const std::string &gameFilename, int slot) + bool HasSaveInSlot(const Path &gameFilename, int slot) { - std::string fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION); + Path fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION); return File::Exists(fn); } - bool HasUndoSaveInSlot(const std::string &gameFilename, int slot) + bool HasUndoSaveInSlot(const Path &gameFilename, int slot) { - std::string fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION); - return File::Exists(fn + ".undo"); + Path fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION); + return File::Exists(fn.WithExtraExtension(".undo")); } - bool HasScreenshotInSlot(const std::string &gameFilename, int slot) + bool HasScreenshotInSlot(const Path &gameFilename, int slot) { - std::string fn = GenerateSaveSlotFilename(gameFilename, slot, SCREENSHOT_EXTENSION); + Path fn = GenerateSaveSlotFilename(gameFilename, slot, SCREENSHOT_EXTENSION); return File::Exists(fn); } @@ -580,11 +579,11 @@ namespace SaveState return true; } - int GetNewestSlot(const std::string &gameFilename) { + int GetNewestSlot(const Path &gameFilename) { int newestSlot = -1; tm newestDate = {0}; for (int i = 0; i < NUM_SLOTS; i++) { - std::string fn = GenerateSaveSlotFilename(gameFilename, i, STATE_EXTENSION); + Path fn = GenerateSaveSlotFilename(gameFilename, i, STATE_EXTENSION); if (File::Exists(fn)) { tm time; bool success = File::GetModifTime(fn, time); @@ -597,11 +596,11 @@ namespace SaveState return newestSlot; } - int GetOldestSlot(const std::string &gameFilename) { + int GetOldestSlot(const Path &gameFilename) { int oldestSlot = -1; tm oldestDate = {0}; for (int i = 0; i < NUM_SLOTS; i++) { - std::string fn = GenerateSaveSlotFilename(gameFilename, i, STATE_EXTENSION); + Path fn = GenerateSaveSlotFilename(gameFilename, i, STATE_EXTENSION); if (File::Exists(fn)) { tm time; bool success = File::GetModifTime(fn, time); @@ -614,8 +613,8 @@ namespace SaveState return oldestSlot; } - std::string GetSlotDateAsString(const std::string &gameFilename, int slot) { - std::string fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION); + std::string GetSlotDateAsString(const Path &gameFilename, int slot) { + Path fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION); if (File::Exists(fn)) { tm time; if (File::GetModifTime(fn, time)) { @@ -798,7 +797,7 @@ namespace SaveState title = g_paramSFO.GetValueString("TITLE"); if (title.empty()) { // Homebrew title - title = PSP_CoreParameter().fileToStart; + title = PSP_CoreParameter().fileToStart.ToVisualString(); std::size_t lslash = title.find_last_of("/"); title = title.substr(lslash + 1); } @@ -866,9 +865,9 @@ namespace SaveState { if (g_Config.bSavestateScreenshotResLimit) { int maxRes = g_Config.iInternalResolution > 2 ? 2 : -1; - tempResult = TakeGameScreenshot(op.filename.c_str(), ScreenshotFormat::JPG, SCREENSHOT_DISPLAY, nullptr, nullptr, maxRes); + tempResult = TakeGameScreenshot(op.filename, ScreenshotFormat::JPG, SCREENSHOT_DISPLAY, nullptr, nullptr, maxRes); } else { - tempResult = TakeGameScreenshot(op.filename.c_str(), ScreenshotFormat::JPG, SCREENSHOT_DISPLAY); + tempResult = TakeGameScreenshot(op.filename, ScreenshotFormat::JPG, SCREENSHOT_DISPLAY); } callbackResult = tempResult ? Status::SUCCESS : Status::FAILURE; if (!tempResult) { diff --git a/Core/SaveState.h b/Core/SaveState.h index 15a7c97462..d7bb0142fe 100644 --- a/Core/SaveState.h +++ b/Core/SaveState.h @@ -19,6 +19,7 @@ #include #include +#include "Common/File/Path.h" #include "Common/Serialize/Serializer.h" namespace SaveState @@ -41,32 +42,32 @@ namespace SaveState // Cycle through the 5 savestate slots void NextSlot(); - void SaveSlot(const std::string &gameFilename, int slot, Callback callback, void *cbUserData = 0); - void LoadSlot(const std::string &gameFilename, int slot, Callback callback, void *cbUserData = 0); - bool UndoSaveSlot(const std::string &gameFilename, int slot); + void SaveSlot(const Path &gameFilename, int slot, Callback callback, void *cbUserData = 0); + void LoadSlot(const Path &gameFilename, int slot, Callback callback, void *cbUserData = 0); + bool UndoSaveSlot(const Path &gameFilename, int slot); // Checks whether there's an existing save in the specified slot. - bool HasSaveInSlot(const std::string &gameFilename, int slot); - bool HasUndoSaveInSlot(const std::string &gameFilename, int slot); - bool HasScreenshotInSlot(const std::string &gameFilename, int slot); + bool HasSaveInSlot(const Path &gameFilename, int slot); + bool HasUndoSaveInSlot(const Path &gameFilename, int slot); + bool HasScreenshotInSlot(const Path &gameFilename, int slot); int GetCurrentSlot(); // Returns -1 if there's no oldest/newest slot. - int GetNewestSlot(const std::string &gameFilename); - int GetOldestSlot(const std::string &gameFilename); + int GetNewestSlot(const Path &gameFilename); + int GetOldestSlot(const Path &gameFilename); - std::string GetSlotDateAsString(const std::string &gameFilename, int slot); - std::string GenerateSaveSlotFilename(const std::string &gameFilename, int slot, const char *extension); + std::string GetSlotDateAsString(const Path &gameFilename, int slot); + Path GenerateSaveSlotFilename(const Path &gameFilename, int slot, const char *extension); - std::string GetTitle(const std::string &filename); + std::string GetTitle(const Path &filename); // Load the specified file into the current state (async.) // Warning: callback will be called on a different thread. - void Load(const std::string &filename, int slot, Callback callback = Callback(), void *cbUserData = 0); + void Load(const Path &filename, int slot, Callback callback = Callback(), void *cbUserData = 0); // Save the current state to the specified file (async.) // Warning: callback will be called on a different thread. - void Save(const std::string &filename, int slot, Callback callback = Callback(), void *cbUserData = 0); + void Save(const Path &filename, int slot, Callback callback = Callback(), void *cbUserData = 0); CChunkFileReader::Error SaveToRam(std::vector &state); CChunkFileReader::Error LoadFromRam(std::vector &state, std::string *errorString); diff --git a/Core/Screenshot.cpp b/Core/Screenshot.cpp index 8a06d54ab7..c049756798 100644 --- a/Core/Screenshot.cpp +++ b/Core/Screenshot.cpp @@ -21,8 +21,9 @@ #include #include "ext/jpge/jpge.h" -#include "Common/ColorConv.h" +#include "Common/Data/Convert/ColorConv.h" #include "Common/File/FileUtil.h" +#include "Common/File/Path.h" #include "Common/Log.h" #include "Common/System/Display.h" #include "Core/Config.h" @@ -37,7 +38,7 @@ class JPEGFileStream : public jpge::output_stream { public: - JPEGFileStream(const char *filename) { + JPEGFileStream(const Path &filename) { fp_ = File::OpenCFile(filename, "wb"); } ~JPEGFileStream() override { @@ -65,7 +66,7 @@ private: FILE *fp_; }; -static bool WriteScreenshotToJPEG(const char *filename, int width, int height, int num_channels, const uint8_t *image_data, const jpge::params &comp_params) { +static bool WriteScreenshotToJPEG(const Path &filename, int width, int height, int num_channels, const uint8_t *image_data, const jpge::params &comp_params) { JPEGFileStream dst_stream(filename); if (!dst_stream.Valid()) { ERROR_LOG(IO, "Unable to open screenshot file for writing."); @@ -97,7 +98,7 @@ static bool WriteScreenshotToJPEG(const char *filename, int width, int height, i return dst_stream.Valid(); } -static bool WriteScreenshotToPNG(png_imagep image, const char *filename, int convert_to_8bit, const void *buffer, png_int_32 row_stride, const void *colormap) { +static bool WriteScreenshotToPNG(png_imagep image, const Path &filename, int convert_to_8bit, const void *buffer, png_int_32 row_stride, const void *colormap) { FILE *fp = File::OpenCFile(filename, "wb"); if (!fp) { ERROR_LOG(IO, "Unable to open screenshot file for writing."); @@ -110,7 +111,8 @@ static bool WriteScreenshotToPNG(png_imagep image, const char *filename, int con } else { ERROR_LOG(IO, "Screenshot PNG encode failed."); fclose(fp); - remove(filename); + // Should we even do this? + File::Delete(filename); return false; } } @@ -326,7 +328,7 @@ static GPUDebugBuffer ApplyRotation(const GPUDebugBuffer &buf, DisplayRotation r return rotated; } -bool TakeGameScreenshot(const char *filename, ScreenshotFormat fmt, ScreenshotType type, int *width, int *height, int maxRes) { +bool TakeGameScreenshot(const Path &filename, ScreenshotFormat fmt, ScreenshotType type, int *width, int *height, int maxRes) { if (!gpuDebug) { ERROR_LOG(SYSTEM, "Can't take screenshots when GPU not running"); return false; @@ -378,7 +380,7 @@ bool TakeGameScreenshot(const char *filename, ScreenshotFormat fmt, ScreenshotTy return success; } -bool Save888RGBScreenshot(const char *filename, ScreenshotFormat fmt, const u8 *bufferRGB888, int w, int h) { +bool Save888RGBScreenshot(const Path &filename, ScreenshotFormat fmt, const u8 *bufferRGB888, int w, int h) { if (fmt == ScreenshotFormat::PNG) { png_image png; memset(&png, 0, sizeof(png)); @@ -403,7 +405,7 @@ bool Save888RGBScreenshot(const char *filename, ScreenshotFormat fmt, const u8 * } } -bool Save8888RGBAScreenshot(const char *filename, const u8 *buffer, int w, int h) { +bool Save8888RGBAScreenshot(const Path &filename, const u8 *buffer, int w, int h) { png_image png; memset(&png, 0, sizeof(png)); png.version = PNG_IMAGE_VERSION; diff --git a/Core/Screenshot.h b/Core/Screenshot.h index 1632a1434c..117cd5e625 100644 --- a/Core/Screenshot.h +++ b/Core/Screenshot.h @@ -17,6 +17,8 @@ #pragma once +#include "Common/File/Path.h" + struct GPUDebugBuffer; enum class ScreenshotFormat { @@ -36,6 +38,6 @@ enum ScreenshotType { const u8 *ConvertBufferToScreenshot(const GPUDebugBuffer &buf, bool alpha, u8 *&temp, u32 &w, u32 &h); // Can only be used while in game. -bool TakeGameScreenshot(const char *filename, ScreenshotFormat fmt, ScreenshotType type, int *width = nullptr, int *height = nullptr, int maxRes = -1); -bool Save888RGBScreenshot(const char *filename, ScreenshotFormat fmt, const u8 *bufferRGB888, int w, int h); -bool Save8888RGBAScreenshot(const char *filename, const u8 *bufferRGBA8888, int w, int h); +bool TakeGameScreenshot(const Path &filename, ScreenshotFormat fmt, ScreenshotType type, int *width = nullptr, int *height = nullptr, int maxRes = -1); +bool Save888RGBScreenshot(const Path &filename, ScreenshotFormat fmt, const u8 *bufferRGB888, int w, int h); +bool Save8888RGBAScreenshot(const Path &filename, const u8 *bufferRGBA8888, int w, int h); diff --git a/Core/System.cpp b/Core/System.cpp index 96d6ae5f8a..c6e510ed88 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -33,6 +33,7 @@ #include #include "Common/System/System.h" +#include "Common/File/Path.h" #include "Common/Math/math_util.h" #include "Common/Thread/ThreadUtil.h" #include "Common/Data/Encoding/Utf8.h" @@ -122,7 +123,8 @@ void UpdateUIState(GlobalUIState newState) { // Never leave the EXIT state. if (globalUIState != newState && globalUIState != UISTATE_EXIT) { globalUIState = newState; - host->UpdateDisassembly(); + if (host) + host->UpdateDisassembly(); const char *state = nullptr; switch (globalUIState) { case UISTATE_EXIT: state = "exit"; break; @@ -187,7 +189,7 @@ bool CPU_HasPendingAction() { void CPU_Shutdown(); -bool DiscIDFromGEDumpPath(const std::string &path, FileLoader *fileLoader, std::string *id) { +bool DiscIDFromGEDumpPath(const Path &path, FileLoader *fileLoader, std::string *id) { using namespace GPURecord; // For newer files, it's stored in the dump. @@ -204,7 +206,7 @@ bool DiscIDFromGEDumpPath(const std::string &path, FileLoader *fileLoader, std:: } // Fall back to using the filename. - std::string filename = File::GetFilename(path); + std::string filename = path.GetFilename(); // Could be more discerning, but hey.. if (filename.size() > 10 && filename[0] == 'U' && filename[9] == '_') { *id = filename.substr(0, 9); @@ -228,7 +230,7 @@ bool CPU_Init() { g_DoubleTextureCoordinates = false; Memory::g_PSPModel = g_Config.iPSPModel; - std::string filename = coreParameter.fileToStart; + Path filename = coreParameter.fileToStart; loadedFile = ResolveFileLoaderTarget(ConstructFileLoader(filename)); #if PPSSPP_ARCH(AMD64) if (g_Config.bCacheFullIsoInRam) { @@ -238,7 +240,7 @@ bool CPU_Init() { IdentifiedFileType type = Identify_File(loadedFile); // TODO: Put this somewhere better? - if (coreParameter.mountIso != "") { + if (!coreParameter.mountIso.empty()) { coreParameter.mountIsoLoader = ConstructFileLoader(coreParameter.mountIso); } @@ -283,7 +285,7 @@ bool CPU_Init() { // likely to collide with any commercial ones. coreParameter.compat.Load(g_paramSFO.GetDiscID()); - InitVFPUSinCos(coreParameter.compat.flags().DoublePrecisionSinCos); + InitVFPUSinCos(); if (allowPlugins) HLEPlugins::Init(); @@ -310,12 +312,12 @@ bool CPU_Init() { // Note: this may return before init is complete, which is checked if CPU_IsReady(). if (!LoadFile(&loadedFile, &coreParameter.errorString)) { CPU_Shutdown(); - coreParameter.fileToStart = ""; + coreParameter.fileToStart.clear(); return false; } if (coreParameter.updateRecent) { - g_Config.AddRecent(filename); + g_Config.AddRecent(filename.ToString()); } InstallExceptionHandler(&Memory::HandleFault); @@ -411,7 +413,10 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) { PSP_SetLoading("Loading game..."); if (!CPU_Init()) { - *error_string = "Failed initializing CPU/Memory"; + *error_string = coreParameter.errorString; + if (error_string->empty()) { + *error_string = "Failed initializing CPU/Memory"; + } pspIsIniting = false; return false; } @@ -422,7 +427,7 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) { } *error_string = coreParameter.errorString; - bool success = coreParameter.fileToStart != ""; + bool success = !coreParameter.fileToStart.empty(); if (!success) { Core_NotifyLifecycle(CoreLifecycle::START_COMPLETE); pspIsIniting = false; @@ -439,7 +444,7 @@ bool PSP_InitUpdate(std::string *error_string) { return false; } - bool success = coreParameter.fileToStart != ""; + bool success = !coreParameter.fileToStart.empty(); *error_string = coreParameter.errorString; if (success && gpu == nullptr) { PSP_SetLoading("Starting graphics..."); @@ -574,39 +579,39 @@ CoreParameter &PSP_CoreParameter() { return coreParameter; } -std::string GetSysDirectory(PSPDirectories directoryType) { +Path GetSysDirectory(PSPDirectories directoryType) { switch (directoryType) { case DIRECTORY_CHEATS: - return g_Config.memStickDirectory + "PSP/Cheats/"; + return g_Config.memStickDirectory / "PSP/Cheats"; case DIRECTORY_GAME: - return g_Config.memStickDirectory + "PSP/GAME/"; + return g_Config.memStickDirectory / "PSP/GAME"; case DIRECTORY_SAVEDATA: - return g_Config.memStickDirectory + "PSP/SAVEDATA/"; + return g_Config.memStickDirectory / "PSP/SAVEDATA"; case DIRECTORY_SCREENSHOT: - return g_Config.memStickDirectory + "PSP/SCREENSHOT/"; + return g_Config.memStickDirectory / "PSP/SCREENSHOT"; case DIRECTORY_SYSTEM: - return g_Config.memStickDirectory + "PSP/SYSTEM/"; + return g_Config.memStickDirectory / "PSP/SYSTEM"; case DIRECTORY_PAUTH: - return g_Config.memStickDirectory + "PAUTH/"; + return g_Config.memStickDirectory / "PAUTH"; case DIRECTORY_DUMP: - return g_Config.memStickDirectory + "PSP/SYSTEM/DUMP/"; + return g_Config.memStickDirectory / "PSP/SYSTEM/DUMP"; case DIRECTORY_SAVESTATE: - return g_Config.memStickDirectory + "PSP/PPSSPP_STATE/"; + return g_Config.memStickDirectory / "PSP/PPSSPP_STATE"; case DIRECTORY_CACHE: - return g_Config.memStickDirectory + "PSP/SYSTEM/CACHE/"; + return g_Config.memStickDirectory / "PSP/SYSTEM/CACHE"; case DIRECTORY_TEXTURES: - return g_Config.memStickDirectory + "PSP/TEXTURES/"; + return g_Config.memStickDirectory / "PSP/TEXTURES"; case DIRECTORY_PLUGINS: - return g_Config.memStickDirectory + "PSP/PLUGINS/"; + return g_Config.memStickDirectory / "PSP/PLUGINS"; case DIRECTORY_APP_CACHE: if (!g_Config.appCacheDirectory.empty()) { return g_Config.appCacheDirectory; } - return g_Config.memStickDirectory + "PSP/SYSTEM/CACHE/"; + return g_Config.memStickDirectory / "PSP/SYSTEM/CACHE"; case DIRECTORY_VIDEO: - return g_Config.memStickDirectory + "PSP/VIDEO/"; + return g_Config.memStickDirectory / "PSP/VIDEO"; case DIRECTORY_AUDIO: - return g_Config.memStickDirectory + "PSP/AUDIO/"; + return g_Config.memStickDirectory / "PSP/AUDIO"; case DIRECTORY_MEMSTICK_ROOT: return g_Config.memStickDirectory; // Just return the memory stick root if we run into some sort of problem. @@ -616,16 +621,16 @@ std::string GetSysDirectory(PSPDirectories directoryType) { } } -#if defined(_WIN32) +#if PPSSPP_PLATFORM(WINDOWS) // Run this at startup time. Please use GetSysDirectory if you need to query where folders are. void InitSysDirectories() { if (!g_Config.memStickDirectory.empty() && !g_Config.flash0Directory.empty()) return; - const std::string path = File::GetExeDirectory(); + const Path &path = File::GetExeDirectory(); // Mount a filesystem - g_Config.flash0Directory = path + "assets/flash0/"; + g_Config.flash0Directory = path / "assets/flash0"; // Detect the "My Documents"(XP) or "Documents"(on Vista/7/8) folder. #if PPSSPP_PLATFORM(UWP) @@ -633,41 +638,34 @@ void InitSysDirectories() { #else // Caller sets this to the Documents folder. - const std::string rootMyDocsPath = g_Config.internalDataDirectory; - const std::string myDocsPath = rootMyDocsPath + "/PPSSPP/"; - const std::string installedFile = path + "installed.txt"; + const Path rootMyDocsPath = g_Config.internalDataDirectory; + const Path myDocsPath = rootMyDocsPath / "PPSSPP"; + const Path installedFile = path / "installed.txt"; const bool installed = File::Exists(installedFile); // If installed.txt exists(and we can determine the Documents directory) - if (installed && rootMyDocsPath.size() > 0) { -#if defined(_WIN32) && defined(__MINGW32__) - std::ifstream inputFile(installedFile); -#else - std::ifstream inputFile(ConvertUTF8ToWString(installedFile)); -#endif - - if (!inputFile.fail() && inputFile.is_open()) { - std::string tempString; - - std::getline(inputFile, tempString); - + if (installed && !rootMyDocsPath.empty()) { + FILE *fp = File::OpenCFile(installedFile, "rt"); + if (fp) { + char temp[2048]; + char *tempStr = fgets(temp, sizeof(temp), fp); // Skip UTF-8 encoding bytes if there are any. There are 3 of them. - if (tempString.substr(0, 3) == "\xEF\xBB\xBF") - tempString = tempString.substr(3); + if (tempStr && strncmp(tempStr, "\xEF\xBB\xBF", 3) == 0) { + tempStr += 3; + } + std::string tempString = tempStr ? tempStr : ""; + if (!tempString.empty() && tempString.back() == '\n') + tempString.resize(tempString.size() - 1); - g_Config.memStickDirectory = tempString; + g_Config.memStickDirectory = Path(tempString); + fclose(fp); } - inputFile.close(); // Check if the file is empty first, before appending the slash. if (g_Config.memStickDirectory.empty()) g_Config.memStickDirectory = myDocsPath; - - size_t lastSlash = g_Config.memStickDirectory.find_last_of("/"); - if (lastSlash != (g_Config.memStickDirectory.length() - 1)) - g_Config.memStickDirectory.append("/"); } else { - g_Config.memStickDirectory = path + "memstick/"; + g_Config.memStickDirectory = path / "memstick"; } // Create the memstickpath before trying to write to it, and fall back on Documents yet again @@ -678,7 +676,7 @@ void InitSysDirectories() { INFO_LOG(COMMON, "Memstick directory not present, creating at '%s'", g_Config.memStickDirectory.c_str()); } - const std::string testFile = g_Config.memStickDirectory + "_writable_test.$$$"; + Path testFile = g_Config.memStickDirectory / "_writable_test.$$$"; // If any directory is read-only, fall back to the Documents directory. // We're screwed anyway if we can't write to Documents, or can't detect it. @@ -692,14 +690,14 @@ void InitSysDirectories() { // Create the default directories that a real PSP creates. Good for homebrew so they can // expect a standard environment. Skipping THEME though, that's pointless. - File::CreateDir(g_Config.memStickDirectory + "PSP"); - File::CreateDir(g_Config.memStickDirectory + "PSP/COMMON"); + File::CreateDir(g_Config.memStickDirectory / "PSP"); + File::CreateDir(g_Config.memStickDirectory / "PSP/COMMON"); File::CreateDir(GetSysDirectory(DIRECTORY_GAME)); File::CreateDir(GetSysDirectory(DIRECTORY_SAVEDATA)); File::CreateDir(GetSysDirectory(DIRECTORY_SAVESTATE)); if (g_Config.currentDirectory.empty()) { - g_Config.currentDirectory = GetSysDirectory(DIRECTORY_GAME); + g_Config.currentDirectory = GetSysDirectory(DIRECTORY_GAME).ToString(); } } #endif diff --git a/Core/System.h b/Core/System.h index 3a927a676e..c66e45ab67 100644 --- a/Core/System.h +++ b/Core/System.h @@ -18,6 +18,7 @@ #pragma once #include "Common/CommonTypes.h" +#include "Common/File/Path.h" #include "Core/CoreParameter.h" class MetaFileSystem; @@ -96,7 +97,7 @@ bool IsAudioInitialised(); void UpdateLoadedFile(FileLoader *fileLoader); -std::string GetSysDirectory(PSPDirectories directoryType); +Path GetSysDirectory(PSPDirectories directoryType); #ifdef _WIN32 void InitSysDirectories(); #endif diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index 8969267b67..b692072d4d 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -17,20 +17,24 @@ #include "ppsspp_config.h" #include +#include +#include #include #include "ext/xxhash.h" -#include "Common/Data/Text/I18n.h" +#include "Common/Data/Convert/ColorConv.h" #include "Common/Data/Format/IniFile.h" +#include "Common/Data/Format/ZIMLoad.h" +#include "Common/Data/Text/I18n.h" #include "Common/Data/Text/Parsers.h" -#include "Common/ColorConv.h" #include "Common/File/FileUtil.h" #include "Common/StringUtils.h" #include "Core/Config.h" #include "Core/Host.h" #include "Core/System.h" #include "Core/TextureReplacer.h" +#include "Core/ThreadPools.h" #include "Core/ELF/ParamSFO.h" #include "GPU/Common/TextureDecoder.h" @@ -55,12 +59,14 @@ void TextureReplacer::NotifyConfigChanged() { enabled_ = g_Config.bReplaceTextures || g_Config.bSaveNewTextures; if (enabled_) { - basePath_ = GetSysDirectory(DIRECTORY_TEXTURES) + gameID_ + "/"; + basePath_ = GetSysDirectory(DIRECTORY_TEXTURES) / gameID_; + + Path newTextureDir = basePath_ / NEW_TEXTURE_DIR; // If we're saving, auto-create the directory. - if (g_Config.bSaveNewTextures && !File::Exists(basePath_ + NEW_TEXTURE_DIR)) { - File::CreateFullPath(basePath_ + NEW_TEXTURE_DIR); - File::CreateEmptyFile(basePath_ + NEW_TEXTURE_DIR + "/.nomedia"); + if (g_Config.bSaveNewTextures && !File::Exists(newTextureDir)) { + File::CreateFullPath(newTextureDir); + File::CreateEmptyFile(newTextureDir / ".nomedia"); } enabled_ = File::Exists(basePath_) && File::IsDirectory(basePath_); @@ -77,16 +83,18 @@ bool TextureReplacer::LoadIni() { aliases_.clear(); hashranges_.clear(); filtering_.clear(); + reducehashranges_.clear(); allowVideo_ = false; ignoreAddress_ = false; reduceHash_ = false; + reduceHashGlobalValue = 0.5; // Prevents dumping the mipmaps. ignoreMipmap_ = false; - if (File::Exists(basePath_ + INI_FILENAME)) { + if (File::Exists(basePath_ / INI_FILENAME)) { IniFile ini; - ini.LoadFromVFS(basePath_ + INI_FILENAME); + ini.LoadFromVFS((basePath_ / INI_FILENAME).ToString()); if (!LoadIniValues(ini)) { return false; @@ -98,7 +106,7 @@ bool TextureReplacer::LoadIni() { if (!overrideFilename.empty() && overrideFilename != INI_FILENAME) { INFO_LOG(G3D, "Loading extra texture ini: %s", overrideFilename.c_str()); IniFile overrideIni; - overrideIni.LoadFromVFS(basePath_ + overrideFilename); + overrideIni.LoadFromVFS((basePath_ / overrideFilename).ToString()); if (!LoadIniValues(overrideIni, true)) { return false; @@ -192,6 +200,14 @@ bool TextureReplacer::LoadIniValues(IniFile &ini, bool isOverride) { } } + if (ini.HasSection("reducehashranges")) { + auto reducehashranges = ini.GetOrCreateSection("reducehashranges")->ToMap(); + // Format: w,h = reducehashvalues + for (const auto& item : reducehashranges) { + ParseReduceHashRange(item.first, item.second); + } + } + return true; } @@ -247,6 +263,39 @@ void TextureReplacer::ParseFiltering(const std::string &key, const std::string & } } +void TextureReplacer::ParseReduceHashRange(const std::string& key, const std::string& value) { + std::vector keyParts; + SplitString(key, ',', keyParts); + std::vector valueParts; + SplitString(value, ',', valueParts); + + if (keyParts.size() != 2 || valueParts.size() != 1) { + ERROR_LOG(G3D, "Ignoring invalid reducehashrange %s = %s, expecting w,h = reducehashvalue", key.c_str(), value.c_str()); + return; + } + + u32 forW; + u32 forH; + if (!TryParse(keyParts[0], &forW) || !TryParse(keyParts[1], &forH)) { + ERROR_LOG(G3D, "Ignoring invalid reducehashrange %s = %s, key format is 512,512", key.c_str(), value.c_str()); + return; + } + + float rhashvalue; + if (!TryParse(valueParts[0], &rhashvalue)) { + ERROR_LOG(G3D, "Ignoring invalid reducehashrange %s = %s, value format is 0.5", key.c_str(), value.c_str()); + return; + } + + if (rhashvalue == 0) { + ERROR_LOG(G3D, "Ignoring invalid hashrange %s = %s, reducehashvalue can't be 0", key.c_str(), value.c_str()); + return; + } + + const u64 reducerangeKey = ((u64)forW << 16) | forH; + reducehashranges_[reducerangeKey] = rhashvalue; +} + u32 TextureReplacer::ComputeHash(u32 addr, int bufw, int w, int h, GETextureFormat fmt, u16 maxSeenV) { _dbg_assert_msg_(enabled_, "Replacement not enabled"); @@ -258,9 +307,10 @@ u32 TextureReplacer::ComputeHash(u32 addr, int bufw, int w, int h, GETextureForm } const u8 *checkp = Memory::GetPointer(addr); - float reduceHashSize = 1.0; - if (reduceHash_) - reduceHashSize = 0.5; + if (reduceHash_) { + reduceHashSize = LookupReduceHashRange(w, h); + // default to reduceHashGlobalValue which default is 0.5 + } if (bufw <= w) { // We can assume the data is contiguous. These are the total used pixels. const u32 totalPixels = bufw * h + (w - bufw); @@ -345,31 +395,20 @@ void TextureReplacer::PopulateReplacement(ReplacedTexture *result, u64 cachekey, for (int i = 0; i < MAX_MIP_LEVELS; ++i) { const std::string hashfile = LookupHashFile(cachekey, hash, i); - const std::string filename = basePath_ + hashfile; + const Path filename = basePath_ / hashfile; if (hashfile.empty() || !File::Exists(filename)) { // Out of valid mip levels. Bail out. break; } - bool good = false; ReplacedTextureLevel level; level.fmt = ReplacedTextureFormat::F_8888; level.file = filename; + bool good = PopulateLevel(level); - png_image png = {}; - png.version = PNG_IMAGE_VERSION; - FILE *fp = File::OpenCFile(filename, "rb"); - if (png_image_begin_read_from_stdio(&png, fp)) { - // We pad files that have been hashrange'd so they are the same texture size. - level.w = (png.width * w) / newW; - level.h = (png.height * h) / newH; - good = true; - } else { - ERROR_LOG(G3D, "Could not load texture replacement info: %s - %s", filename.c_str(), png.message); - } - fclose(fp); - - png_image_free(&png); + // We pad files that have been hashrange'd so they are the same texture size. + level.w = (level.w * w) / newW; + level.h = (level.h * h) / newH; if (good && i != 0) { // Check that the mipmap size is correct. Can't load mips of the wrong size. @@ -389,7 +428,59 @@ void TextureReplacer::PopulateReplacement(ReplacedTexture *result, u64 cachekey, result->alphaStatus_ = ReplacedTextureAlpha::UNKNOWN; } -static bool WriteTextureToPNG(png_imagep image, const std::string &filename, int convert_to_8bit, const void *buffer, png_int_32 row_stride, const void *colormap) { +enum class ReplacedImageType { + PNG, + ZIM, + INVALID, +}; + +static ReplacedImageType Identify(FILE *fp) { + uint8_t magic[4]; + if (fread(magic, 1, 4, fp) != 4) + return ReplacedImageType::INVALID; + rewind(fp); + + if (strncmp((const char *)magic, "ZIMG", 4) == 0) + return ReplacedImageType::ZIM; + if (magic[0] == 0x89 && strncmp((const char *)&magic[1], "PNG", 3) == 0) + return ReplacedImageType::PNG; + return ReplacedImageType::INVALID; +} + +bool TextureReplacer::PopulateLevel(ReplacedTextureLevel &level) { + bool good = false; + + FILE *fp = File::OpenCFile(level.file, "rb"); + auto imageType = Identify(fp); + if (imageType == ReplacedImageType::ZIM) { + fseek(fp, 4, SEEK_SET); + fread(&level.w, 1, 4, fp); + fread(&level.h, 1, 4, fp); + int flags; + if (fread(&flags, 1, 4, fp) == 4) { + good = (flags & ZIM_FORMAT_MASK) == ZIM_RGBA8888; + } + } else if (imageType == ReplacedImageType::PNG) { + png_image png = {}; + png.version = PNG_IMAGE_VERSION; + if (png_image_begin_read_from_stdio(&png, fp)) { + // We pad files that have been hashrange'd so they are the same texture size. + level.w = png.width; + level.h = png.height; + good = true; + } else { + ERROR_LOG(G3D, "Could not load texture replacement info: %s - %s", level.file.ToVisualString().c_str(), png.message); + } + png_image_free(&png); + } else { + ERROR_LOG(G3D, "Could not load texture replacement info: %s - unsupported format", level.file.ToVisualString().c_str()); + } + fclose(fp); + + return good; +} + +static bool WriteTextureToPNG(png_imagep image, const Path &filename, int convert_to_8bit, const void *buffer, png_int_32 row_stride, const void *colormap) { FILE *fp = File::OpenCFile(filename, "wb"); if (!fp) { ERROR_LOG(IO, "Unable to open texture file for writing."); @@ -429,8 +520,8 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl } std::string hashfile = LookupHashFile(cachekey, replacedInfo.hash, level); - const std::string filename = basePath_ + hashfile; - const std::string saveFilename = basePath_ + NEW_TEXTURE_DIR + hashfile; + const Path filename = basePath_ / hashfile; + const Path saveFilename = basePath_ / NEW_TEXTURE_DIR / hashfile; // If it's empty, it's an ignored hash, we intentionally don't save. if (hashfile.empty() || File::Exists(filename)) { @@ -454,10 +545,10 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl #endif if (slash != hashfile.npos) { // Create any directory structure as needed. - const std::string saveDirectory = basePath_ + NEW_TEXTURE_DIR + hashfile.substr(0, slash); + const Path saveDirectory = basePath_ / NEW_TEXTURE_DIR / hashfile.substr(0, slash); if (!File::Exists(saveDirectory)) { File::CreateFullPath(saveDirectory); - File::CreateEmptyFile(saveDirectory + "/.nomedia"); + File::CreateEmptyFile(saveDirectory / ".nomedia"); } } @@ -629,91 +720,125 @@ bool TextureReplacer::LookupHashRange(u32 addr, int &w, int &h) { return false; } +float TextureReplacer::LookupReduceHashRange(int& w, int& h) { + const u64 reducerangeKey = ((u64)w << 16) | h; + auto range = reducehashranges_.find(reducerangeKey); + if (range != reducehashranges_.end()) { + float rhv = range->second; + return rhv; + } + else { + return reduceHashGlobalValue; + } +} + void ReplacedTexture::Load(int level, void *out, int rowPitch) { _assert_msg_((size_t)level < levels_.size(), "Invalid miplevel"); _assert_msg_(out != nullptr && rowPitch > 0, "Invalid out/pitch"); const ReplacedTextureLevel &info = levels_[level]; - png_image png = {}; - png.version = PNG_IMAGE_VERSION; - FILE *fp = File::OpenCFile(info.file, "rb"); - if (!png_image_begin_read_from_stdio(&png, fp)) { - ERROR_LOG(G3D, "Could not load texture replacement info: %s - %s", info.file.c_str(), png.message); - return; - } + auto imageType = Identify(fp); + if (imageType == ReplacedImageType::ZIM) { + size_t zimSize = File::GetFileSize(fp); + std::unique_ptr zim(new uint8_t[zimSize]); + fread(&zim[0], 1, zimSize, fp); - bool checkedAlpha = false; - if ((png.format & PNG_FORMAT_FLAG_ALPHA) == 0) { - // Well, we know for sure it doesn't have alpha. - if (level == 0) { - alphaStatus_ = ReplacedTextureAlpha::FULL; + int w, h, f; + uint8_t *image; + if (LoadZIMPtr(&zim[0], zimSize, &w, &h, &f, &image)) { + GlobalThreadPool::Loop([&](int low, int high) { + for (int y = low; y < high; ++y) { + memcpy((uint8_t *)out + rowPitch * y, image + w * 4 * y, w * 4); + } + }, 0, h); + free(image); } - checkedAlpha = true; - } - png.format = PNG_FORMAT_RGBA; - if (!png_image_finish_read(&png, nullptr, out, rowPitch, nullptr)) { - ERROR_LOG(G3D, "Could not load texture replacement: %s - %s", info.file.c_str(), png.message); - return; - } - - if (!checkedAlpha) { // This will only check the hashed bits. - CheckAlphaResult res = CheckAlphaRGBA8888Basic((u32 *)out, rowPitch / sizeof(u32), png.width, png.height); + CheckAlphaResult res = CheckAlphaRGBA8888Basic((u32 *)out, rowPitch / sizeof(u32), w, h); if (res == CHECKALPHA_ANY || level == 0) { alphaStatus_ = ReplacedTextureAlpha(res); } + } else if (imageType == ReplacedImageType::PNG) { + png_image png = {}; + png.version = PNG_IMAGE_VERSION; + + if (!png_image_begin_read_from_stdio(&png, fp)) { + ERROR_LOG(G3D, "Could not load texture replacement info: %s - %s", info.file.c_str(), png.message); + return; + } + + bool checkedAlpha = false; + if ((png.format & PNG_FORMAT_FLAG_ALPHA) == 0) { + // Well, we know for sure it doesn't have alpha. + if (level == 0) { + alphaStatus_ = ReplacedTextureAlpha::FULL; + } + checkedAlpha = true; + } + png.format = PNG_FORMAT_RGBA; + + if (!png_image_finish_read(&png, nullptr, out, rowPitch, nullptr)) { + ERROR_LOG(G3D, "Could not load texture replacement: %s - %s", info.file.c_str(), png.message); + return; + } + png_image_free(&png); + + if (!checkedAlpha) { + // This will only check the hashed bits. + CheckAlphaResult res = CheckAlphaRGBA8888Basic((u32 *)out, rowPitch / sizeof(u32), png.width, png.height); + if (res == CHECKALPHA_ANY || level == 0) { + alphaStatus_ = ReplacedTextureAlpha(res); + } + } } fclose(fp); - png_image_free(&png); } -bool TextureReplacer::GenerateIni(const std::string &gameID, std::string *generatedFilename) { +bool TextureReplacer::GenerateIni(const std::string &gameID, Path &generatedFilename) { if (gameID.empty()) return false; - std::string texturesDirectory = GetSysDirectory(DIRECTORY_TEXTURES) + gameID + "/"; + Path texturesDirectory = GetSysDirectory(DIRECTORY_TEXTURES) / gameID; if (!File::Exists(texturesDirectory)) { File::CreateFullPath(texturesDirectory); } - if (generatedFilename) - *generatedFilename = texturesDirectory + INI_FILENAME; - if (File::Exists(texturesDirectory + INI_FILENAME)) + generatedFilename = texturesDirectory / INI_FILENAME; + if (File::Exists(generatedFilename)) return true; - FILE *f = File::OpenCFile(texturesDirectory + INI_FILENAME, "wb"); + FILE *f = File::OpenCFile(generatedFilename, "wb"); if (f) { - fwrite("\xEF\xBB\xBF", 0, 3, f); - fclose(f); + fwrite("\xEF\xBB\xBF", 1, 3, f); // Let's also write some defaults. - std::fstream fs; - File::OpenCPPFile(fs, texturesDirectory + INI_FILENAME, std::ios::out | std::ios::ate); - fs << "# This file is optional and describes your textures.\n"; - fs << "# Some information on syntax available here:\n"; - fs << "# https://github.com/hrydgard/ppsspp/wiki/Texture-replacement-ini-syntax\n"; - fs << "[options]\n"; - fs << "version = 1\n"; - fs << "hash = quick\n"; - fs << "ignoreMipmap = false\n"; - fs << "\n"; - fs << "[games]\n"; - fs << "# Used to make it easier to install, and override settings for other regions.\n"; - fs << "# Files still have to be copied to each TEXTURES folder."; - fs << gameID << " = " << INI_FILENAME << "\n"; - fs << "\n"; - fs << "[hashes]\n"; - fs << "# Use / for folders not \\, avoid special characters, and stick to lowercase.\n"; - fs << "# See wiki for more info.\n"; - fs << "\n"; - fs << "[hashranges]\n"; - fs << "\n"; - fs << "[filtering]\n"; - fs.close(); + fprintf(f, "# This file is optional and describes your textures.\n"); + fprintf(f, "# Some information on syntax available here:\n"); + fprintf(f, "# https://github.com/hrydgard/ppsspp/wiki/Texture-replacement-ini-syntax \n"); + fprintf(f, "[options]\n"); + fprintf(f, "version = 1\n"); + fprintf(f, "hash = quick\n"); + fprintf(f, "ignoreMipmap = false\n"); + fprintf(f, "\n"); + fprintf(f, "[games]\n"); + fprintf(f, "# Used to make it easier to install, and override settings for other regions.\n"); + fprintf(f, "# Files still have to be copied to each TEXTURES folder."); + fprintf(f, "%s = %s\n", gameID.c_str(), INI_FILENAME.c_str()); + fprintf(f, "\n"); + fprintf(f, "[hashes]\n"); + fprintf(f, "# Use / for folders not \\, avoid special characters, and stick to lowercase.\n"); + fprintf(f, "# See wiki for more info.\n"); + fprintf(f, "\n"); + fprintf(f, "[hashranges]\n"); + fprintf(f, "\n"); + fprintf(f, "[filtering]\n"); + fprintf(f, "\n"); + fprintf(f, "[reducehashranges]\n"); + fclose(f); } - return File::Exists(texturesDirectory + INI_FILENAME); + return File::Exists(generatedFilename); } diff --git a/Core/TextureReplacer.h b/Core/TextureReplacer.h index 46d67037ed..1410389157 100644 --- a/Core/TextureReplacer.h +++ b/Core/TextureReplacer.h @@ -21,8 +21,11 @@ #include #include #include + #include "Common/Common.h" #include "Common/MemoryUtil.h" +#include "Common/File/Path.h" + #include "GPU/Common/TextureDecoder.h" #include "GPU/ge_constants.h" @@ -58,7 +61,7 @@ struct ReplacedTextureLevel { int w; int h; ReplacedTextureFormat fmt; - std::string file; + Path file; }; struct ReplacementCacheKey { @@ -188,29 +191,35 @@ public: void NotifyTextureDecoded(const ReplacedTextureDecodeInfo &replacedInfo, const void *data, int pitch, int level, int w, int h); - static bool GenerateIni(const std::string &gameID, std::string *generatedFilename); + static bool GenerateIni(const std::string &gameID, Path &generatedFilename); protected: bool LoadIni(); bool LoadIniValues(IniFile &ini, bool isOverride = false); void ParseHashRange(const std::string &key, const std::string &value); void ParseFiltering(const std::string &key, const std::string &value); + void ParseReduceHashRange(const std::string& key, const std::string& value); bool LookupHashRange(u32 addr, int &w, int &h); + float LookupReduceHashRange(int& w, int& h); std::string LookupHashFile(u64 cachekey, u32 hash, int level); std::string HashName(u64 cachekey, u32 hash, int level); void PopulateReplacement(ReplacedTexture *result, u64 cachekey, u32 hash, int w, int h); + bool PopulateLevel(ReplacedTextureLevel &level); SimpleBuf saveBuf; bool enabled_ = false; bool allowVideo_ = false; bool ignoreAddress_ = false; bool reduceHash_ = false; + float reduceHashSize = 1.0; // default value with reduceHash to false + float reduceHashGlobalValue = 0.5; // Global value for textures dump pngs of all sizes, 0.5 by default but can be set in textures.ini bool ignoreMipmap_ = false; std::string gameID_; - std::string basePath_; + Path basePath_; ReplacedTextureHash hash_ = ReplacedTextureHash::QUICK; typedef std::pair WidthHeightPair; std::unordered_map hashranges_; + std::unordered_map reducehashranges_; std::unordered_map aliases_; std::unordered_map filtering_; diff --git a/Core/ThreadPools.cpp b/Core/ThreadPools.cpp index 9af420cf37..e4eb7b5b9d 100644 --- a/Core/ThreadPools.cpp +++ b/Core/ThreadPools.cpp @@ -6,9 +6,19 @@ std::unique_ptr GlobalThreadPool::pool; std::once_flag GlobalThreadPool::init_flag; -void GlobalThreadPool::Loop(const std::function& loop, int lower, int upper) { +void GlobalThreadPool::Loop(const std::function& loop, int lower, int upper, int minSize) { std::call_once(init_flag, Inititialize); - pool->ParallelLoop(loop, lower, upper); + pool->ParallelLoop(loop, lower, upper, minSize); +} + +void GlobalThreadPool::Memcpy(void *dest, const void *src, int size) { + std::call_once(init_flag, Inititialize); + pool->ParallelMemcpy(dest, src, size); +} + +void GlobalThreadPool::Memset(void *dest, uint8_t val, int size) { + std::call_once(init_flag, Inititialize); + pool->ParallelMemset(dest, val, size); } void GlobalThreadPool::Inititialize() { diff --git a/Core/ThreadPools.h b/Core/ThreadPools.h index 350d56da1e..86af0afdad 100644 --- a/Core/ThreadPools.h +++ b/Core/ThreadPools.h @@ -6,7 +6,9 @@ class GlobalThreadPool { public: // will execute slices of "loop" from "lower" to "upper" // in parallel on the global thread pool - static void Loop(const std::function& loop, int lower, int upper); + static void Loop(const std::function& loop, int lower, int upper, int minSize = -1); + static void Memcpy(void *dest, const void *src, int size); + static void Memset(void *dest, uint8_t val, int size); private: static std::unique_ptr pool; diff --git a/Core/Util/GameManager.cpp b/Core/Util/GameManager.cpp index 3e0c618b0e..b77d81e6d3 100644 --- a/Core/Util/GameManager.cpp +++ b/Core/Util/GameManager.cpp @@ -15,6 +15,8 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "ppsspp_config.h" + #include #include #include @@ -50,21 +52,21 @@ GameManager g_GameManager; GameManager::GameManager() { } -std::string GameManager::GetTempFilename() const { +Path GameManager::GetTempFilename() const { #ifdef _WIN32 wchar_t tempPath[MAX_PATH]; GetTempPath(MAX_PATH, tempPath); wchar_t buffer[MAX_PATH]; GetTempFileName(tempPath, L"PSP", 1, buffer); - return ConvertWStringToUTF8(buffer); + return Path(buffer); #else - return g_Config.memStickDirectory + "ppsspp.dl"; + return g_Config.memStickDirectory / "ppsspp.dl"; #endif } bool GameManager::IsGameInstalled(std::string name) { - std::string pspGame = GetSysDirectory(DIRECTORY_GAME); - return File::Exists(pspGame + name); + Path pspGame = GetSysDirectory(DIRECTORY_GAME); + return File::Exists(pspGame / name); } bool GameManager::DownloadAndInstall(std::string storeFileUrl) { @@ -77,11 +79,17 @@ bool GameManager::DownloadAndInstall(std::string storeFileUrl) { return false; } - std::string filename = GetTempFilename(); + Path filename = GetTempFilename(); curDownload_ = g_DownloadManager.StartDownload(storeFileUrl, filename); return true; } +bool GameManager::IsDownloading(std::string storeZipUrl) { + if (curDownload_) + return curDownload_->url() == storeZipUrl; + return false; +} + bool GameManager::CancelDownload() { if (!curDownload_) return false; @@ -91,12 +99,18 @@ bool GameManager::CancelDownload() { return true; } +float GameManager::DownloadSpeedKBps() { + if (curDownload_) + return curDownload_->SpeedKBps(); + return 0.0f; +} + bool GameManager::Uninstall(std::string name) { if (name.empty()) { ERROR_LOG(HLE, "Cannot remove an empty-named game"); return false; } - std::string gameDir = GetSysDirectory(DIRECTORY_GAME) + name; + Path gameDir = GetSysDirectory(DIRECTORY_GAME) / name; INFO_LOG(HLE, "Deleting '%s'", gameDir.c_str()); if (!File::Exists(gameDir)) { ERROR_LOG(HLE, "Game '%s' not installed, cannot uninstall", name.c_str()); @@ -117,7 +131,7 @@ bool GameManager::Uninstall(std::string name) { void GameManager::Update() { if (curDownload_.get() && curDownload_->Done()) { INFO_LOG(HLE, "Download completed! Status = %d", curDownload_->ResultCode()); - std::string fileName = curDownload_->outfile(); + Path fileName = Path(curDownload_->outfile()); if (curDownload_->ResultCode() == 200) { if (!File::Exists(fileName)) { ERROR_LOG(HLE, "Downloaded file '%s' does not exist :(", fileName.c_str()); @@ -125,11 +139,11 @@ void GameManager::Update() { return; } // Game downloaded to temporary file - install it! - InstallGameOnThread(curDownload_->url(), fileName, true); + InstallGameOnThread(Path(curDownload_->url()), fileName, true); } else { ERROR_LOG(HLE, "Expected HTTP status code 200, got status code %d. Install cancelled, deleting partial file '%s'", curDownload_->ResultCode(), fileName.c_str()); - File::Delete(fileName.c_str()); + File::Delete(fileName); } curDownload_.reset(); } @@ -157,13 +171,9 @@ static void countSlashes(const std::string &fileName, int *slashLocation, int *s } } -ZipFileContents DetectZipFileContents(std::string fileName, ZipFileInfo *info) { +ZipFileContents DetectZipFileContents(const Path &fileName, ZipFileInfo *info) { int error = 0; -#ifdef _WIN32 - struct zip *z = zip_open(ConvertUTF8ToWString(fileName).c_str(), 0, &error); -#else struct zip *z = zip_open(fileName.c_str(), 0, &error); -#endif if (!z) { return ZipFileContents::UNKNOWN; } @@ -246,7 +256,8 @@ ZipFileContents DetectZipFileContents(struct zip *z, ZipFileInfo *info) { } } -bool GameManager::InstallGame(const std::string &url, const std::string &fileName, bool deleteAfter) { +// Parameters need to be by value, since this is a thread func. +bool GameManager::InstallGame(Path url, Path fileName, bool deleteAfter) { if (installInProgress_) { ERROR_LOG(HLE, "Cannot have two installs in progress at the same time"); return false; @@ -257,24 +268,23 @@ bool GameManager::InstallGame(const std::string &url, const std::string &fileNam return false; } + std::string extension = url.GetFileExtension(); // Examine the URL to guess out what we're installing. - if (endsWithNoCase(url, ".cso") || endsWithNoCase(url, ".iso")) { + if (extension == "cso" || extension == "iso") { // It's a raw ISO or CSO file. We just copy it to the destination. - std::string shortFilename = GetFilenameFromPath(url); + std::string shortFilename = url.GetFilename(); return InstallRawISO(fileName, shortFilename, deleteAfter); } auto sy = GetI18NCategory("System"); installInProgress_ = true; - std::string pspGame = GetSysDirectory(DIRECTORY_GAME); - std::string dest = pspGame; + Path pspGame = GetSysDirectory(DIRECTORY_GAME); + Path dest = pspGame; int error = 0; -#ifdef _WIN32 - struct zip *z = zip_open(ConvertUTF8ToWString(fileName).c_str(), 0, &error); -#else + + // TODO(scoped): zip_open ain't gonna work. zip_fdopen though.. struct zip *z = zip_open(fileName.c_str(), 0, &error); -#endif if (!z) { ERROR_LOG(HLE, "Failed to open ZIP file '%s', error code=%i", fileName.c_str(), error); return false; @@ -293,10 +303,10 @@ bool GameManager::InstallGame(const std::string &url, const std::string &fileNam return InstallZippedISO(z, info.isoFileIndex, fileName, deleteAfter); case ZipFileContents::TEXTURE_PACK: // InstallMemstickGame contains code to close z, and works for textures too. - if (DetectTexturePackDest(z, info.textureIniIndex, &dest)) { + if (DetectTexturePackDest(z, info.textureIniIndex, dest)) { INFO_LOG(HLE, "Installing '%s' into '%s'", fileName.c_str(), dest.c_str()); File::CreateFullPath(dest); - File::CreateEmptyFile(dest + "/.nomedia"); + File::CreateEmptyFile(dest / ".nomedia"); return InstallMemstickGame(z, fileName, dest, info, true, deleteAfter); } else { zip_close(z); @@ -314,7 +324,7 @@ bool GameManager::InstallGame(const std::string &url, const std::string &fileNam } } -bool GameManager::DetectTexturePackDest(struct zip *z, int iniIndex, std::string *dest) { +bool GameManager::DetectTexturePackDest(struct zip *z, int iniIndex, Path &dest) { auto iz = GetI18NCategory("InstallZip"); struct zip_stat zstat; @@ -328,7 +338,7 @@ bool GameManager::DetectTexturePackDest(struct zip *z, int iniIndex, std::string std::string buffer; buffer.resize(zstat.size); zip_file *zf = zip_fopen_index(z, iniIndex, 0); - if (zip_fread(zf, &buffer[0], buffer.size()) != (ssize_t)zstat.size) { + if (zip_fread(zf, &buffer[0], buffer.size()) != (zip_int64_t)zstat.size) { SetInstallError(iz->T("Zip archive corrupt")); return false; } @@ -347,7 +357,7 @@ bool GameManager::DetectTexturePackDest(struct zip *z, int iniIndex, std::string if (games.size() > 1) { // Check for any supported game on their recent list and use that instead. for (const std::string &path : g_Config.recentIsos) { - std::string recentID = GetGameID(path); + std::string recentID = GetGameID(Path(path)); if (games.find(recentID) != games.end()) { gameID = recentID; break; @@ -355,8 +365,8 @@ bool GameManager::DetectTexturePackDest(struct zip *z, int iniIndex, std::string } } - std::string pspTextures = GetSysDirectory(DIRECTORY_TEXTURES); - *dest = pspTextures + gameID + "/"; + Path pspTextures = GetSysDirectory(DIRECTORY_TEXTURES); + dest = pspTextures / gameID; return true; } @@ -367,7 +377,7 @@ void GameManager::SetInstallError(const std::string &err) { InstallDone(); } -std::string GameManager::GetGameID(const std::string &path) const { +std::string GameManager::GetGameID(const Path &path) const { auto loader = ConstructFileLoader(path); std::string id; @@ -434,7 +444,7 @@ std::string GameManager::GetISOGameID(FileLoader *loader) const { return sfo.GetValueString("DISC_ID"); } -bool GameManager::ExtractFile(struct zip *z, int file_index, std::string outFilename, size_t *bytesCopied, size_t allBytes) { +bool GameManager::ExtractFile(struct zip *z, int file_index, const Path &outFilename, size_t *bytesCopied, size_t allBytes) { struct zip_stat zstat; zip_stat_index(z, file_index, 0, &zstat); size_t size = zstat.size; @@ -457,13 +467,13 @@ bool GameManager::ExtractFile(struct zip *z, int file_index, std::string outFile u8 *buffer = new u8[blockSize]; while (pos < size) { size_t readSize = std::min(blockSize, size - pos); - ssize_t retval = zip_fread(zf, buffer, readSize); + zip_int64_t retval = zip_fread(zf, buffer, readSize); if (retval < 0 || (size_t)retval < readSize) { ERROR_LOG(HLE, "Failed to read %d bytes from zip (%d) - archive corrupt?", (int)readSize, (int)retval); delete[] buffer; fclose(f); zip_fclose(zf); - File::Delete(outFilename.c_str()); + File::Delete(outFilename); return false; } size_t written = fwrite(buffer, 1, readSize, f); @@ -472,7 +482,7 @@ bool GameManager::ExtractFile(struct zip *z, int file_index, std::string outFile delete[] buffer; fclose(f); zip_fclose(zf); - File::Delete(outFilename.c_str()); + File::Delete(outFilename); return false; } pos += readSize; @@ -490,7 +500,7 @@ bool GameManager::ExtractFile(struct zip *z, int file_index, std::string outFile } } -bool GameManager::InstallMemstickGame(struct zip *z, const std::string &zipfile, const std::string &dest, const ZipFileInfo &info, bool allowRoot, bool deleteAfter) { +bool GameManager::InstallMemstickGame(struct zip *z, const Path &zipfile, const Path &dest, const ZipFileInfo &info, bool allowRoot, bool deleteAfter) { size_t allBytes = 0; size_t bytesCopied = 0; @@ -512,21 +522,26 @@ bool GameManager::InstallMemstickGame(struct zip *z, const std::string &zipfile, }; // Create all the directories first in one pass - std::set createdDirs; + std::set createdDirs; for (int i = 0; i < info.numFiles; i++) { const char *fn = zip_get_name(z, i, 0); std::string zippedName = fn; if (zippedName.length() < (size_t)info.stripChars) { continue; } - std::string outFilename = dest + zippedName.substr(info.stripChars); - bool isDir = *outFilename.rbegin() == '/'; - if (!isDir && outFilename.find("/") != std::string::npos) { - outFilename = outFilename.substr(0, outFilename.rfind('/')); + Path outFilename = dest / zippedName.substr(info.stripChars); + + bool isDir = zippedName.empty() || zippedName.back() == '/'; + if (!isDir && zippedName.find("/") != std::string::npos) { + outFilename = dest / zippedName.substr(0, zippedName.rfind('/')); + } else if (!isDir) { + outFilename = dest; } - if (createdDirs.find(outFilename) == createdDirs.end()) { - File::CreateFullPath(outFilename.c_str()); - createdDirs.insert(outFilename); + + Path outPath(outFilename); + if (createdDirs.find(outPath) == createdDirs.end()) { + File::CreateFullPath(outPath); + createdDirs.insert(outPath); } if (!isDir && fileAllowed(fn)) { struct zip_stat zstat; @@ -537,15 +552,16 @@ bool GameManager::InstallMemstickGame(struct zip *z, const std::string &zipfile, } // Now, loop through again in a second pass, writing files. - std::vector createdFiles; + std::vector createdFiles; for (int i = 0; i < info.numFiles; i++) { const char *fn = zip_get_name(z, i, 0); // Note that we do NOT write files that are not in a directory, to avoid random // README files etc. (unless allowRoot is true.) if (fileAllowed(fn) && strlen(fn) > (size_t)info.stripChars) { + std::string zippedName = fn; fn += info.stripChars; - std::string outFilename = dest + fn; - bool isDir = *outFilename.rbegin() == '/'; + Path outFilename = dest / fn; + bool isDir = zippedName.empty() || zippedName.back() == '/'; if (isDir) continue; @@ -556,7 +572,7 @@ bool GameManager::InstallMemstickGame(struct zip *z, const std::string &zipfile, } } } - INFO_LOG(HLE, "Extracted %i files (%i bytes / %i).", info.numFiles, (int)bytesCopied, (int)allBytes); + INFO_LOG(HLE, "Extracted %d files from zip (%d bytes / %d).", info.numFiles, (int)bytesCopied, (int)allBytes); zip_close(z); z = nullptr; @@ -564,7 +580,7 @@ bool GameManager::InstallMemstickGame(struct zip *z, const std::string &zipfile, installInProgress_ = false; installError_ = ""; if (deleteAfter) { - File::Delete(zipfile.c_str()); + File::Delete(zipfile); } InstallDone(); return true; @@ -574,16 +590,16 @@ bail: zip_close(z); // We don't delete the original in this case. Try to delete the files we created so far. for (size_t i = 0; i < createdFiles.size(); i++) { - File::Delete(createdFiles[i].c_str()); + File::Delete(createdFiles[i]); } - for (auto iter = createdDirs.begin(); iter != createdDirs.end(); ++iter) { - File::DeleteDir(iter->c_str()); + for (auto const &iter : createdDirs) { + File::DeleteDir(iter); } SetInstallError(sy->T("Storage full")); return false; } -bool GameManager::InstallZippedISO(struct zip *z, int isoFileIndex, std::string zipfile, bool deleteAfter) { +bool GameManager::InstallZippedISO(struct zip *z, int isoFileIndex, const Path &zipfile, bool deleteAfter) { // Let's place the output file in the currently selected Games directory. std::string fn = zip_get_name(z, isoFileIndex, 0); @@ -599,14 +615,14 @@ bool GameManager::InstallZippedISO(struct zip *z, int isoFileIndex, std::string allBytes += zstat.size; } - std::string outputISOFilename = g_Config.currentDirectory + "/" + fn.substr(nameOffset); + Path outputISOFilename = Path(g_Config.currentDirectory) / fn.substr(nameOffset); size_t bytesCopied = 0; if (ExtractFile(z, isoFileIndex, outputISOFilename, &bytesCopied, allBytes)) { INFO_LOG(IO, "Successfully extracted ISO file to '%s'", outputISOFilename.c_str()); } zip_close(z); if (deleteAfter) { - File::Delete(zipfile.c_str()); + File::Delete(zipfile); } z = 0; @@ -617,7 +633,7 @@ bool GameManager::InstallZippedISO(struct zip *z, int isoFileIndex, std::string return true; } -bool GameManager::InstallGameOnThread(std::string url, std::string fileName, bool deleteAfter) { +bool GameManager::InstallGameOnThread(const Path &url, const Path &fileName, bool deleteAfter) { if (installInProgress_) { return false; } @@ -625,12 +641,12 @@ bool GameManager::InstallGameOnThread(std::string url, std::string fileName, boo return true; } -bool GameManager::InstallRawISO(const std::string &file, const std::string &originalName, bool deleteAfter) { - std::string destPath = g_Config.currentDirectory + "/" + originalName; +bool GameManager::InstallRawISO(const Path &file, const std::string &originalName, bool deleteAfter) { + Path destPath = Path(g_Config.currentDirectory) / originalName; // TODO: To save disk space, we should probably attempt a move first. if (File::Copy(file, destPath)) { if (deleteAfter) { - File::Delete(file.c_str()); + File::Delete(file); } } installProgress_ = 1.0f; diff --git a/Core/Util/GameManager.h b/Core/Util/GameManager.h index 63460f66d7..4f8826bc88 100644 --- a/Core/Util/GameManager.h +++ b/Core/Util/GameManager.h @@ -23,7 +23,9 @@ #pragma once #include + #include "Common/Net/HTTPClient.h" +#include "Common/File/Path.h" enum class GameManagerState { IDLE, @@ -43,11 +45,14 @@ public: // This starts off a background process. bool DownloadAndInstall(std::string storeZipUrl); + bool IsDownloading(std::string storeZipUrl); bool Uninstall(std::string name); // Cancels the download in progress, if any. bool CancelDownload(); + float DownloadSpeedKBps(); + // Call from time to time to check on completed downloads from the // main UI thread. void Update(); @@ -68,20 +73,20 @@ public: } // Only returns false if there's already an installation in progress. - bool InstallGameOnThread(std::string url, std::string tempFileName, bool deleteAfter); + bool InstallGameOnThread(const Path &url, const Path &tempFileName, bool deleteAfter); private: - bool InstallGame(const std::string &url, const std::string &tempFileName, bool deleteAfter); - bool InstallMemstickGame(struct zip *z, const std::string &zipFile, const std::string &pspGame, const ZipFileInfo &info, bool allowRoot, bool deleteAfter); - bool InstallZippedISO(struct zip *z, int isoFileIndex, std::string zipfile, bool deleteAfter); - bool InstallRawISO(const std::string &zipFile, const std::string &originalName, bool deleteAfter); + bool InstallGame(Path url, Path tempFileName, bool deleteAfter); + bool InstallMemstickGame(struct zip *z, const Path &zipFile, const Path &dest, const ZipFileInfo &info, bool allowRoot, bool deleteAfter); + bool InstallZippedISO(struct zip *z, int isoFileIndex, const Path &zipfile, bool deleteAfter); + bool InstallRawISO(const Path &zipFile, const std::string &originalName, bool deleteAfter); void InstallDone(); - bool ExtractFile(struct zip *z, int file_index, std::string outFilename, size_t *bytesCopied, size_t allBytes); - bool DetectTexturePackDest(struct zip *z, int iniIndex, std::string *dest); + bool ExtractFile(struct zip *z, int file_index, const Path &outFilename, size_t *bytesCopied, size_t allBytes); + bool DetectTexturePackDest(struct zip *z, int iniIndex, Path &dest); void SetInstallError(const std::string &err); - std::string GetTempFilename() const; - std::string GetGameID(const std::string &path) const; + Path GetTempFilename() const; + std::string GetGameID(const Path &path) const; std::string GetPBPGameID(FileLoader *loader) const; std::string GetISOGameID(FileLoader *loader) const; std::shared_ptr curDownload_; @@ -110,4 +115,4 @@ struct ZipFileInfo { }; ZipFileContents DetectZipFileContents(struct zip *z, ZipFileInfo *info); -ZipFileContents DetectZipFileContents(std::string fileName, ZipFileInfo *info); +ZipFileContents DetectZipFileContents(const Path &fileName, ZipFileInfo *info); diff --git a/Core/Util/PPGeDraw.cpp b/Core/Util/PPGeDraw.cpp index ac0f04dee6..5b6bb9bc8e 100644 --- a/Core/Util/PPGeDraw.cpp +++ b/Core/Util/PPGeDraw.cpp @@ -954,13 +954,21 @@ static void PPGeDecimateTextImages(int age) { } } +static std::string PPGeSanitizeText(const std::string &text) { + return SanitizeUTF8(text); +} + void PPGeDrawText(const char *text, float x, float y, const PPGeStyle &style) { - if (!text || !strlen(text)) { + if (!text) { + return; + } + std::string str = PPGeSanitizeText(text); + if (str.empty()) { return; } if (HasTextDrawer()) { - PPGeTextDrawerImage im = PPGeGetTextImage(text, style, 480.0f - x, false); + PPGeTextDrawerImage im = PPGeGetTextImage(str.c_str(), style, 480.0f - x, false); if (im.ptr) { PPGeDrawTextImage(im, x, y, style); return; @@ -1001,7 +1009,7 @@ static std::string CropLinesToCount(const std::string &s, int numLines) { } void PPGeDrawTextWrapped(const char *text, float x, float y, float wrapWidth, float wrapHeight, const PPGeStyle &style) { - std::string s = text; + std::string s = PPGeSanitizeText(text); if (wrapHeight != 0.0f) { s = StripTrailingWhite(s); } diff --git a/Core/Util/PortManager.cpp b/Core/Util/PortManager.cpp index 06e1968716..0bd27db7db 100644 --- a/Core/Util/PortManager.cpp +++ b/Core/Util/PortManager.cpp @@ -181,8 +181,10 @@ bool PortManager::Initialize(const unsigned int timeout) { } ERROR_LOG(SCENET, "PortManager - upnpDiscover failed (error: %i) or No UPnP device detected", error); - auto n = GetI18NCategory("Networking"); - host->NotifyUserMessage(n->T("Unable to find UPnP device"), 2.0f, 0x0000ff); + if (g_Config.bEnableUPnP) { + auto n = GetI18NCategory("Networking"); + host->NotifyUserMessage(n->T("Unable to find UPnP device"), 2.0f, 0x0000ff); + } m_InitState = UPNP_INITSTATE_NONE; #endif // WITH_UPNP return false; @@ -197,13 +199,18 @@ bool PortManager::Add(const char* protocol, unsigned short port, unsigned short char port_str[16]; char intport_str[16]; int r; + auto n = GetI18NCategory("Networking"); if (intport == 0) intport = port; INFO_LOG(SCENET, "PortManager::Add(%s, %d, %d)", protocol, port, intport); if (urls == NULL || urls->controlURL == NULL || urls->controlURL[0] == '\0') { - if (g_Config.bEnableUPnP) WARN_LOG(SCENET, "PortManager::Add - the init was not done !"); + if (g_Config.bEnableUPnP) { + WARN_LOG(SCENET, "PortManager::Add - the init was not done !"); + host->NotifyUserMessage(n->T("UPnP need to be reinitialized"), 2.0f, 0x0000ff); + } + Terminate(); return false; } sprintf(port_str, "%d", port); @@ -229,8 +236,9 @@ bool PortManager::Add(const char* protocol, unsigned short port, unsigned short { ERROR_LOG(SCENET, "PortManager - AddPortMapping failed (error: %i)", r); if (r == UPNPCOMMAND_HTTP_ERROR) { - auto n = GetI18NCategory("Networking"); - host->NotifyUserMessage(n->T("UPnP need to be reinitialized"), 2.0f, 0x0000ff); + if (g_Config.bEnableUPnP) { + host->NotifyUserMessage(n->T("UPnP need to be reinitialized"), 2.0f, 0x0000ff); + } Terminate(); // Most of the time errors occurred because the router is no longer reachable (ie. changed networks) so we should invalidate the state to prevent further lags due to timeouts return false; } @@ -248,11 +256,16 @@ bool PortManager::Add(const char* protocol, unsigned short port, unsigned short bool PortManager::Remove(const char* protocol, unsigned short port) { #ifdef WITH_UPNP char port_str[16]; + auto n = GetI18NCategory("Networking"); INFO_LOG(SCENET, "PortManager::Remove(%s, %d)", protocol, port); if (urls == NULL || urls->controlURL == NULL || urls->controlURL[0] == '\0') { - if (g_Config.bEnableUPnP) WARN_LOG(SCENET, "PortManager::Remove - the init was not done !"); + if (g_Config.bEnableUPnP) { + WARN_LOG(SCENET, "PortManager::Remove - the init was not done !"); + host->NotifyUserMessage(n->T("UPnP need to be reinitialized"), 2.0f, 0x0000ff); + } + Terminate(); return false; } sprintf(port_str, "%d", port); @@ -261,8 +274,9 @@ bool PortManager::Remove(const char* protocol, unsigned short port) { { ERROR_LOG(SCENET, "PortManager - DeletePortMapping failed (error: %i)", r); if (r == UPNPCOMMAND_HTTP_ERROR) { - auto n = GetI18NCategory("Networking"); - host->NotifyUserMessage(n->T("UPnP need to be reinitialized"), 2.0f, 0x0000ff); + if (g_Config.bEnableUPnP) { + host->NotifyUserMessage(n->T("UPnP need to be reinitialized"), 2.0f, 0x0000ff); + } Terminate(); // Most of the time errors occurred because the router is no longer reachable (ie. changed networks) so we should invalidate the state to prevent further lags due to timeouts return false; } @@ -439,7 +453,7 @@ bool PortManager::RefreshPortList() { int upnpService(const unsigned int timeout) { - setCurrentThreadName("UPnPService"); + SetCurrentThreadName("UPnPService"); INFO_LOG(SCENET, "UPnPService: Begin of UPnPService Thread"); // Service Loop diff --git a/Core/WaveFile.cpp b/Core/WaveFile.cpp index 1f4f4bdef4..83d45077b3 100644 --- a/Core/WaveFile.cpp +++ b/Core/WaveFile.cpp @@ -17,7 +17,7 @@ WaveFileWriter::~WaveFileWriter() Stop(); } -bool WaveFileWriter::Start(const std::string& filename, unsigned int HLESampleRate) +bool WaveFileWriter::Start(const Path &filename, unsigned int HLESampleRate) { // Check if the file is already open if (file) { diff --git a/Core/WaveFile.h b/Core/WaveFile.h index 8198cecf50..725e64fff2 100644 --- a/Core/WaveFile.h +++ b/Core/WaveFile.h @@ -24,7 +24,7 @@ public: WaveFileWriter(); ~WaveFileWriter(); - bool Start(const std::string& filename, unsigned int HLESampleRate); + bool Start(const Path& filename, unsigned int HLESampleRate); void Stop(); void SetSkipSilence(bool skip) { skip_silence = skip; } diff --git a/Core/WebServer.cpp b/Core/WebServer.cpp index ade556a563..b3b1b9b333 100644 --- a/Core/WebServer.cpp +++ b/Core/WebServer.cpp @@ -63,7 +63,10 @@ static ServerStatus RetrieveStatus() { static bool RegisterServer(int port) { bool success = false; http::Client http; - Buffer theVoid; + http::RequestProgress progress; + Buffer theVoid = Buffer::Void(); + + http.SetUserAgent(StringFromFormat("PPSSPP/%s", PPSSPP_GIT_VERSION)); char resource4[1024] = {}; if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT, net::DNSType::IPV4)) { @@ -71,7 +74,7 @@ static bool RegisterServer(int port) { std::string ip = fd_util::GetLocalIP(http.sock()); snprintf(resource4, sizeof(resource4) - 1, "/match/update?local=%s&port=%d", ip.c_str(), port); - if (http.GET(resource4, &theVoid) > 0) + if (http.GET(resource4, &theVoid, &progress) > 0) success = true; theVoid.Skip(theVoid.size()); http.Disconnect(); @@ -84,7 +87,7 @@ static bool RegisterServer(int port) { // We register both IPv4 and IPv6 in case the other client is using a different one. if (resource4[0] != 0 && http.Connect(timeout)) { - if (http.GET(resource4, &theVoid) > 0) + if (http.GET(resource4, &theVoid, &progress) > 0) success = true; theVoid.Skip(theVoid.size()); http.Disconnect(); @@ -96,7 +99,7 @@ static bool RegisterServer(int port) { std::string ip = fd_util::GetLocalIP(http.sock()); snprintf(resource6, sizeof(resource6) - 1, "/match/update?local=%s&port=%d", ip.c_str(), port); - if (http.GET(resource6, &theVoid) > 0) + if (http.GET(resource6, &theVoid, &progress) > 0) success = true; theVoid.Skip(theVoid.size()); http.Disconnect(); @@ -145,17 +148,17 @@ static std::string RemotePathForRecent(const std::string &filename) { return ""; } -static std::string LocalFromRemotePath(const std::string &path) { +static Path LocalFromRemotePath(const std::string &path) { for (const std::string &filename : g_Config.recentIsos) { std::string basename = RemotePathForRecent(filename); if (basename == path) { - return filename; + return Path(filename); } } - return ""; + return Path(); } -static void DiscHandler(const http::Request &request, const std::string &filename) { +static void DiscHandler(const http::Request &request, const Path &filename) { s64 sz = File::GetFileSize(filename); std::string range; @@ -226,7 +229,7 @@ static void HandleListing(const http::Request &request) { static void HandleFallback(const http::Request &request) { if (serverFlags & (int)WebServerFlags::DISCS) { - std::string filename = LocalFromRemotePath(request.resource()); + Path filename = LocalFromRemotePath(request.resource()); if (!filename.empty()) { DiscHandler(request, filename); return; @@ -247,9 +250,9 @@ static void ForwardDebuggerRequest(const http::Request &request) { } static void ExecuteWebServer() { - setCurrentThreadName("HTTPServer"); + SetCurrentThreadName("HTTPServer"); - auto http = new http::Server(new threading::NewThreadExecutor()); + auto http = new http::Server(new NewThreadExecutor()); http->RegisterHandler("/", &HandleListing); // This lists all the (current) recent ISOs. http->SetFallbackHandler(&HandleFallback); diff --git a/GPU/Common/DepalettizeShaderCommon.cpp b/GPU/Common/DepalettizeShaderCommon.cpp index 11236f1f9a..11c598a7f0 100644 --- a/GPU/Common/DepalettizeShaderCommon.cpp +++ b/GPU/Common/DepalettizeShaderCommon.cpp @@ -292,7 +292,7 @@ void GenerateDepalShaderFloat(char *buffer, GEBufferFormat pixelFormat, ShaderLa WRITE(p, "precision mediump float;\n"); } else { WRITE(p, "#version %d\n", gl_extensions.GLSLVersion()); - if (gl_extensions.VersionGEThan(3, 3)) { + if (gl_extensions.VersionGEThan(3, 0, 0)) { WRITE(p, "#define gl_FragColor fragColor0\n"); WRITE(p, "out vec4 fragColor0;\n"); } diff --git a/GPU/Common/DrawEngineCommon.cpp b/GPU/Common/DrawEngineCommon.cpp index 2393431c76..595cc31297 100644 --- a/GPU/Common/DrawEngineCommon.cpp +++ b/GPU/Common/DrawEngineCommon.cpp @@ -17,8 +17,8 @@ #include +#include "Common/Data/Convert/ColorConv.h" #include "Common/Profiler/Profiler.h" -#include "Common/ColorConv.h" #include "Core/Config.h" #include "GPU/Common/DrawEngineCommon.h" #include "GPU/Common/SplineCommon.h" @@ -36,8 +36,6 @@ DrawEngineCommon::DrawEngineCommon() : decoderMap_(16) { decJitCache_ = new VertexDecoderJitCache(); transformed = (TransformedVertex *)AllocateMemoryPages(TRANSFORMED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); transformedExpanded = (TransformedVertex *)AllocateMemoryPages(3 * TRANSFORMED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); - useHWTransform_ = g_Config.bHardwareTransform; - useHWTessellation_ = UpdateUseHWTessellation(g_Config.bHardwareTessellation); } DrawEngineCommon::~DrawEngineCommon() { @@ -50,6 +48,11 @@ DrawEngineCommon::~DrawEngineCommon() { ClearSplineBezierWeights(); } +void DrawEngineCommon::Init() { + useHWTransform_ = g_Config.bHardwareTransform; + useHWTessellation_ = UpdateUseHWTessellation(g_Config.bHardwareTessellation); +} + VertexDecoder *DrawEngineCommon::GetVertexDecoder(u32 vtype) { VertexDecoder *dec = decoderMap_.Get(vtype); if (dec) diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index 7a063a2d26..1a2bfd7b17 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -56,6 +56,8 @@ public: DrawEngineCommon(); virtual ~DrawEngineCommon(); + void Init(); + bool GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices); static u32 NormalizeVertices(u8 *outPtr, u8 *bufPtr, const u8 *inPtr, VertexDecoder *dec, int lowerBound, int upperBound, u32 vertType); diff --git a/GPU/Common/FragmentShaderGenerator.cpp b/GPU/Common/FragmentShaderGenerator.cpp index 2d8c52b594..3fc0e16ffc 100644 --- a/GPU/Common/FragmentShaderGenerator.cpp +++ b/GPU/Common/FragmentShaderGenerator.cpp @@ -589,8 +589,8 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu WRITE(p, " uint depalOffset = ((u_depal_mask_shift_off_fmt >> 16) & 0xFFU) << 4;\n"); WRITE(p, " uint depalFmt = (u_depal_mask_shift_off_fmt >> 24) & 0x3U;\n"); WRITE(p, " uvec4 col; uint index0; uint index1; uint index2; uint index3;\n"); - WRITE(p, " switch (depalFmt) {\n"); // We might want to include fmt in the shader ID if this is a performance issue. - WRITE(p, " case 0U:\n"); // 565 + WRITE(p, " switch (int(depalFmt)) {\n"); // We might want to include fmt in the shader ID if this is a performance issue. + WRITE(p, " case 0:\n"); // 565 WRITE(p, " col = uvec4(t.rgb * vec3(31.99, 63.99, 31.99), 0);\n"); WRITE(p, " index0 = (col.b << 11) | (col.g << 5) | (col.r);\n"); WRITE(p, " if (bilinear) {\n"); @@ -602,7 +602,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu WRITE(p, " index3 = (col.b << 11) | (col.g << 5) | (col.r);\n"); WRITE(p, " }\n"); WRITE(p, " break;\n"); - WRITE(p, " case 1U:\n"); // 5551 + WRITE(p, " case 1:\n"); // 5551 WRITE(p, " col = uvec4(t.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n"); WRITE(p, " index0 = (col.a << 15) | (col.b << 10) | (col.g << 5) | (col.r);\n"); WRITE(p, " if (bilinear) {\n"); @@ -614,7 +614,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu WRITE(p, " index3 = (col.a << 15) | (col.b << 10) | (col.g << 5) | (col.r);\n"); WRITE(p, " }\n"); WRITE(p, " break;\n"); - WRITE(p, " case 2U:\n"); // 4444 + WRITE(p, " case 2:\n"); // 4444 WRITE(p, " col = uvec4(t.rgba * 15.99);\n"); WRITE(p, " index0 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n"); WRITE(p, " if (bilinear) {\n"); @@ -626,7 +626,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu WRITE(p, " index3 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n"); WRITE(p, " }\n"); WRITE(p, " break;\n"); - WRITE(p, " case 3U:\n"); // 8888 + WRITE(p, " case 3:\n"); // 8888 WRITE(p, " col = uvec4(t.rgba * 255.99);\n"); WRITE(p, " index0 = (col.a << 24) | (col.b << 16) | (col.g << 8) | (col.r);\n"); WRITE(p, " if (bilinear) {\n"); diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 436a5d719a..3554eca166 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -21,8 +21,8 @@ #include "Common/GPU/thin3d.h" #include "Common/GPU/OpenGL/GLFeatures.h" +#include "Common/Data/Convert/ColorConv.h" #include "Common/Data/Text/I18n.h" -#include "Common/ColorConv.h" #include "Common/Common.h" #include "Core/Config.h" #include "Core/ConfigValues.h" @@ -2181,7 +2181,9 @@ void FramebufferManagerCommon::PackFramebufferSync_(VirtualFramebuffer *vfb, int if (destPtr) { draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, x, y, w, h, destFormat, destPtr, vfb->fb_stride, "PackFramebufferSync_"); - NotifyMemInfo(MemBlockFlags::WRITE, fb_address + dstByteOffset, dstSize, "FramebufferPack"); + char tag[128]; + size_t len = snprintf(tag, sizeof(tag), "FramebufferPack/%08x_%08x_%dx%d_%s", vfb->fb_address, vfb->z_address, w, h, GeBufferFormatToString(vfb->format)); + NotifyMemInfo(MemBlockFlags::WRITE, fb_address + dstByteOffset, dstSize, tag, len); } else { ERROR_LOG(G3D, "PackFramebufferSync_: Tried to readback to bad address %08x (stride = %d)", fb_address + dstByteOffset, vfb->fb_stride); } diff --git a/GPU/Common/PostShader.cpp b/GPU/Common/PostShader.cpp index e415aa9226..c14754ff32 100644 --- a/GPU/Common/PostShader.cpp +++ b/GPU/Common/PostShader.cpp @@ -22,6 +22,7 @@ #include #include +#include "Common/Log.h" #include "Common/Data/Format/IniFile.h" #include "Common/File/FileUtil.h" #include "Common/File/DirListing.h" @@ -38,7 +39,7 @@ static std::vector textureShaderInfo; // Scans the directories for shader ini files and collects info about all the shaders found. -void LoadPostShaderInfo(const std::vector &directories) { +void LoadPostShaderInfo(const std::vector &directories) { std::vector notVisible; shaderInfo.clear(); @@ -78,8 +79,8 @@ void LoadPostShaderInfo(const std::vector &directories) { }; for (size_t d = 0; d < directories.size(); d++) { - std::vector fileInfo; - getFilesInDir(directories[d].c_str(), &fileInfo, "ini:"); + std::vector fileInfo; + File::GetFilesInDir(directories[d], &fileInfo, "ini:"); if (fileInfo.size() == 0) { VFSGetFileListing(directories[d].c_str(), &fileInfo, "ini:"); @@ -88,18 +89,19 @@ void LoadPostShaderInfo(const std::vector &directories) { for (size_t f = 0; f < fileInfo.size(); f++) { IniFile ini; bool success = false; - std::string name = fileInfo[f].fullName; - std::string path = directories[d]; + Path name = fileInfo[f].fullName; + Path path = directories[d]; // Hack around Android VFS path bug. really need to redesign this. - if (name.substr(0, 7) == "assets/") - name = name.substr(7); - if (path.substr(0, 7) == "assets/") - path = path.substr(7); + if (name.ToString().substr(0, 7) == "assets/") + name = Path(name.ToString().substr(7)); + if (path.ToString().substr(0, 7) == "assets/") + path = Path(path.ToString().substr(7)); - if (ini.LoadFromVFS(name) || ini.Load(fileInfo[f].fullName)) { + if (ini.LoadFromVFS(name.ToString()) || ini.Load(fileInfo[f].fullName)) { success = true; // vsh load. meh. } + if (!success) continue; @@ -118,14 +120,17 @@ void LoadPostShaderInfo(const std::vector &directories) { section.Get("Parent", &info.parent, ""); section.Get("Visible", &info.visible, true); section.Get("Fragment", &temp, ""); - info.fragmentShaderFile = path + "/" + temp; + info.fragmentShaderFile = path / temp; section.Get("Vertex", &temp, ""); - info.vertexShaderFile = path + "/" + temp; + info.vertexShaderFile = path / temp; section.Get("OutputResolution", &info.outputResolution, false); section.Get("Upscaling", &info.isUpscalingFilter, false); section.Get("SSAA", &info.SSAAFilterLevel, 0); section.Get("60fps", &info.requires60fps, false); + if (info.parent == "Off") + info.parent = ""; + for (size_t i = 0; i < ARRAY_SIZE(info.settings); ++i) { auto &setting = info.settings[i]; section.Get(StringFromFormat("SettingName%d", i + 1).c_str(), &setting.name, ""); @@ -162,7 +167,7 @@ void LoadPostShaderInfo(const std::vector &directories) { section.Get("Name", &info.name, section.name().c_str()); section.Get("Compute", &temp, ""); section.Get("MaxScale", &info.maxScale, 255); - info.computeShaderFile = path + "/" + temp; + info.computeShaderFile = path / temp; appendTextureShader(info); } @@ -178,9 +183,9 @@ void LoadPostShaderInfo(const std::vector &directories) { // Scans the directories for shader ini files and collects info about all the shaders found. void ReloadAllPostShaderInfo() { - std::vector directories; - directories.push_back("shaders"); - directories.push_back(g_Config.memStickDirectory + "PSP/shaders"); + std::vector directories; + directories.push_back(Path("shaders")); // For VFS + directories.push_back(g_Config.memStickDirectory / "PSP" / "shaders"); LoadPostShaderInfo(directories); } @@ -198,7 +203,7 @@ std::vector GetPostShaderChain(const std::string &name) { while (shaderInfo) { backwards.push_back(shaderInfo); - if (!shaderInfo->parent.empty() && shaderInfo->parent != "Off") { + if (!shaderInfo->parent.empty()) { shaderInfo = GetPostShaderInfo(shaderInfo->parent); } else { shaderInfo = nullptr; @@ -219,8 +224,6 @@ std::vector GetPostShaderChain(const std::string &name) { std::vector GetFullPostShadersChain(const std::vector &names) { std::vector fullChain; for (auto shaderName : names) { - if (shaderName == "Off") - break; auto shaderChain = GetPostShaderChain(shaderName); fullChain.insert(fullChain.end(), shaderChain.begin(), shaderChain.end()); } diff --git a/GPU/Common/PostShader.h b/GPU/Common/PostShader.h index b5c1f562f9..76c2e8ce6f 100644 --- a/GPU/Common/PostShader.h +++ b/GPU/Common/PostShader.h @@ -25,13 +25,13 @@ #include "Common/Data/Format/IniFile.h" struct ShaderInfo { - std::string iniFile; // which ini file was this definition in? So we can write settings back later + Path iniFile; // which ini file was this definition in? So we can write settings back later std::string section; // ini file section. This is saved. std::string name; // Fancy display name. std::string parent; // Parent shader ini section name. - std::string fragmentShaderFile; - std::string vertexShaderFile; + Path fragmentShaderFile; + Path vertexShaderFile; // Show this shader in lists (i.e. not just for chaining.) bool visible; @@ -65,11 +65,11 @@ struct ShaderInfo { }; struct TextureShaderInfo { - std::string iniFile; + Path iniFile; std::string section; std::string name; - std::string computeShaderFile; + Path computeShaderFile; int maxScale; bool operator == (const std::string &other) { diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index 2122980b66..59590e8401 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -197,11 +197,13 @@ void PresentationCommon::CalculatePostShaderUniforms(int bufferWidth, int buffer uniforms->setting[3] = g_Config.mPostShaderSetting[shaderInfo->section + "SettingValue4"]; } -static std::string ReadShaderSrc(const std::string &filename) { +static std::string ReadShaderSrc(const Path &filename) { size_t sz = 0; + // TODO(scoped): VFS paths not handled well. char *data = (char *)VFSReadFile(filename.c_str(), &sz); - if (!data) + if (!data) { return ""; + } std::string src(data, sz); delete[] data; @@ -211,7 +213,7 @@ static std::string ReadShaderSrc(const std::string &filename) { // Note: called on resize and settings changes. bool PresentationCommon::UpdatePostShader() { std::vector shaderInfo; - if (!g_Config.vPostShaderNames.empty() && g_Config.vPostShaderNames[0] != "Off") { + if (!g_Config.vPostShaderNames.empty()) { ReloadAllPostShaderInfo(); shaderInfo = GetFullPostShadersChain(g_Config.vPostShaderNames); } @@ -735,7 +737,7 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u void PresentationCommon::CalculateRenderResolution(int *width, int *height, int *scaleFactor, bool *upscaling, bool *ssaa) { // Check if postprocessing shader is doing upscaling as it requires native resolution std::vector shaderInfo; - if (!g_Config.vPostShaderNames.empty() && g_Config.vPostShaderNames[0] != "Off") { + if (!g_Config.vPostShaderNames.empty()) { ReloadAllPostShaderInfo(); shaderInfo = GetFullPostShadersChain(g_Config.vPostShaderNames); } diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 83b4ff14cf..e7f26a6e14 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -18,8 +18,8 @@ #include #include "ppsspp_config.h" +#include "Common/Data/Convert/ColorConv.h" #include "Common/Profiler/Profiler.h" -#include "Common/ColorConv.h" #include "Common/MemoryUtil.h" #include "Common/StringUtils.h" #include "Core/Config.h" diff --git a/GPU/Common/TextureDecoder.cpp b/GPU/Common/TextureDecoder.cpp index 2e10ac1f36..750fc3f9b7 100644 --- a/GPU/Common/TextureDecoder.cpp +++ b/GPU/Common/TextureDecoder.cpp @@ -17,8 +17,8 @@ #include "ppsspp_config.h" #include "ext/xxhash.h" +#include "Common/Data/Convert/ColorConv.h" #include "Common/CPUDetect.h" -#include "Common/ColorConv.h" #include "GPU/GPU.h" #include "GPU/GPUState.h" @@ -26,8 +26,6 @@ // NEON is in a separate file so that it can be compiled with a runtime check. #include "GPU/Common/TextureDecoderNEON.h" -// TODO: Move some common things into here. - #ifdef _M_SSE #include #if _M_SSE >= 0x401 diff --git a/GPU/Common/TransformCommon.cpp b/GPU/Common/TransformCommon.cpp index 85031c8e0e..5d74f30dd3 100644 --- a/GPU/Common/TransformCommon.cpp +++ b/GPU/Common/TransformCommon.cpp @@ -51,15 +51,9 @@ Lighter::Lighter(int vertType) { lconv[l] = getFloat24(gstate.lconv[l]); int i = l * 3; if (gstate.isLightChanEnabled(l)) { - lpos[i] = getFloat24(gstate.lpos[i]); - lpos[i + 1] = getFloat24(gstate.lpos[i + 1]); - lpos[i + 2] = getFloat24(gstate.lpos[i + 2]); - ldir[i] = getFloat24(gstate.ldir[i]); - ldir[i + 1] = getFloat24(gstate.ldir[i + 1]); - ldir[i + 2] = getFloat24(gstate.ldir[i + 2]); - latt[i] = getFloat24(gstate.latt[i]); - latt[i + 1] = getFloat24(gstate.latt[i + 1]); - latt[i + 2] = getFloat24(gstate.latt[i + 2]); + lpos[l] = Vec3fFromGE(&gstate.lpos[i]); + ldir[l] = Vec3fFromGE(&gstate.ldir[i]); + latt[l] = Vec3fFromGE(&gstate.latt[i]); for (int t = 0; t < 3; t++) { u32 data = gstate.lcolor[l * 3 + t] & 0xFFFFFF; float r = (float)(data & 0xff) * (1.0f / 255.0f); @@ -108,9 +102,9 @@ void Lighter::Light(float colorOut0[4], float colorOut1[4], const float colorIn[ Vec3f lightDir(0, 0, 0); if (type == GE_LIGHTTYPE_DIRECTIONAL) - toLight = Vec3Packedf(&lpos[l * 3]); // lightdir is for spotlights + toLight = lpos[l]; // lightdir is for spotlights else - toLight = Vec3Packedf(&lpos[l * 3]) - pos; + toLight = lpos[l] - pos; bool doSpecular = gstate.isUsingSpecularLight(l); bool poweredDiffuse = gstate.isUsingPoweredDiffuseLight(l); @@ -136,14 +130,14 @@ void Lighter::Light(float colorOut0[4], float colorOut1[4], const float colorIn[ lightScale = 1.0f; break; case GE_LIGHTTYPE_POINT: - lightScale = clamp(1.0f / (latt[l * 3] + latt[l * 3 + 1] * distanceToLight + latt[l * 3 + 2] * distanceToLight*distanceToLight), 0.0f, 1.0f); + lightScale = clamp(1.0f / (latt[l].x + latt[l].y * distanceToLight + latt[l].z * distanceToLight*distanceToLight), 0.0f, 1.0f); break; case GE_LIGHTTYPE_SPOT: case GE_LIGHTTYPE_UNKNOWN: - lightDir = Vec3Packedf(&ldir[l * 3]); + lightDir = ldir[l]; angle = Dot(toLight.NormalizedOr001(cpu_info.bSSE4_1), lightDir.NormalizedOr001(cpu_info.bSSE4_1)); if (angle >= lcutoff[l]) - lightScale = clamp(1.0f / (latt[l * 3] + latt[l * 3 + 1] * distanceToLight + latt[l * 3 + 2] * distanceToLight*distanceToLight), 0.0f, 1.0f) * powf(angle, lconv[l]); + lightScale = clamp(1.0f / (latt[l].x + latt[l].y * distanceToLight + latt[l].z * distanceToLight*distanceToLight), 0.0f, 1.0f) * powf(angle, lconv[l]); break; default: // ILLEGAL diff --git a/GPU/Common/TransformCommon.h b/GPU/Common/TransformCommon.h index 0a97975d26..f33a33c4c3 100644 --- a/GPU/Common/TransformCommon.h +++ b/GPU/Common/TransformCommon.h @@ -69,6 +69,13 @@ public: void Light(float colorOut0[4], float colorOut1[4], const float colorIn[4], const Vec3f &pos, const Vec3f &normal); private: + inline Vec3f Vec3fFromGE(const u32 *values) const { + float x = getFloat24(values[0]); + float y = getFloat24(values[1]); + float z = getFloat24(values[2]); + return Vec3f(x, y, z); + } + Color4 globalAmbient; Color4 materialEmissive; Color4 materialAmbient; @@ -80,11 +87,9 @@ private: int materialUpdate_; // Converted light parameters -public: - float lpos[12]; // Used by shade UV mapping -private: - float ldir[12]; - float latt[12]; + Vec3f lpos[4]; // Used by shade UV mapping + Vec3f ldir[4]; + Vec3f latt[4]; float lcutoff[4]; float lconv[4]; float lcolor[3][4][3]; diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index 920083577f..ecf247e530 100644 --- a/GPU/Common/VertexDecoderCommon.cpp +++ b/GPU/Common/VertexDecoderCommon.cpp @@ -20,10 +20,10 @@ #include "ppsspp_config.h" -#include "Common/Log.h" -#include "Common/CPUDetect.h" -#include "Common/ColorConv.h" #include "Common/Common.h" +#include "Common/CPUDetect.h" +#include "Common/Data/Convert/ColorConv.h" +#include "Common/Log.h" #include "Core/Config.h" #include "Core/ConfigValues.h" #include "Core/MemMap.h" diff --git a/GPU/Common/VertexDecoderCommon.h b/GPU/Common/VertexDecoderCommon.h index 9377b36119..2911e8e6fe 100644 --- a/GPU/Common/VertexDecoderCommon.h +++ b/GPU/Common/VertexDecoderCommon.h @@ -35,8 +35,6 @@ #include "Common/Arm64Emitter.h" #elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) #include "Common/x64Emitter.h" -#elif PPSSPP_ARCH(MIPS) -#include "Common/MipsEmitter.h" #else #include "Common/FakeEmitter.h" #endif @@ -601,18 +599,17 @@ public: // // that's it! - #if PPSSPP_ARCH(ARM) -class VertexDecoderJitCache : public ArmGen::ARMXCodeBlock { +#define VERTEXDECODER_JIT_BACKEND ArmGen::ARMXCodeBlock #elif PPSSPP_ARCH(ARM64) -class VertexDecoderJitCache : public Arm64Gen::ARM64CodeBlock { +#define VERTEXDECODER_JIT_BACKEND Arm64Gen::ARM64CodeBlock #elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) -class VertexDecoderJitCache : public Gen::XCodeBlock { -#elif PPSSPP_ARCH(MIPS) -class VertexDecoderJitCache : public MIPSGen::MIPSCodeBlock { -#else -class VertexDecoderJitCache : public FakeGen::FakeXCodeBlock { +#define VERTEXDECODER_JIT_BACKEND Gen::XCodeBlock #endif + + +#ifdef VERTEXDECODER_JIT_BACKEND +class VertexDecoderJitCache : public VERTEXDECODER_JIT_BACKEND { public: VertexDecoderJitCache(); @@ -705,3 +702,14 @@ private: Arm64Gen::ARM64FloatEmitter fp; #endif }; +#else +class VertexDecoderJitCache : public FakeGen::FakeXCodeBlock { +public: + VertexDecoderJitCache(); + + JittedVertexDecoder Compile(const VertexDecoder &dec, int32_t *jittedSize) { + return nullptr; + } + void Clear(); +}; +#endif diff --git a/GPU/Common/VertexDecoderFake.cpp b/GPU/Common/VertexDecoderFake.cpp deleted file mode 100644 index 709d0647e8..0000000000 --- a/GPU/Common/VertexDecoderFake.cpp +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright (c) 2013- PPSSPP Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0 or later versions. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official git repository and contact information can be found at -// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. - -#include "ppsspp_config.h" -#include "Common/CPUDetect.h" -#if !PPSSPP_ARCH(MIPS) -#include "Common/FakeEmitter.h" -#endif -#include "Core/Config.h" -#include "Core/Reporting.h" -#include "GPU/GPUState.h" -#include "GPU/Common/VertexDecoderCommon.h" - -static const float by128 = 1.0f / 128.0f; -static const float by16384 = 1.0f / 16384.0f; -static const float by32768 = 1.0f / 32768.0f; - -#if PPSSPP_ARCH(MIPS) -using namespace MIPSGen; -#else -using namespace FakeGen; -#endif - -static const JitLookup jitLookup[] = { - {&VertexDecoder::Step_WeightsU8, &VertexDecoderJitCache::Jit_WeightsU8}, - {&VertexDecoder::Step_WeightsU16, &VertexDecoderJitCache::Jit_WeightsU16}, - {&VertexDecoder::Step_WeightsFloat, &VertexDecoderJitCache::Jit_WeightsFloat}, - - {&VertexDecoder::Step_WeightsU8Skin, &VertexDecoderJitCache::Jit_WeightsU8Skin}, - {&VertexDecoder::Step_WeightsU16Skin, &VertexDecoderJitCache::Jit_WeightsU16Skin}, - {&VertexDecoder::Step_WeightsFloatSkin, &VertexDecoderJitCache::Jit_WeightsFloatSkin}, - - {&VertexDecoder::Step_TcU8ToFloat, &VertexDecoderJitCache::Jit_TcU8ToFloat}, - {&VertexDecoder::Step_TcU16ToFloat, &VertexDecoderJitCache::Jit_TcU16ToFloat}, - {&VertexDecoder::Step_TcFloat, &VertexDecoderJitCache::Jit_TcFloat}, - - {&VertexDecoder::Step_TcU8Prescale, &VertexDecoderJitCache::Jit_TcU8Prescale}, - {&VertexDecoder::Step_TcU16Prescale, &VertexDecoderJitCache::Jit_TcU16Prescale}, - {&VertexDecoder::Step_TcFloatPrescale, &VertexDecoderJitCache::Jit_TcFloatPrescale}, - - {&VertexDecoder::Step_TcU16ThroughToFloat, &VertexDecoderJitCache::Jit_TcU16ThroughToFloat}, - {&VertexDecoder::Step_TcFloatThrough, &VertexDecoderJitCache::Jit_TcFloatThrough}, - - {&VertexDecoder::Step_NormalS8, &VertexDecoderJitCache::Jit_NormalS8}, - {&VertexDecoder::Step_NormalS16, &VertexDecoderJitCache::Jit_NormalS16}, - {&VertexDecoder::Step_NormalFloat, &VertexDecoderJitCache::Jit_NormalFloat}, - - {&VertexDecoder::Step_NormalS8Skin, &VertexDecoderJitCache::Jit_NormalS8Skin}, - {&VertexDecoder::Step_NormalS16Skin, &VertexDecoderJitCache::Jit_NormalS16Skin}, - {&VertexDecoder::Step_NormalFloatSkin, &VertexDecoderJitCache::Jit_NormalFloatSkin}, - - {&VertexDecoder::Step_Color8888, &VertexDecoderJitCache::Jit_Color8888}, - {&VertexDecoder::Step_Color4444, &VertexDecoderJitCache::Jit_Color4444}, - {&VertexDecoder::Step_Color565, &VertexDecoderJitCache::Jit_Color565}, - {&VertexDecoder::Step_Color5551, &VertexDecoderJitCache::Jit_Color5551}, - - {&VertexDecoder::Step_PosS8Through, &VertexDecoderJitCache::Jit_PosS8Through}, - {&VertexDecoder::Step_PosS16Through, &VertexDecoderJitCache::Jit_PosS16Through}, - {&VertexDecoder::Step_PosFloatThrough, &VertexDecoderJitCache::Jit_PosFloat}, - - {&VertexDecoder::Step_PosS8, &VertexDecoderJitCache::Jit_PosS8}, - {&VertexDecoder::Step_PosS16, &VertexDecoderJitCache::Jit_PosS16}, - {&VertexDecoder::Step_PosFloat, &VertexDecoderJitCache::Jit_PosFloat}, - - {&VertexDecoder::Step_PosS8Skin, &VertexDecoderJitCache::Jit_PosS8Skin}, - {&VertexDecoder::Step_PosS16Skin, &VertexDecoderJitCache::Jit_PosS16Skin}, - {&VertexDecoder::Step_PosFloatSkin, &VertexDecoderJitCache::Jit_PosFloatSkin}, - - {&VertexDecoder::Step_NormalS8Morph, &VertexDecoderJitCache::Jit_NormalS8Morph}, - {&VertexDecoder::Step_NormalS16Morph, &VertexDecoderJitCache::Jit_NormalS16Morph}, - {&VertexDecoder::Step_NormalFloatMorph, &VertexDecoderJitCache::Jit_NormalFloatMorph}, - - {&VertexDecoder::Step_PosS8Morph, &VertexDecoderJitCache::Jit_PosS8Morph}, - {&VertexDecoder::Step_PosS16Morph, &VertexDecoderJitCache::Jit_PosS16Morph}, - {&VertexDecoder::Step_PosFloatMorph, &VertexDecoderJitCache::Jit_PosFloatMorph}, - - {&VertexDecoder::Step_Color8888Morph, &VertexDecoderJitCache::Jit_Color8888Morph}, - {&VertexDecoder::Step_Color4444Morph, &VertexDecoderJitCache::Jit_Color4444Morph}, - {&VertexDecoder::Step_Color565Morph, &VertexDecoderJitCache::Jit_Color565Morph}, - {&VertexDecoder::Step_Color5551Morph, &VertexDecoderJitCache::Jit_Color5551Morph}, -}; - -JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int32_t *jittedSize) { - dec_ = &dec; - //const u8 *start = AlignCode16(); - - bool prescaleStep = false; - bool skinning = false; - - return nullptr; -} - -void VertexDecoderJitCache::Jit_WeightsU8() { -} - -void VertexDecoderJitCache::Jit_WeightsU16() { -} - -void VertexDecoderJitCache::Jit_WeightsFloat() { -} - -void VertexDecoderJitCache::Jit_ApplyWeights() { -} - -void VertexDecoderJitCache::Jit_WeightsU8Skin() { -} - -void VertexDecoderJitCache::Jit_WeightsU16Skin() { -} - -void VertexDecoderJitCache::Jit_WeightsFloatSkin() { -} - -void VertexDecoderJitCache::Jit_TcU8ToFloat() { -} - -void VertexDecoderJitCache::Jit_TcU16ToFloat() { -} - -void VertexDecoderJitCache::Jit_TcFloat() { -} - -void VertexDecoderJitCache::Jit_TcU16ThroughToFloat() { -} - -void VertexDecoderJitCache::Jit_TcFloatThrough() { -} - -void VertexDecoderJitCache::Jit_TcU8Prescale() { -} - -void VertexDecoderJitCache::Jit_TcU16Prescale() { -} - -void VertexDecoderJitCache::Jit_TcFloatPrescale() { -} - -void VertexDecoderJitCache::Jit_Color8888() { -} - -void VertexDecoderJitCache::Jit_Color4444() { -} - -void VertexDecoderJitCache::Jit_Color565() { -} - -void VertexDecoderJitCache::Jit_Color5551() { -} - -void VertexDecoderJitCache::Jit_Color8888Morph() { -} - -void VertexDecoderJitCache::Jit_Color4444Morph() { -} - -void VertexDecoderJitCache::Jit_Color565Morph() { -} - -void VertexDecoderJitCache::Jit_Color5551Morph() { -} - -void VertexDecoderJitCache::Jit_WriteMorphColor(int outOff, bool checkAlpha) { -} - -void VertexDecoderJitCache::Jit_NormalS8() { -} - -void VertexDecoderJitCache::Jit_NormalS16() { -} - -void VertexDecoderJitCache::Jit_NormalFloat() { -} - -void VertexDecoderJitCache::Jit_PosS8Through() { -} - -void VertexDecoderJitCache::Jit_PosS16Through() { -} - -void VertexDecoderJitCache::Jit_PosS8() { -} - -void VertexDecoderJitCache::Jit_PosS16() { -} - -// Just copy 12 bytes. -void VertexDecoderJitCache::Jit_PosFloat() { -} - -void VertexDecoderJitCache::Jit_NormalS8Skin() { -} - -void VertexDecoderJitCache::Jit_NormalS16Skin() { -} - -void VertexDecoderJitCache::Jit_NormalFloatSkin() { -} - -void VertexDecoderJitCache::Jit_WriteMatrixMul(int outOff, bool pos) { -} - -void VertexDecoderJitCache::Jit_PosS8Skin() { -} - -void VertexDecoderJitCache::Jit_PosS16Skin() { -} - -void VertexDecoderJitCache::Jit_PosFloatSkin() { -} - -void VertexDecoderJitCache::Jit_AnyS8ToFloat(int srcoff) { -} - -void VertexDecoderJitCache::Jit_AnyS16ToFloat(int srcoff) { -} - -void VertexDecoderJitCache::Jit_AnyS8Morph(int srcoff, int dstoff) { -} - -void VertexDecoderJitCache::Jit_AnyS16Morph(int srcoff, int dstoff) { -} - -void VertexDecoderJitCache::Jit_AnyFloatMorph(int srcoff, int dstoff) { -} - -void VertexDecoderJitCache::Jit_PosS8Morph() { - Jit_AnyS8Morph(dec_->posoff, dec_->decFmt.posoff); -} - -void VertexDecoderJitCache::Jit_PosS16Morph() { - Jit_AnyS16Morph(dec_->posoff, dec_->decFmt.posoff); -} - -void VertexDecoderJitCache::Jit_PosFloatMorph() { - Jit_AnyFloatMorph(dec_->posoff, dec_->decFmt.posoff); -} - -void VertexDecoderJitCache::Jit_NormalS8Morph() { - Jit_AnyS8Morph(dec_->nrmoff, dec_->decFmt.nrmoff); -} - -void VertexDecoderJitCache::Jit_NormalS16Morph() { - Jit_AnyS16Morph(dec_->nrmoff, dec_->decFmt.nrmoff); -} - -void VertexDecoderJitCache::Jit_NormalFloatMorph() { - Jit_AnyFloatMorph(dec_->nrmoff, dec_->decFmt.nrmoff); -} - -bool VertexDecoderJitCache::CompileStep(const VertexDecoder &dec, int step) { - return false; -} diff --git a/GPU/D3D11/DepalettizeShaderD3D11.cpp b/GPU/D3D11/DepalettizeShaderD3D11.cpp index faf6c22de2..e2a1c58e91 100644 --- a/GPU/D3D11/DepalettizeShaderD3D11.cpp +++ b/GPU/D3D11/DepalettizeShaderD3D11.cpp @@ -19,8 +19,8 @@ #include #include "Common/Common.h" +#include "Common/Data/Convert/ColorConv.h" #include "Common/Log.h" -#include "Common/ColorConv.h" #include "Common/StringUtils.h" #include "Core/Reporting.h" #include "GPU/D3D11/TextureCacheD3D11.h" diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index 0828b68a1d..40391fbfc5 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -20,11 +20,11 @@ #include #include "Common/Common.h" +#include "Common/Data/Convert/ColorConv.h" #include "Common/System/Display.h" #include "Common/Math/lin/matrix4x4.h" #include "Common/Math/math_util.h" #include "Common/GPU/thin3d.h" -#include "Common/ColorConv.h" #include "Core/MemMap.h" #include "Core/Config.h" diff --git a/GPU/D3D11/GPU_D3D11.cpp b/GPU/D3D11/GPU_D3D11.cpp index 136f81d188..22725465df 100644 --- a/GPU/D3D11/GPU_D3D11.cpp +++ b/GPU/D3D11/GPU_D3D11.cpp @@ -15,25 +15,6 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include "GPU/D3D11/GPU_D3D11.h" - -// Copyright (c) 2012- PPSSPP Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0 or later versions. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official git repository and contact information can be found at -// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. - #include #include "Common/Log.h" @@ -87,6 +68,7 @@ GPU_D3D11::GPU_D3D11(GraphicsContext *gfxCtx, Draw::DrawContext *draw) drawEngine_.SetShaderManager(shaderManagerD3D11_); drawEngine_.SetTextureCache(textureCacheD3D11_); drawEngine_.SetFramebufferManager(framebufferManagerD3D11_); + drawEngine_.Init(); framebufferManagerD3D11_->SetTextureCache(textureCacheD3D11_); framebufferManagerD3D11_->SetShaderManager(shaderManagerD3D11_); framebufferManagerD3D11_->SetDrawEngine(&drawEngine_); diff --git a/GPU/D3D11/TextureScalerD3D11.cpp b/GPU/D3D11/TextureScalerD3D11.cpp index 17dcfa3c25..45aa67e18d 100644 --- a/GPU/D3D11/TextureScalerD3D11.cpp +++ b/GPU/D3D11/TextureScalerD3D11.cpp @@ -18,7 +18,7 @@ #include #include -#include "Common/ColorConv.h" +#include "Common/Data/Convert/ColorConv.h" #include "Core/ThreadPools.h" #include "GPU/Common/TextureScalerCommon.h" #include "GPU/D3D11/TextureScalerD3D11.h" @@ -54,4 +54,4 @@ void TextureScalerD3D11::ConvertTo8888(u32 format, u32* source, u32* &dest, int dest = source; ERROR_LOG(G3D, "iXBRZTexScaling: unsupported texture format"); } -} \ No newline at end of file +} diff --git a/GPU/Debugger/Playback.cpp b/GPU/Debugger/Playback.cpp index 9009632b33..459fe52f23 100644 --- a/GPU/Debugger/Playback.cpp +++ b/GPU/Debugger/Playback.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "Common/Profiler/Profiler.h" #include "Common/Common.h" #include "Common/Log.h" @@ -637,7 +638,7 @@ bool DumpExecute::Run() { return true; } -static bool ReadCompressed(u32 fp, void *dest, size_t sz) { +static bool ReadCompressed(u32 fp, void *dest, size_t sz, uint32_t version) { u32 compressed_size = 0; if (pspFileSystem.ReadFile(fp, (u8 *)&compressed_size, sizeof(compressed_size)) != sizeof(compressed_size)) { return false; @@ -650,7 +651,10 @@ static bool ReadCompressed(u32 fp, void *dest, size_t sz) { } size_t real_size = sz; - snappy_uncompress((const char *)compressed, compressed_size, (char *)dest, &real_size); + if (version < 5) + snappy_uncompress((const char *)compressed, compressed_size, (char *)dest, &real_size); + else + real_size = ZSTD_decompress(dest, real_size, compressed, compressed_size); delete[] compressed; return real_size == sz; @@ -699,8 +703,8 @@ bool RunMountedReplay(const std::string &filename) { lastExecPushbuf.resize(bufsz); bool truncated = false; - truncated = truncated || !ReadCompressed(fp, lastExecCommands.data(), sizeof(Command) * sz); - truncated = truncated || !ReadCompressed(fp, lastExecPushbuf.data(), bufsz); + truncated = truncated || !ReadCompressed(fp, lastExecCommands.data(), sizeof(Command) * sz, header.version); + truncated = truncated || !ReadCompressed(fp, lastExecPushbuf.data(), bufsz, header.version); pspFileSystem.CloseFile(fp); diff --git a/GPU/Debugger/Record.cpp b/GPU/Debugger/Record.cpp index 4fdc32b29f..dc9ae32834 100644 --- a/GPU/Debugger/Record.cpp +++ b/GPU/Debugger/Record.cpp @@ -16,11 +16,12 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include #include #include #include #include -#include +#include #include "Common/Common.h" #include "Common/File/FileUtil.h" @@ -32,6 +33,7 @@ #include "Core/HLE/sceDisplay.h" #include "Core/MemMap.h" #include "Core/System.h" +#include "Core/ThreadPools.h" #include "GPU/GPUInterface.h" #include "GPU/GPUState.h" #include "GPU/ge_constants.h" @@ -45,7 +47,7 @@ namespace GPURecord { static bool active = false; static bool nextFrame = false; static int flipLastAction = -1; -static std::function writeCallback; +static std::function writeCallback; static std::vector pushbuf; static std::vector commands; @@ -66,20 +68,24 @@ static void FlushRegisters() { } } -static std::string GenRecordingFilename() { - const std::string dumpDir = GetSysDirectory(DIRECTORY_DUMP); - const std::string prefix = dumpDir + g_paramSFO.GetDiscID(); +static Path GenRecordingFilename() { + const Path dumpDir = GetSysDirectory(DIRECTORY_DUMP); File::CreateFullPath(dumpDir); + const std::string prefix = g_paramSFO.GetDiscID(); + for (int n = 1; n < 10000; ++n) { std::string filename = StringFromFormat("%s_%04d.ppdmp", prefix.c_str(), n); - if (!File::Exists(filename)) { - return filename; + + const Path path = dumpDir / filename; + + if (!File::Exists(path)) { + return path; } } - return StringFromFormat("%s_%04d.ppdmp", prefix.c_str(), 9999); + return dumpDir / StringFromFormat("%s_%04d.ppdmp", prefix.c_str(), 9999); } static void BeginRecording() { @@ -98,9 +104,9 @@ static void BeginRecording() { } static void WriteCompressed(FILE *fp, const void *p, size_t sz) { - size_t compressed_size = snappy_max_compressed_length(sz); + size_t compressed_size = ZSTD_compressBound(sz); u8 *compressed = new u8[compressed_size]; - snappy_compress((const char *)p, sz, (char *)compressed, &compressed_size); + compressed_size = ZSTD_compress(compressed, compressed_size, p, sz, 6); u32 write_size = (u32)compressed_size; fwrite(&write_size, sizeof(write_size), 1, fp); @@ -109,10 +115,10 @@ static void WriteCompressed(FILE *fp, const void *p, size_t sz) { delete [] compressed; } -static std::string WriteRecording() { +static Path WriteRecording() { FlushRegisters(); - const std::string filename = GenRecordingFilename(); + const Path filename = GenRecordingFilename(); NOTICE_LOG(G3D, "Recording filename: %s", filename.c_str()); @@ -166,34 +172,47 @@ static const u8 *mymemmem(const u8 *haystack, size_t off, size_t hlen, const u8 } const u8 *last_possible = haystack + hlen - nlen; + const u8 *first_possible = haystack + off; int first = *needle; - const u8 *p = haystack + off; - const uintptr_t align_mask = align - 1; - auto poffset = [&]() { - return ((uintptr_t)(p - haystack) & align_mask); - }; - auto alignp = [&]() { - uintptr_t offset = poffset(); - if (offset != 0) - p += align - offset; - }; + const u8 *result = nullptr; + std::mutex resultLock; - alignp(); - while (p <= last_possible) { - p = (const u8 *)memchr(p, first, last_possible - p + 1); - if (!p) { - return nullptr; - } - if (poffset() == 0 && !memcmp(p, needle, nlen)) { - return p; - } + int range = (int)(last_possible - first_possible); + GlobalThreadPool::Loop([&](int l, int h) { + const u8 *p = haystack + off + l; + const u8 *pend = haystack + off + h; + + const uintptr_t align_mask = align - 1; + auto poffset = [&]() { + return ((uintptr_t)(p - haystack) & align_mask); + }; + auto alignp = [&]() { + uintptr_t offset = poffset(); + if (offset != 0) + p += align - offset; + }; - p++; alignp(); - } + while (p <= pend) { + p = (const u8 *)memchr(p, first, pend - p + 1); + if (!p) { + return; + } + if (poffset() == 0 && !memcmp(p, needle, nlen)) { + std::lock_guard guard(resultLock); + // Take the lowest result so we get the same file for any # of threads. + if (!result || p < result) + result = p; + return; + } - return nullptr; + p++; + alignp(); + } + }, 0, range, 128 * 1024); + + return result; } static Command EmitCommandWithRAM(CommandType t, const void *p, u32 sz, u32 align) { @@ -397,13 +416,13 @@ bool Activate() { return false; } -void SetCallback(const std::function callback) { +void SetCallback(const std::function callback) { writeCallback = callback; } static void FinishRecording() { // We're done - this was just to write the result out. - std::string filename = WriteRecording(); + Path filename = WriteRecording(); commands.clear(); pushbuf.clear(); diff --git a/GPU/Debugger/Record.h b/GPU/Debugger/Record.h index ce15aa68d0..e5e7e31cd1 100644 --- a/GPU/Debugger/Record.h +++ b/GPU/Debugger/Record.h @@ -19,7 +19,9 @@ #include #include + #include "Common/CommonTypes.h" +#include "Common/File/Path.h" namespace GPURecord { @@ -27,7 +29,7 @@ bool IsActive(); bool IsActivePending(); bool Activate(); // Call only if Activate() returns true. -void SetCallback(const std::function callback); +void SetCallback(const std::function callback); void NotifyCommand(u32 pc); void NotifyMemcpy(u32 dest, u32 src, u32 sz); diff --git a/GPU/Debugger/RecordFormat.h b/GPU/Debugger/RecordFormat.h index f59e71aa7e..22395e1594 100644 --- a/GPU/Debugger/RecordFormat.h +++ b/GPU/Debugger/RecordFormat.h @@ -33,7 +33,8 @@ static const char *HEADER_MAGIC = "PPSSPPGE"; // Version 2: Uses snappy // Version 3: Adds FRAMEBUF0-FRAMEBUF9 // Version 4: Expanded header with game ID -static const int VERSION = 4; +// Version 5: Uses zstd +static const int VERSION = 5; static const int MIN_VERSION = 2; enum class CommandType : u8 { diff --git a/GPU/Directx9/FramebufferManagerDX9.cpp b/GPU/Directx9/FramebufferManagerDX9.cpp index 08c40135b3..b06c0c4e06 100644 --- a/GPU/Directx9/FramebufferManagerDX9.cpp +++ b/GPU/Directx9/FramebufferManagerDX9.cpp @@ -18,7 +18,7 @@ #include "Common/Math/lin/matrix4x4.h" #include "Common/GPU/thin3d.h" -#include "Common/ColorConv.h" +#include "Common/Data/Convert/ColorConv.h" #include "Core/MemMap.h" #include "Core/Config.h" #include "Core/ConfigValues.h" diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index f773e2c46f..f979ceb386 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -69,6 +69,7 @@ GPU_DX9::GPU_DX9(GraphicsContext *gfxCtx, Draw::DrawContext *draw) drawEngine_.SetShaderManager(shaderManagerDX9_); drawEngine_.SetTextureCache(textureCacheDX9_); drawEngine_.SetFramebufferManager(framebufferManagerDX9_); + drawEngine_.Init(); framebufferManagerDX9_->SetTextureCache(textureCacheDX9_); framebufferManagerDX9_->SetShaderManager(shaderManagerDX9_); framebufferManagerDX9_->SetDrawEngine(&drawEngine_); diff --git a/GPU/Directx9/TextureScalerDX9.cpp b/GPU/Directx9/TextureScalerDX9.cpp index 4508358d7e..574fa3eebc 100644 --- a/GPU/Directx9/TextureScalerDX9.cpp +++ b/GPU/Directx9/TextureScalerDX9.cpp @@ -18,7 +18,7 @@ #include #include "Common/Common.h" -#include "Common/ColorConv.h" +#include "Common/Data/Convert/ColorConv.h" #include "Core/ThreadPools.h" #include "GPU/Common/TextureScalerCommon.h" #include "GPU/Directx9/TextureScalerDX9.h" diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index be01c06702..5870c899ef 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -17,12 +17,12 @@ #include +#include "Common/Data/Convert/ColorConv.h" #include "Common/Profiler/Profiler.h" #include "Common/GPU/OpenGL/GLCommon.h" #include "Common/GPU/OpenGL/GLDebugLog.h" #include "Common/GPU/OpenGL/GLSLProgram.h" #include "Common/GPU/thin3d.h" -#include "Common/ColorConv.h" #include "Core/MemMap.h" #include "Core/Config.h" #include "Core/ConfigValues.h" diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 88799d5120..d60e1b5592 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -53,7 +53,7 @@ #endif GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw) -: GPUCommon(gfxCtx, draw), depalShaderCache_(draw), drawEngine_(draw), fragmentTestCache_(draw) { + : GPUCommon(gfxCtx, draw), depalShaderCache_(draw), drawEngine_(draw), fragmentTestCache_(draw) { UpdateVsyncInterval(true); CheckGPUFeatures(); @@ -72,6 +72,7 @@ GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw) drawEngine_.SetTextureCache(textureCacheGL_); drawEngine_.SetFramebufferManager(framebufferManagerGL_); drawEngine_.SetFragmentTestCache(&fragmentTestCache_); + drawEngine_.Init(); framebufferManagerGL_->SetTextureCache(textureCacheGL_); framebufferManagerGL_->SetShaderManager(shaderManagerGL_); framebufferManagerGL_->SetDrawEngine(&drawEngine_); @@ -103,7 +104,7 @@ GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw) std::string discID = g_paramSFO.GetDiscID(); if (discID.size()) { File::CreateFullPath(GetSysDirectory(DIRECTORY_APP_CACHE)); - shaderCachePath_ = GetSysDirectory(DIRECTORY_APP_CACHE) + "/" + discID + ".glshadercache"; + shaderCachePath_ = GetSysDirectory(DIRECTORY_APP_CACHE) / (discID + ".glshadercache"); // Actually precompiled by IsReady() since we're single-threaded. shaderManagerGL_->Load(shaderCachePath_); } @@ -127,7 +128,7 @@ GPU_GLES::~GPU_GLES() { // If we're here during app shutdown (exiting the Windows app in-game, for example) // everything should already be cleared since DeviceLost has been run. - if (!shaderCachePath_.empty() && draw_) { + if (shaderCachePath_.Valid() && draw_) { shaderManagerGL_->Save(shaderCachePath_); } @@ -353,7 +354,7 @@ void GPU_GLES::BeginFrame() { GPUCommon::BeginFrame(); // Save the cache from time to time. TODO: How often? We save on exit, so shouldn't need to do this all that often. - if (!shaderCachePath_.empty() && (gpuStats.numFlips & 4095) == 0) { + if (shaderCachePath_.Valid() && (gpuStats.numFlips & 4095) == 0) { shaderManagerGL_->Save(shaderCachePath_); } diff --git a/GPU/GLES/GPU_GLES.h b/GPU/GLES/GPU_GLES.h index 5bb68e2ef3..bcd2c4d1c5 100644 --- a/GPU/GLES/GPU_GLES.h +++ b/GPU/GLES/GPU_GLES.h @@ -20,6 +20,8 @@ #include #include +#include "Common/File/Path.h" + #include "GPU/GPUCommon.h" #include "GPU/GLES/FramebufferManagerGLES.h" #include "GPU/GLES/DrawEngineGLES.h" @@ -86,5 +88,5 @@ private: FragmentTestCacheGLES fragmentTestCache_; ShaderManagerGLES *shaderManagerGL_; - std::string shaderCachePath_; + Path shaderCachePath_; }; diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index f6914aecc4..3fcc537d55 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -849,7 +849,7 @@ struct CacheHeader { int numLinkedPrograms; }; -void ShaderManagerGLES::Load(const std::string &filename) { +void ShaderManagerGLES::Load(const Path &filename) { File::IOFile f(filename, "rb"); u64 sz = f.GetSize(); if (!f.IsOpen()) { @@ -997,7 +997,7 @@ void ShaderManagerGLES::CancelPrecompile() { diskCachePending_.Clear(); } -void ShaderManagerGLES::Save(const std::string &filename) { +void ShaderManagerGLES::Save(const Path &filename) { if (!diskCacheDirty_) { return; } diff --git a/GPU/GLES/ShaderManagerGLES.h b/GPU/GLES/ShaderManagerGLES.h index bdaf73a367..c49b6700c0 100644 --- a/GPU/GLES/ShaderManagerGLES.h +++ b/GPU/GLES/ShaderManagerGLES.h @@ -21,6 +21,7 @@ #include "Common/Data/Collections/Hashmaps.h" #include "Common/GPU/OpenGL/GLRenderManager.h" +#include "Common/File/Path.h" #include "GPU/Common/ShaderCommon.h" #include "GPU/Common/ShaderId.h" #include "GPU/Common/VertexShaderGenerator.h" @@ -162,10 +163,10 @@ public: std::vector DebugGetShaderIDs(DebugShaderType type); std::string DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType); - void Load(const std::string &filename); + void Load(const Path &filename); bool ContinuePrecompile(float sliceTime = 1.0f / 60.0f); void CancelPrecompile(); - void Save(const std::string &filename); + void Save(const Path &filename); private: void Clear(); diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 2b153eb8fe..f69be7a23f 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -19,12 +19,12 @@ #include #include "ext/xxhash.h" +#include "Common/Data/Convert/ColorConv.h" #include "Common/Data/Text/I18n.h" #include "Common/Math/math_util.h" #include "Common/Profiler/Profiler.h" #include "Common/GPU/OpenGL/GLRenderManager.h" -#include "Common/ColorConv.h" #include "Core/Config.h" #include "Core/Host.h" #include "Core/MemMap.h" diff --git a/GPU/GLES/TextureScalerGLES.cpp b/GPU/GLES/TextureScalerGLES.cpp index 2a22d8b178..abe06bb5bd 100644 --- a/GPU/GLES/TextureScalerGLES.cpp +++ b/GPU/GLES/TextureScalerGLES.cpp @@ -20,7 +20,7 @@ #include "GPU/Common/TextureScalerCommon.h" #include "GPU/GLES/TextureScalerGLES.h" -#include "Common/ColorConv.h" +#include "Common/Data/Convert/ColorConv.h" #include "Common/Log.h" #include "Core/ThreadPools.h" #include "Common/GPU/DataFormat.h" diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index ddaaa6e7ef..290a86c054 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -164,7 +164,7 @@ Level3 - ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang + ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_32=1;_M_IX86=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) StreamingSIMDExtensions2 Precise @@ -182,7 +182,7 @@ Level3 - ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang + ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib NotSet Precise false @@ -202,7 +202,7 @@ Level3 - ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang + ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib NotSet Precise false @@ -221,7 +221,7 @@ Level3 - ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang + ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib NotSet Precise false @@ -244,7 +244,7 @@ MaxSpeed true true - ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang + ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib false StreamingSIMDExtensions2 Precise @@ -267,7 +267,7 @@ MaxSpeed true true - ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang + ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib false NotSet Precise @@ -292,7 +292,7 @@ MaxSpeed true true - ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang + ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib false NotSet Precise @@ -317,7 +317,7 @@ MaxSpeed true true - ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang + ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib false NotSet Precise diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index e08ee51b51..3b7176bf38 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -5,7 +5,7 @@ #include "Common/Profiler/Profiler.h" -#include "Common/ColorConv.h" +#include "Common/Data/Convert/ColorConv.h" #include "Common/GraphicsContext.h" #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" @@ -2727,8 +2727,11 @@ void GPUCommon::DoBlockTransfer(u32 skipDrawReason) { framebufferManager_->NotifyBlockTransferAfter(dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, width, height, bpp, skipDrawReason); } - NotifyMemInfo(MemBlockFlags::READ, srcBasePtr + (srcY * srcStride + srcX) * bpp, height * srcStride * bpp, "GPUBlockTransfer"); - NotifyMemInfo(MemBlockFlags::WRITE, dstBasePtr + (dstY * dstStride + dstX) * bpp, height * dstStride * bpp, "GPUBlockTransfer"); + const uint32_t src = srcBasePtr + (srcY * srcStride + srcX) * bpp; + const uint32_t srcSize = height * srcStride * bpp; + const std::string tag = "GPUBlockTransfer/" + GetMemWriteTagAt(src, srcSize); + NotifyMemInfo(MemBlockFlags::READ, src, srcSize, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, dstBasePtr + (dstY * dstStride + dstX) * bpp, height * dstStride * bpp, tag.c_str(), tag.size()); // TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate. cyclesExecuted += ((height * width * bpp) * 16) / 10; @@ -2741,15 +2744,17 @@ bool GPUCommon::PerformMemoryCopy(u32 dest, u32 src, int size) { // We use a little hack for PerformMemoryDownload/PerformMemoryUpload using a VRAM mirror. // Since they're identical we don't need to copy. if (!Memory::IsVRAMAddress(dest) || (dest ^ 0x00400000) != src) { - Memory::Memcpy(dest, src, size); + const std::string tag = "GPUMemcpy/" + GetMemWriteTagAt(src, size); + Memory::Memcpy(dest, src, size, tag.c_str(), tag.size()); } } InvalidateCache(dest, size, GPU_INVALIDATE_HINT); return true; } - NotifyMemInfo(MemBlockFlags::READ, src, size, "GPUMemcpy"); - NotifyMemInfo(MemBlockFlags::WRITE, dest, size, "GPUMemcpy"); + const std::string tag = "GPUMemcpy/" + GetMemWriteTagAt(src, size); + NotifyMemInfo(MemBlockFlags::READ, src, size, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, dest, size, tag.c_str(), tag.size()); InvalidateCache(dest, size, GPU_INVALIDATE_HINT); GPURecord::NotifyMemcpy(dest, src, size); return false; diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index 943b3f5656..f961175c2c 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -19,10 +19,10 @@ #include #include +#include "Common/Data/Convert/ColorConv.h" #include "Common/Profiler/Profiler.h" #include "Core/ThreadPools.h" -#include "Common/ColorConv.h" #include "Core/Config.h" #include "Core/MemMap.h" #include "Core/Reporting.h" diff --git a/GPU/Software/RasterizerRectangle.cpp b/GPU/Software/RasterizerRectangle.cpp index 828a7d622b..bbc7e27463 100644 --- a/GPU/Software/RasterizerRectangle.cpp +++ b/GPU/Software/RasterizerRectangle.cpp @@ -3,17 +3,15 @@ #include #include +#include "Common/Data/Convert/ColorConv.h" #include "Common/Profiler/Profiler.h" -#include "Core/System.h" - -#include "Common/ColorConv.h" #include "Core/Config.h" #include "Core/MemMap.h" #include "Core/Reporting.h" +#include "Core/System.h" #include "GPU/GPUState.h" -#include "Rasterizer.h" #include "GPU/Common/TextureCacheCommon.h" #include "GPU/Software/SoftGpu.h" #include "GPU/Software/Rasterizer.h" diff --git a/GPU/Software/Sampler.cpp b/GPU/Software/Sampler.cpp index 2fccf208b2..607aafd3dd 100644 --- a/GPU/Software/Sampler.cpp +++ b/GPU/Software/Sampler.cpp @@ -18,7 +18,7 @@ #include "ppsspp_config.h" #include #include -#include "Common/ColorConv.h" +#include "Common/Data/Convert/ColorConv.h" #include "Core/Reporting.h" #include "GPU/Common/TextureDecoder.h" #include "GPU/GPUState.h" diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index a04369a723..16e7537171 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -21,7 +21,7 @@ #include "GPU/GPUState.h" #include "GPU/ge_constants.h" #include "GPU/Common/TextureDecoder.h" -#include "Common/ColorConv.h" +#include "Common/Data/Convert/ColorConv.h" #include "Common/GraphicsContext.h" #include "Core/Config.h" #include "Core/ConfigValues.h" @@ -68,25 +68,12 @@ SoftGPU::SoftGPU(GraphicsContext *gfxCtx, Draw::DrawContext *draw) Sampler::Init(); drawEngine_ = new SoftwareDrawEngine(); + drawEngine_->Init(); drawEngineCommon_ = drawEngine_; if (gfxCtx && draw) { presentation_ = new PresentationCommon(draw_); - - switch (GetGPUBackend()) { - case GPUBackend::OPENGL: - presentation_->SetLanguage(draw_->GetShaderLanguageDesc().shaderLanguage); - break; - case GPUBackend::DIRECT3D9: - presentation_->SetLanguage(HLSL_D3D9); - break; - case GPUBackend::DIRECT3D11: - presentation_->SetLanguage(HLSL_D3D11); - break; - case GPUBackend::VULKAN: - presentation_->SetLanguage(GLSL_VULKAN); - break; - } + presentation_->SetLanguage(draw_->GetShaderLanguageDesc().shaderLanguage); } Resized(); } @@ -653,8 +640,11 @@ void SoftGPU::ExecuteOp(u32 op, u32 diff) { memcpy(dst, src, width * bpp); } - NotifyMemInfo(MemBlockFlags::READ, srcBasePtr + (srcY * srcStride + srcX) * bpp, height * srcStride * bpp, "GPUBlockTransfer"); - NotifyMemInfo(MemBlockFlags::WRITE, dstBasePtr + (dstY * dstStride + dstX) * bpp, height * dstStride * bpp, "GPUBlockTransfer"); + const uint32_t src = srcBasePtr + (srcY * srcStride + srcX) * bpp; + const uint32_t srcSize = height * srcStride * bpp; + const std::string tag = "GPUBlockTransfer/" + GetMemWriteTagAt(src, srcSize); + NotifyMemInfo(MemBlockFlags::READ, src, srcSize, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, dstBasePtr + (dstY * dstStride + dstX) * bpp, height * dstStride * bpp, tag.c_str(), tag.size()); // TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate. cyclesExecuted += ((height * width * bpp) * 16) / 10; diff --git a/GPU/Vulkan/DepalettizeShaderVulkan.cpp b/GPU/Vulkan/DepalettizeShaderVulkan.cpp index 4d11207eb2..7768218998 100644 --- a/GPU/Vulkan/DepalettizeShaderVulkan.cpp +++ b/GPU/Vulkan/DepalettizeShaderVulkan.cpp @@ -15,7 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include "Common/ColorConv.h" +#include "Common/Data/Convert/ColorConv.h" #include "Common/GPU/Vulkan/VulkanImage.h" #include "Common/GPU/Vulkan/VulkanMemory.h" #include "Common/GPU/Vulkan/VulkanContext.h" diff --git a/GPU/Vulkan/FramebufferManagerVulkan.cpp b/GPU/Vulkan/FramebufferManagerVulkan.cpp index a9e2fb230b..ac60198cb9 100644 --- a/GPU/Vulkan/FramebufferManagerVulkan.cpp +++ b/GPU/Vulkan/FramebufferManagerVulkan.cpp @@ -21,6 +21,7 @@ #include "Common/System/Display.h" #include "Common/Math/lin/matrix4x4.h" +#include "Common/Data/Convert/ColorConv.h" #include "Common/Data/Convert/SmallDataConvert.h" #include "Common/GPU/thin3d.h" @@ -28,7 +29,6 @@ #include "Common/GPU/Vulkan/VulkanMemory.h" #include "Common/GPU/Vulkan/VulkanImage.h" #include "Common/GPU/Vulkan/VulkanRenderManager.h" -#include "Common/ColorConv.h" #include "Core/MemMap.h" #include "Core/Config.h" #include "Core/ConfigValues.h" diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index cb627e1567..e52df542be 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -72,6 +72,7 @@ GPU_Vulkan::GPU_Vulkan(GraphicsContext *gfxCtx, Draw::DrawContext *draw) drawEngine_.SetFramebufferManager(framebufferManagerVulkan_); drawEngine_.SetShaderManager(shaderManagerVulkan_); drawEngine_.SetPipelineManager(pipelineManager_); + drawEngine_.Init(); framebufferManagerVulkan_->SetVulkan2D(&vulkan2D_); framebufferManagerVulkan_->SetTextureCache(textureCacheVulkan_); framebufferManagerVulkan_->SetDrawEngine(&drawEngine_); @@ -103,7 +104,7 @@ GPU_Vulkan::GPU_Vulkan(GraphicsContext *gfxCtx, Draw::DrawContext *draw) std::string discID = g_paramSFO.GetDiscID(); if (discID.size()) { File::CreateFullPath(GetSysDirectory(DIRECTORY_APP_CACHE)); - shaderCachePath_ = GetSysDirectory(DIRECTORY_APP_CACHE) + "/" + discID + ".vkshadercache"; + shaderCachePath_ = GetSysDirectory(DIRECTORY_APP_CACHE) / (discID + ".vkshadercache"); shaderCacheLoaded_ = false; std::thread th([&] { @@ -124,7 +125,7 @@ void GPU_Vulkan::CancelReady() { pipelineManager_->CancelCache(); } -void GPU_Vulkan::LoadCache(std::string filename) { +void GPU_Vulkan::LoadCache(const Path &filename) { PSP_SetLoading("Loading shader cache..."); // Actually precompiled by IsReady() since we're single-threaded. FILE *f = File::OpenCFile(filename, "rb"); @@ -149,7 +150,7 @@ void GPU_Vulkan::LoadCache(std::string filename) { } } -void GPU_Vulkan::SaveCache(std::string filename) { +void GPU_Vulkan::SaveCache(const Path &filename) { if (!draw_) { // Already got the lost message, we're in shutdown. WARN_LOG(G3D, "Not saving shaders - shutting down from in-game."); @@ -523,7 +524,7 @@ void GPU_Vulkan::DeviceLost() { while (!IsReady()) { sleep_ms(10); } - if (!shaderCachePath_.empty()) { + if (shaderCachePath_.Valid()) { SaveCache(shaderCachePath_); } DestroyDeviceObjects(); diff --git a/GPU/Vulkan/GPU_Vulkan.h b/GPU/Vulkan/GPU_Vulkan.h index 894cc295b4..2cd6edb16a 100644 --- a/GPU/Vulkan/GPU_Vulkan.h +++ b/GPU/Vulkan/GPU_Vulkan.h @@ -20,6 +20,8 @@ #include #include +#include "Common/File/Path.h" + #include "GPU/GPUCommon.h" #include "GPU/Vulkan/DrawEngineVulkan.h" #include "GPU/Vulkan/PipelineManagerVulkan.h" @@ -84,8 +86,8 @@ private: void InitDeviceObjects(); void DestroyDeviceObjects(); - void LoadCache(std::string filename); - void SaveCache(std::string filename); + void LoadCache(const Path &filename); + void SaveCache(const Path &filename); VulkanContext *vulkan_; FramebufferManagerVulkan *framebufferManagerVulkan_; @@ -108,6 +110,6 @@ private: FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]{}; - std::string shaderCachePath_; + Path shaderCachePath_; bool shaderCacheLoaded_ = false; }; diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 3fc4f661c7..572cb64c37 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -26,7 +26,7 @@ #include "Common/GPU/thin3d.h" #include "Common/GPU/Vulkan/VulkanRenderManager.h" -#include "Common/ColorConv.h" +#include "Common/Data/Convert/ColorConv.h" #include "Common/StringUtils.h" #include "Core/Config.h" #include "Core/Host.h" @@ -375,7 +375,7 @@ void TextureCacheVulkan::NotifyConfigChanged() { CompileScalingShader(); } -static std::string ReadShaderSrc(const std::string &filename) { +static std::string ReadShaderSrc(const Path &filename) { size_t sz = 0; char *data = (char *)VFSReadFile(filename.c_str(), &sz); if (!data) diff --git a/GPU/Vulkan/TextureScalerVulkan.cpp b/GPU/Vulkan/TextureScalerVulkan.cpp index 66720c0f36..e714e3eb0c 100644 --- a/GPU/Vulkan/TextureScalerVulkan.cpp +++ b/GPU/Vulkan/TextureScalerVulkan.cpp @@ -18,8 +18,8 @@ #include #include "Common/Common.h" +#include "Common/Data/Convert/ColorConv.h" #include "Common/GPU/Vulkan/VulkanContext.h" -#include "Common/ColorConv.h" #include "Common/Log.h" #include "Core/ThreadPools.h" #include "GPU/Common/TextureScalerCommon.h" diff --git a/Qt/QtHost.cpp b/Qt/QtHost.cpp index e13f916c69..02ed199734 100644 --- a/Qt/QtHost.cpp +++ b/Qt/QtHost.cpp @@ -18,12 +18,6 @@ #include #include "Qt/QtHost.h" -std::string QtHost::SymbolMapFilename(std::string currentFilename) { - size_t dot = currentFilename.rfind('.'); - if (dot == std::string::npos) - currentFilename.append(".map"); - else - currentFilename.replace(dot, -1, ".map"); - - return currentFilename; +Path QtHost::SymbolMapFilename(Path currentFilename) { + return currentFilename.WithReplacedExtension(".map"); } diff --git a/Qt/QtHost.h b/Qt/QtHost.h index 2aa9bc1870..79a65caa68 100644 --- a/Qt/QtHost.h +++ b/Qt/QtHost.h @@ -65,14 +65,14 @@ public: } virtual bool AttemptLoadSymbolMap() override { auto fn = SymbolMapFilename(PSP_CoreParameter().fileToStart); - return g_symbolMap->LoadSymbolMap(fn.c_str()); + return g_symbolMap->LoadSymbolMap(fn); } virtual void NotifySymbolMapUpdated() override { g_symbolMap->SortSymbols(); } void PrepareShutdown() { auto fn = SymbolMapFilename(PSP_CoreParameter().fileToStart); - g_symbolMap->SaveSymbolMap(fn.c_str()); + g_symbolMap->SaveSymbolMap(fn); } void SetWindowTitle(const char *message) override { std::string title = std::string("PPSSPP ") + PPSSPP_GIT_VERSION; @@ -95,6 +95,6 @@ public: void NotifySwitchUMDUpdated() override {} private: - std::string SymbolMapFilename(std::string currentFilename); + Path SymbolMapFilename(Path currentFilename); MainWindow* mainWindow; }; diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 70aa0100d4..0e7916b4bc 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -320,7 +320,7 @@ static int mainInternal(QApplication &a) { } void MainUI::EmuThreadFunc() { - setCurrentThreadName("Emu"); + SetCurrentThreadName("Emu"); // There's no real requirement that NativeInit happen on this thread, though it can't hurt... // We just call the update/render loop here. NativeInitGraphics should be here though. diff --git a/Qt/mainwindow.cpp b/Qt/mainwindow.cpp index 964a61880d..53e18c23d4 100644 --- a/Qt/mainwindow.cpp +++ b/Qt/mainwindow.cpp @@ -11,6 +11,7 @@ #include "Common/System/Display.h" #include "Common/System/NativeApp.h" #include "Common/System/System.h" +#include "Common/File/Path.h" #include "Core/MIPS/MIPSDebugInterface.h" #include "Core/Debugger/SymbolMap.h" #include "Core/HLE/sceUmd.h" @@ -156,13 +157,13 @@ void SaveStateActionFinished(SaveState::Status status, const std::string &messag void MainWindow::qlstateAct() { - std::string gamePath = PSP_CoreParameter().fileToStart; + Path gamePath = PSP_CoreParameter().fileToStart; SaveState::LoadSlot(gamePath, 0, SaveStateActionFinished, this); } void MainWindow::qsstateAct() { - std::string gamePath = PSP_CoreParameter().fileToStart; + Path gamePath = PSP_CoreParameter().fileToStart; SaveState::SaveSlot(gamePath, 0, SaveStateActionFinished, this); } @@ -177,7 +178,7 @@ void MainWindow::lstateAct() if (dialog.exec()) { QStringList fileNames = dialog.selectedFiles(); - SaveState::Load(fileNames[0].toStdString(), -1, SaveStateActionFinished, this); + SaveState::Load(Path(fileNames[0].toStdString()), -1, SaveStateActionFinished, this); } } @@ -192,7 +193,7 @@ void MainWindow::sstateAct() if (dialog.exec()) { QStringList fileNames = dialog.selectedFiles(); - SaveState::Save(fileNames[0].toStdString(), -1, SaveStateActionFinished, this); + SaveState::Save(Path(fileNames[0].toStdString()), -1, SaveStateActionFinished, this); } } @@ -252,7 +253,7 @@ void MainWindow::switchUMDAct() { QFileInfo info(filename); g_Config.currentDirectory = info.absolutePath().toStdString(); - __UmdReplace(filename.toStdString().c_str()); + __UmdReplace(Path(filename.toStdString())); } } @@ -276,7 +277,7 @@ void MainWindow::lmapAct() if (fileNames.count() > 0) { QString fileName = QFileInfo(fileNames[0]).absoluteFilePath(); - g_symbolMap->LoadSymbolMap(fileName.toStdString().c_str()); + g_symbolMap->LoadSymbolMap(Path(fileName.toStdString())); } } @@ -292,7 +293,7 @@ void MainWindow::smapAct() if (dialog.exec()) { fileNames = dialog.selectedFiles(); - g_symbolMap->SaveSymbolMap(fileNames[0].toStdString().c_str()); + g_symbolMap->SaveSymbolMap(Path(fileNames[0].toStdString())); } } @@ -311,7 +312,7 @@ void MainWindow::lsymAct() if (fileNames.count() > 0) { QString fileName = QFileInfo(fileNames[0]).absoluteFilePath(); - g_symbolMap->LoadNocashSym(fileName.toStdString().c_str()); + g_symbolMap->LoadNocashSym(Path(fileName.toStdString())); } } @@ -327,7 +328,7 @@ void MainWindow::ssymAct() if (dialog.exec()) { fileNames = dialog.selectedFiles(); - g_symbolMap->SaveNocashSym(fileNames[0].toStdString().c_str()); + g_symbolMap->SaveNocashSym(Path(fileNames[0].toStdString())); } } diff --git a/README.md b/README.md index bf9f2dce93..3497f2d6ee 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,9 @@ If you want to download regularly updated builds for Android, Windows x86 and x6 For game compatibility, see [community compatibility feedback](https://report.ppsspp.org/games). +What's new in 1.11.3 +==================== +* Fix for graphics glitches in the on-screen keyboard What's new in 1.11.2 ==================== diff --git a/SDL/SDLJoystick.cpp b/SDL/SDLJoystick.cpp index 1dfedeac51..b32d7a471e 100644 --- a/SDL/SDLJoystick.cpp +++ b/SDL/SDLJoystick.cpp @@ -1,11 +1,12 @@ #include #include +#include "Common/File/FileUtil.h" +#include "Common/File/VFS/VFS.h" +#include "Common/StringUtils.h" #include "Common/System/NativeApp.h" #include "Common/System/System.h" -#include "Common/File/VFS/VFS.h" -#include "Common/File/FileUtil.h" #include "Core/Config.h" #include "SDL/SDLJoystick.h" @@ -61,7 +62,8 @@ void SDLJoystick::setUpController(int deviceIndex) { if (joystick) { SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), pszGUID, cbGUID); // create default mapping - this is the PS3 dual shock mapping - std::string mapping = string(pszGUID) + "," + string(SDL_JoystickName(joystick)) + ",x:b3,a:b0,b:b1,y:b2,back:b8,guide:b10,start:b9,dpleft:b15,dpdown:b14,dpright:b16,dpup:b13,leftshoulder:b4,lefttrigger:a2,rightshoulder:b6,rightshoulder:b5,righttrigger:a5,leftstick:b7,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4"; + const std::string safeName = ReplaceAll(SDL_JoystickName(joystick), ",", ""); + std::string mapping = std::string(pszGUID) + "," + safeName + ",x:b3,a:b0,b:b1,y:b2,back:b8,guide:b10,start:b9,dpleft:b15,dpdown:b14,dpright:b16,dpup:b13,leftshoulder:b4,lefttrigger:a2,rightshoulder:b6,rightshoulder:b5,righttrigger:a5,leftstick:b7,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4"; if (SDL_GameControllerAddMapping(mapping.c_str()) == 1){ cout << "Added default mapping ok" << endl; } else { diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index bfc6de059d..f0f74f979a 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -445,7 +445,7 @@ static std::thread emuThread; static std::atomic emuThreadState((int)EmuThreadState::DISABLED); static void EmuThreadFunc(GraphicsContext *graphicsContext) { - setCurrentThreadName("Emu"); + SetCurrentThreadName("Emu"); // There's no real requirement that NativeInit happen on this thread. // We just call the update/render loop here. diff --git a/UI/BackgroundAudio.cpp b/UI/BackgroundAudio.cpp index 809993d5b4..82d7bf586d 100644 --- a/UI/BackgroundAudio.cpp +++ b/UI/BackgroundAudio.cpp @@ -312,7 +312,7 @@ void BackgroundAudio::Clear(bool hard) { sndLoadPending_ = false; } -void BackgroundAudio::SetGame(const std::string &path) { +void BackgroundAudio::SetGame(const Path &path) { if (path == bgGamePath_) { // Do nothing return; diff --git a/UI/BackgroundAudio.h b/UI/BackgroundAudio.h index d8c1ba18e6..85c0da4315 100644 --- a/UI/BackgroundAudio.h +++ b/UI/BackgroundAudio.h @@ -5,6 +5,7 @@ #include #include +#include "Common/File/Path.h" #include "Common/UI/Root.h" class AT3PlusReader; @@ -14,7 +15,7 @@ public: BackgroundAudio(); ~BackgroundAudio(); - void SetGame(const std::string &path); + void SetGame(const Path &path); void Update(); int Play(); @@ -29,7 +30,7 @@ private: }; std::mutex mutex_; - std::string bgGamePath_; + Path bgGamePath_; std::atomic sndLoadPending_; int playbackOffset_ = 0; AT3PlusReader *at3Reader_; diff --git a/UI/ChatScreen.cpp b/UI/ChatScreen.cpp index b21d64d27e..c93d755e42 100644 --- a/UI/ChatScreen.cpp +++ b/UI/ChatScreen.cpp @@ -196,7 +196,7 @@ void ChatMenu::UpdateChat() { } bool ChatMenu::touch(const TouchInput &touch) { - if (!box_ || (touch.flags & TOUCH_DOWN) == 0 || touch.id != 0) { + if (!box_ || (touch.flags & TOUCH_DOWN) == 0) { return UIDialogScreen::touch(touch); } diff --git a/UI/CwCheatScreen.cpp b/UI/CwCheatScreen.cpp index 4f3bdb79d4..09f0da889e 100644 --- a/UI/CwCheatScreen.cpp +++ b/UI/CwCheatScreen.cpp @@ -34,7 +34,7 @@ static const int FILE_CHECK_FRAME_INTERVAL = 53; -CwCheatScreen::CwCheatScreen(const std::string &gamePath) +CwCheatScreen::CwCheatScreen(const Path &gamePath) : UIDialogScreenWithBackground() { gamePath_ = gamePath; } @@ -50,8 +50,8 @@ void CwCheatScreen::LoadCheatInfo() { gameID = info->paramSFO.GetValueString("DISC_ID"); } if ((info->id.empty() || !info->disc_total) - && gamePath_.find("/PSP/GAME/") != std::string::npos) { - gameID = g_paramSFO.GenerateFakeID(gamePath_); + && gamePath_.FilePathContains("PSP/GAME/")) { + gameID = g_paramSFO.GenerateFakeID(gamePath_.ToString()); } if (engine_ == nullptr || gameID != gameID_) { @@ -63,7 +63,7 @@ void CwCheatScreen::LoadCheatInfo() { // We won't parse this, just using it to detect changes to the file. std::string str; - if (readFileToString(true, engine_->CheatFilename().c_str(), str)) { + if (File::ReadFileToString(true, engine_->CheatFilename(), str)) { fileCheckHash_ = XXH3_64bits(str.c_str(), str.size()); } fileCheckCounter_ = 0; @@ -120,7 +120,7 @@ void CwCheatScreen::update() { if (fileCheckCounter_++ >= FILE_CHECK_FRAME_INTERVAL && engine_) { // Check if the file has changed. If it has, we'll reload. std::string str; - if (readFileToString(true, engine_->CheatFilename().c_str(), str)) { + if (File::ReadFileToString(true, engine_->CheatFilename(), str)) { uint64_t newHash = XXH3_64bits(str.c_str(), str.size()); if (newHash != fileCheckHash_) { // This will update the hash. @@ -176,87 +176,111 @@ UI::EventReturn CwCheatScreen::OnEditCheatFile(UI::EventParams ¶ms) { #if PPSSPP_PLATFORM(UWP) LaunchBrowser(engine_->CheatFilename().c_str()); #else - File::openIniFile(engine_->CheatFilename()); + File::OpenFileInEditor(engine_->CheatFilename()); #endif } return UI::EVENT_DONE; } -void CwCheatScreen::ParseCheatDB(std::string line, std::vector title, std::vector newList, std::string cheatFile) { +static char *GetLineNoNewline(char *temp, int sz, FILE *fp) { + char *line = fgets(temp, sz, fp); + if (!line) + return nullptr; + + // If the last character is \n, just make it the terminator. + char *end = line + strlen(line) - 1; + if (*end == '\n') + *end = '\0'; + return line; +} + +void CwCheatScreen::ParseCheatDB(std::string line, std::vector title, std::vector newList, Path cheatFile) { bool finished = false; std::string gameID = StringFromFormat("_S %s-%s", gameID_.substr(0, 4).c_str(), gameID_.substr(4).c_str()); - std::fstream fs; - File::OpenCPPFile(fs, cheatFile, std::ios::in); + FILE *in = File::OpenCFile(cheatFile, "rt"); - if (!fs.is_open()) { + if (!in) { WARN_LOG(COMMON, "Unable to open %s\n", cheatFile.c_str()); } - while (fs.good()) { - getline(fs, line); // get line from file - if (line == gameID) { - title.push_back(line); - getline(fs, line); + char linebuf[2048]{}; + while (in && !feof(in)) { + char *line = GetLineNoNewline(linebuf, sizeof(linebuf), in); + if (line && gameID == line) { title.push_back(line); + line = GetLineNoNewline(linebuf, sizeof(linebuf), in); + if (line) + title.push_back(line); do { if (finished == false) { - getline(fs, line); + line = GetLineNoNewline(linebuf, sizeof(linebuf), in); } - if (line[0] == '_' && line[1] == 'C') { + if (line && line[0] == '_' && line[1] == 'C') { // Test if cheat already exists. for (const auto &existing : fileInfo_) { - if (line.substr(4) == existing.name) { + if (std::string(line).substr(4) == existing.name) { finished = false; goto loop; } } newList.push_back(line); - getline(fs, line); + line = GetLineNoNewline(linebuf, sizeof(linebuf), in); do { - newList.push_back(line); - getline(fs, line); + if (line) + newList.push_back(line); + line = GetLineNoNewline(linebuf, sizeof(linebuf), in); } while ((line[0] == '_' && line[1] == 'L') || line[0] == '/' || line[0] == '#'); finished = true; } else { continue; } loop:; - } while (fs.good() && ((line[0] == '_' && line[1] != 'S') || line[0] == '/' || line[0] == '#')); + } while (!feof(in) && ((line[0] == '_' && line[1] != 'S') || line[0] == '/' || line[0] == '#')); finished = true; } if (finished == true) break; } - fs.close(); + fclose(in); + std::string title2; - File::OpenCPPFile(fs, engine_->CheatFilename(), std::ios::in); - getline(fs, title2); - fs.close(); - File::OpenCPPFile(fs, engine_->CheatFilename(), std::ios::out | std::ios::app); + // Hmm, this probably gets confused about BOMs? + FILE *inTitle2 = File::OpenCFile(engine_->CheatFilename(), "rt"); + if (inTitle2) { + char temp[2048]; + char *line = GetLineNoNewline(temp, sizeof(temp), inTitle2); + if (line) + title2 = line; + fclose(inTitle2); + } + + FILE *append = File::OpenCFile(engine_->CheatFilename(), "at"); + if (!append) + return UI::EVENT_SKIPPED; auto it = title.begin(); if (((title2[0] == '_' && title2[1] != 'S') || title2[0] == '/' || title2[0] == '#') && it != title.end() && (++it) != title.end()) { - fs << title[0] << "\n" << title[1]; + fprintf(append, "%s\n%s", title[0].c_str(), title[1].c_str()); } NOTICE_LOG(COMMON, "Imported %u entries from %s.\n", (int)newList.size(), cheatFile.c_str()); if (newList.size() != 0) { - fs << "\n"; + fputc('\n', append); } for (int i = 0; i < (int)newList.size(); i++) { - fs << newList[i]; + fprintf(append, "%s", newList[i].c_str()); if (i < (int)newList.size() - 1) { - fs << "\n"; + fputc('\n', append); } } - fs.close(); + fclose(append); } UI::EventReturn CwCheatScreen::OnImportCheat(UI::EventParams ¶ms) { - if (gameID_.length() != 9) { + if (gameID_.length() != 9 || !engine_) { WARN_LOG(COMMON, "CWCHEAT: Incorrect ID(%s) - can't import cheats.", gameID_.c_str()); return UI::EVENT_DONE; } @@ -264,10 +288,10 @@ UI::EventReturn CwCheatScreen::OnImportCheat(UI::EventParams ¶ms) { std::vector title; std::vector newList; - std::string cheatFile = GetSysDirectory(DIRECTORY_CHEATS) + "cheat.db"; + Path cheatFile = GetSysDirectory(DIRECTORY_CHEATS) + "cheat.db"; ParseCheatDB(line, title, newList, cheatFile); const std::string path = File::GetExeDirectory(); - std::string patchesFile = path + "assets/patches/cheat.db"; + Path patchesFile = path + "assets/patches/cheat.db"; ParseCheatDB(line, title, newList, patchesFile); g_Config.bReloadCheats = true; @@ -286,19 +310,23 @@ UI::EventReturn CwCheatScreen::OnCheckBox(int index) { } bool CwCheatScreen::RebuildCheatFile(int index) { - std::fstream fs; - if (!engine_ || !File::OpenCPPFile(fs, engine_->CheatFilename(), std::ios::in)) { + if (!engine_) + return false; + FILE *in = File::OpenCFile(engine_->CheatFilename(), "rt"); + if (!in) return false; - } // In case lines were edited while we weren't looking, reload them. std::vector lines; - for (; fs && !fs.eof(); ) { - std::string line; - std::getline(fs, line, '\n'); + for (; !feof(in); ) { + char temp[2048]; + char *line = GetLineNoNewline(temp, sizeof(temp), in); + if (!line) + break; + lines.push_back(line); } - fs.close(); + fclose(in); auto updateLine = [&](const CheatFileInfo &info) { // Line numbers start with one, not zero. @@ -331,17 +359,17 @@ bool CwCheatScreen::RebuildCheatFile(int index) { } } - - if (!File::OpenCPPFile(fs, engine_->CheatFilename(), std::ios::out | std::ios::trunc)) { + FILE *out = File::OpenCFile(engine_->CheatFilename(), "wt"); + if (!out) { return false; } for (int i = 0; i < lines.size(); ++i) { - fs << lines[i]; - if (i != lines.size()-1) - fs << '\n'; + fprintf(out, "%s", lines[i].c_str()); + if (i != lines.size() - 1) + fputc('\n', out); } - fs.close(); + fclose(out); // Cheats will need to be reparsed now. g_Config.bReloadCheats = true; diff --git a/UI/CwCheatScreen.h b/UI/CwCheatScreen.h index dd545da301..8d847d6f3d 100644 --- a/UI/CwCheatScreen.h +++ b/UI/CwCheatScreen.h @@ -28,7 +28,7 @@ class CWCheatEngine; class CwCheatScreen : public UIDialogScreenWithBackground { public: - CwCheatScreen(const std::string &gamePath); + CwCheatScreen(const Path &gamePath); ~CwCheatScreen(); void LoadCheatInfo(); @@ -53,7 +53,7 @@ private: UI::ScrollView *rightScroll_ = nullptr; CWCheatEngine *engine_ = nullptr; std::vector fileInfo_; - std::string gamePath_; + Path gamePath_; std::string gameID_; int fileCheckCounter_ = 0; uint64_t fileCheckHash_; diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 96740b5789..0fb6b27ee2 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -589,10 +589,10 @@ void SystemInfoScreen::CreateViews() { storage->Add(new ItemHeader(si->T("Directories"))); // Intentionally non-translated - storage->Add(new InfoItem("MemStickDirectory", g_Config.memStickDirectory)); - storage->Add(new InfoItem("InternalDataDirectory", g_Config.internalDataDirectory)); - storage->Add(new InfoItem("AppCacheDir", g_Config.appCacheDirectory)); - storage->Add(new InfoItem("ExtStorageDir", g_Config.externalDirectory)); + storage->Add(new InfoItem("MemStickDirectory", g_Config.memStickDirectory.ToVisualString())); + storage->Add(new InfoItem("InternalDataDirectory", g_Config.internalDataDirectory.ToVisualString())); + storage->Add(new InfoItem("AppCacheDir", g_Config.appCacheDirectory.ToVisualString())); + storage->Add(new InfoItem("ExtStorageDir", g_Config.externalDirectory.ToVisualString())); #if PPSSPP_PLATFORM(ANDROID) storage->Add(new InfoItem("ExtFilesDir", g_extFilesDir)); @@ -1184,7 +1184,7 @@ UI::EventReturn FrameDumpTestScreen::OnLoadDump(UI::EventParams ¶ms) { // Our disc streaming functionality detects the URL and takes over and handles loading framedumps well, // except for some reason the game ID. // TODO: Fix that since it can be important for compat settings. - LaunchFile(screenManager(), url); + LaunchFile(screenManager(), Path(url)); return UI::EVENT_DONE; } @@ -1192,7 +1192,7 @@ void FrameDumpTestScreen::update() { UIScreen::update(); if (!listing_) { - listing_ = g_DownloadManager.StartDownload(framedumpsBaseUrl, ""); + listing_ = g_DownloadManager.StartDownload(framedumpsBaseUrl, Path()); } if (listing_ && listing_->Done() && files_.empty()) { diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 703e4626d9..2aef0ca6f7 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -47,6 +47,7 @@ #include "Core/CoreTiming.h" #include "Core/CoreParameter.h" #include "Core/Core.h" +#include "Core/CwCheat.h" #include "Core/Host.h" #include "Core/KeyMap.h" #include "Core/MemFault.h" @@ -66,6 +67,7 @@ #include "Core/MIPS/MIPS.h" #include "Core/HLE/__sceAudio.h" #include "Core/HLE/proAdhoc.h" +#include "Core/HLE/Plugins.h" #include "UI/BackgroundAudio.h" #include "UI/OnScreenDisplay.h" @@ -127,7 +129,7 @@ static void __EmuScreenVblank() #endif } -EmuScreen::EmuScreen(const std::string &filename) +EmuScreen::EmuScreen(const Path &filename) : bootPending_(true), gamePath_(filename), invalid_(true), quit_(false), pauseTrigger_(false), saveStatePreviewShownTime_(0.0), saveStatePreview_(nullptr) { memset(axisState_, 0, sizeof(axisState_)); saveStateSlot_ = SaveState::GetCurrentSlot(); @@ -145,10 +147,11 @@ EmuScreen::EmuScreen(const std::string &filename) OnChatMenu.Handle(this, &EmuScreen::OnChat); } -bool EmuScreen::bootAllowStorage(const std::string &filename) { +bool EmuScreen::bootAllowStorage(const Path &filename) { // No permissions needed. The easy life. - if (filename.find("http://") == 0 || filename.find("https://") == 0) + if (filename.Type() == PathType::HTTP) return true; + if (!System_GetPropertyBool(SYSPROP_SUPPORTS_PERMISSIONS)) return true; @@ -176,7 +179,7 @@ bool EmuScreen::bootAllowStorage(const std::string &filename) { return false; } -void EmuScreen::bootGame(const std::string &filename) { +void EmuScreen::bootGame(const Path &filename) { if (PSP_IsIniting()) { std::string error_string; bootPending_ = !PSP_InitUpdate(&error_string); @@ -193,7 +196,7 @@ void EmuScreen::bootGame(const std::string &filename) { return; } - g_BackgroundAudio.SetGame(""); + g_BackgroundAudio.SetGame(Path()); // Check permission status first, in case we came from a shortcut. if (!bootAllowStorage(filename)) @@ -244,8 +247,8 @@ void EmuScreen::bootGame(const std::string &filename) { coreParam.graphicsContext = PSP_CoreParameter().graphicsContext; coreParam.enableSound = g_Config.bEnableSound; coreParam.fileToStart = filename; - coreParam.mountIso = ""; - coreParam.mountRoot = ""; + coreParam.mountIso.clear(); + coreParam.mountRoot.clear(); coreParam.startBreak = !g_Config.bAutoRun; coreParam.printfEmuLog = false; coreParam.headLess = false; @@ -411,11 +414,11 @@ void EmuScreen::sendMessage(const char *message, const char *value) { } else if (!strcmp(message, "boot")) { const char *ext = strrchr(value, '.'); if (ext != nullptr && !strcmp(ext, ".ppst")) { - SaveState::Load(value, -1, &AfterStateBoot); + SaveState::Load(Path(value), -1, &AfterStateBoot); } else { PSP_Shutdown(); bootPending_ = true; - gamePath_ = value; + gamePath_ = Path(value); // Don't leave it on CORE_POWERDOWN, we'll sometimes aggressively bail. Core_UpdateState(CORE_POWERUP); } @@ -595,27 +598,27 @@ void EmuScreen::onVKeyDown(int virtualKeyCode) { case VIRTKEY_AXIS_X_MIN: case VIRTKEY_AXIS_X_MAX: - setVKeyAnalogX(CTRL_STICK_LEFT, VIRTKEY_AXIS_X_MIN, VIRTKEY_AXIS_X_MAX); + setVKeyAnalog('X', CTRL_STICK_LEFT, VIRTKEY_AXIS_X_MIN, VIRTKEY_AXIS_X_MAX); break; case VIRTKEY_AXIS_Y_MIN: case VIRTKEY_AXIS_Y_MAX: - setVKeyAnalogY(CTRL_STICK_LEFT, VIRTKEY_AXIS_Y_MIN, VIRTKEY_AXIS_Y_MAX); + setVKeyAnalog('Y', CTRL_STICK_LEFT, VIRTKEY_AXIS_Y_MIN, VIRTKEY_AXIS_Y_MAX); break; case VIRTKEY_AXIS_RIGHT_X_MIN: case VIRTKEY_AXIS_RIGHT_X_MAX: - setVKeyAnalogX(CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_X_MIN, VIRTKEY_AXIS_RIGHT_X_MAX); + setVKeyAnalog('X', CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_X_MIN, VIRTKEY_AXIS_RIGHT_X_MAX); break; case VIRTKEY_AXIS_RIGHT_Y_MIN: case VIRTKEY_AXIS_RIGHT_Y_MAX: - setVKeyAnalogY(CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_Y_MIN, VIRTKEY_AXIS_RIGHT_Y_MAX); + setVKeyAnalog('Y', CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_Y_MIN, VIRTKEY_AXIS_RIGHT_Y_MAX); break; case VIRTKEY_ANALOG_LIGHTLY: - setVKeyAnalogX(CTRL_STICK_LEFT, VIRTKEY_AXIS_X_MIN, VIRTKEY_AXIS_X_MAX); - setVKeyAnalogY(CTRL_STICK_LEFT, VIRTKEY_AXIS_Y_MIN, VIRTKEY_AXIS_Y_MAX); - setVKeyAnalogX(CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_X_MIN, VIRTKEY_AXIS_RIGHT_X_MAX); - setVKeyAnalogY(CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_Y_MIN, VIRTKEY_AXIS_RIGHT_Y_MAX); + setVKeyAnalog('X', CTRL_STICK_LEFT, VIRTKEY_AXIS_X_MIN, VIRTKEY_AXIS_X_MAX, false); + setVKeyAnalog('Y', CTRL_STICK_LEFT, VIRTKEY_AXIS_Y_MIN, VIRTKEY_AXIS_Y_MAX, false); + setVKeyAnalog('X', CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_X_MIN, VIRTKEY_AXIS_RIGHT_X_MAX, false); + setVKeyAnalog('Y', CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_Y_MIN, VIRTKEY_AXIS_RIGHT_Y_MAX, false); break; case VIRTKEY_REWIND: @@ -700,27 +703,27 @@ void EmuScreen::onVKeyUp(int virtualKeyCode) { case VIRTKEY_AXIS_X_MIN: case VIRTKEY_AXIS_X_MAX: - setVKeyAnalogX(CTRL_STICK_LEFT, VIRTKEY_AXIS_X_MIN, VIRTKEY_AXIS_X_MAX); + setVKeyAnalog('X', CTRL_STICK_LEFT, VIRTKEY_AXIS_X_MIN, VIRTKEY_AXIS_X_MAX); break; case VIRTKEY_AXIS_Y_MIN: case VIRTKEY_AXIS_Y_MAX: - setVKeyAnalogY(CTRL_STICK_LEFT, VIRTKEY_AXIS_Y_MIN, VIRTKEY_AXIS_Y_MAX); + setVKeyAnalog('Y', CTRL_STICK_LEFT, VIRTKEY_AXIS_Y_MIN, VIRTKEY_AXIS_Y_MAX); break; case VIRTKEY_AXIS_RIGHT_X_MIN: case VIRTKEY_AXIS_RIGHT_X_MAX: - setVKeyAnalogX(CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_X_MIN, VIRTKEY_AXIS_RIGHT_X_MAX); + setVKeyAnalog('X', CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_X_MIN, VIRTKEY_AXIS_RIGHT_X_MAX); break; case VIRTKEY_AXIS_RIGHT_Y_MIN: case VIRTKEY_AXIS_RIGHT_Y_MAX: - setVKeyAnalogY(CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_Y_MIN, VIRTKEY_AXIS_RIGHT_Y_MAX); + setVKeyAnalog('Y', CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_Y_MIN, VIRTKEY_AXIS_RIGHT_Y_MAX); break; case VIRTKEY_ANALOG_LIGHTLY: - setVKeyAnalogX(CTRL_STICK_LEFT, VIRTKEY_AXIS_X_MIN, VIRTKEY_AXIS_X_MAX); - setVKeyAnalogY(CTRL_STICK_LEFT, VIRTKEY_AXIS_Y_MIN, VIRTKEY_AXIS_Y_MAX); - setVKeyAnalogX(CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_X_MIN, VIRTKEY_AXIS_RIGHT_X_MAX); - setVKeyAnalogY(CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_Y_MIN, VIRTKEY_AXIS_RIGHT_Y_MAX); + setVKeyAnalog('X', CTRL_STICK_LEFT, VIRTKEY_AXIS_X_MIN, VIRTKEY_AXIS_X_MAX, false); + setVKeyAnalog('Y', CTRL_STICK_LEFT, VIRTKEY_AXIS_Y_MIN, VIRTKEY_AXIS_Y_MAX, false); + setVKeyAnalog('X', CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_X_MIN, VIRTKEY_AXIS_RIGHT_X_MAX, false); + setVKeyAnalog('Y', CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_Y_MIN, VIRTKEY_AXIS_RIGHT_Y_MAX, false); break; case VIRTKEY_RAPID_FIRE: @@ -770,25 +773,19 @@ static void SetPSPAxis(char axis, float value, int stick) { __CtrlSetAnalogY(value, stick); } -inline void EmuScreen::setVKeyAnalogX(int stick, int virtualKeyMin, int virtualKeyMax) { - const float value = virtKeys[VIRTKEY_ANALOG_LIGHTLY - VIRTKEY_FIRST] ? g_Config.fAnalogLimiterDeadzone : 1.0f; - float axis = 0.0f; +inline void EmuScreen::setVKeyAnalog(char axis, int stick, int virtualKeyMin, int virtualKeyMax, bool setZero) { // The down events can repeat, so just trust the virtKeys array. - if (virtKeys[virtualKeyMin - VIRTKEY_FIRST]) - axis -= value; - if (virtKeys[virtualKeyMax - VIRTKEY_FIRST]) - axis += value; - SetPSPAxis('X', axis, stick); -} + bool minDown = virtKeys[virtualKeyMin - VIRTKEY_FIRST]; + bool maxDown = virtKeys[virtualKeyMax - VIRTKEY_FIRST]; -inline void EmuScreen::setVKeyAnalogY(int stick, int virtualKeyMin, int virtualKeyMax) { - const float value = virtKeys[VIRTKEY_ANALOG_LIGHTLY - VIRTKEY_FIRST] ? g_Config.fAnalogLimiterDeadzone : 1.0f; - float axis = 0.0f; - if (virtKeys[virtualKeyMin - VIRTKEY_FIRST]) - axis -= value; - if (virtKeys[virtualKeyMax - VIRTKEY_FIRST]) - axis += value; - SetPSPAxis('Y', axis, stick); + const float scale = virtKeys[VIRTKEY_ANALOG_LIGHTLY - VIRTKEY_FIRST] ? g_Config.fAnalogLimiterDeadzone : 1.0f; + float value = 0.0f; + if (minDown) + value -= scale; + if (maxDown) + value += scale; + if (setZero || minDown || maxDown) + SetPSPAxis(axis, value, stick); } bool EmuScreen::key(const KeyInput &key) { @@ -927,36 +924,38 @@ void EmuScreen::processAxis(const AxisInput &axis, int direction) { return; } + const float scale = virtKeys[VIRTKEY_ANALOG_LIGHTLY - VIRTKEY_FIRST] ? g_Config.fAnalogLimiterDeadzone : 1.0f; + std::vector results; KeyMap::AxisToPspButton(axis.deviceId, axis.axisId, direction, &results); - for (size_t i = 0; i < results.size(); i++) { - int result = results[i]; + for (int result : results) { + float value = fabs(axis.value) * scale; switch (result) { case VIRTKEY_AXIS_X_MIN: - SetPSPAxis('X', -fabs(axis.value), CTRL_STICK_LEFT); + SetPSPAxis('X', -value, CTRL_STICK_LEFT); break; case VIRTKEY_AXIS_X_MAX: - SetPSPAxis('X', fabs(axis.value), CTRL_STICK_LEFT); + SetPSPAxis('X', value, CTRL_STICK_LEFT); break; case VIRTKEY_AXIS_Y_MIN: - SetPSPAxis('Y', -fabs(axis.value), CTRL_STICK_LEFT); + SetPSPAxis('Y', -value, CTRL_STICK_LEFT); break; case VIRTKEY_AXIS_Y_MAX: - SetPSPAxis('Y', fabs(axis.value), CTRL_STICK_LEFT); + SetPSPAxis('Y', value, CTRL_STICK_LEFT); break; case VIRTKEY_AXIS_RIGHT_X_MIN: - SetPSPAxis('X', -fabs(axis.value), CTRL_STICK_RIGHT); + SetPSPAxis('X', -value, CTRL_STICK_RIGHT); break; case VIRTKEY_AXIS_RIGHT_X_MAX: - SetPSPAxis('X', fabs(axis.value), CTRL_STICK_RIGHT); + SetPSPAxis('X', value, CTRL_STICK_RIGHT); break; case VIRTKEY_AXIS_RIGHT_Y_MIN: - SetPSPAxis('Y', -fabs(axis.value), CTRL_STICK_RIGHT); + SetPSPAxis('Y', -value, CTRL_STICK_RIGHT); break; case VIRTKEY_AXIS_RIGHT_Y_MAX: - SetPSPAxis('Y', fabs(axis.value), CTRL_STICK_RIGHT); + SetPSPAxis('Y', value, CTRL_STICK_RIGHT); break; } } @@ -1002,7 +1001,7 @@ void EmuScreen::processAxis(const AxisInput &axis, int direction) { class GameInfoBGView : public UI::InertView { public: - GameInfoBGView(const std::string &gamePath, UI::LayoutParams *layoutParams) : InertView(layoutParams), gamePath_(gamePath) { + GameInfoBGView(const Path &gamePath, UI::LayoutParams *layoutParams) : InertView(layoutParams), gamePath_(gamePath) { } void Draw(UIContext &dc) override { @@ -1034,7 +1033,7 @@ public: } protected: - std::string gamePath_; + Path gamePath_; uint32_t color_ = 0xFFC0C0C0; }; @@ -1099,7 +1098,7 @@ void EmuScreen::CreateViews() { chatButton_ = nullptr; } - saveStatePreview_ = new AsyncImageFileView("", IS_FIXED, nullptr, new AnchorLayoutParams(bounds.centerX(), 100, NONE, NONE, true)); + saveStatePreview_ = new AsyncImageFileView(Path(), IS_FIXED, new AnchorLayoutParams(bounds.centerX(), 100, NONE, NONE, true)); saveStatePreview_->SetFixedSize(160, 90); saveStatePreview_->SetColor(0x90FFFFFF); saveStatePreview_->SetVisibility(V_GONE); @@ -1213,7 +1212,7 @@ void EmuScreen::update() { if (errorMessage_.size()) { auto err = GetI18NCategory("Error"); - std::string errLoadingFile = gamePath_ + "\n"; + std::string errLoadingFile = gamePath_.ToVisualString() + "\n"; errLoadingFile.append(err->T("Error loading file", "Could not load game")); errLoadingFile.append(" "); errLoadingFile.append(err->T(errorMessage_.c_str())); @@ -1249,7 +1248,7 @@ void EmuScreen::update() { if (saveStateSlot_ != currentSlot) { saveStateSlot_ = currentSlot; - std::string fn; + Path fn; if (SaveState::HasSaveInSlot(gamePath_, currentSlot)) { fn = SaveState::GenerateSaveSlotFilename(gamePath_, currentSlot, SaveState::SCREENSHOT_EXTENSION); } @@ -1398,10 +1397,12 @@ BREAK x += columnWidth + 10; y = 50; snprintf(statbuf, sizeof(statbuf), - "CPU Core: %s\n" - "Locked CPU freq: %d MHz\n", - CPUCoreAsString(g_Config.iCpuCore), - g_Config.iLockedCPUSpeed); + "CPU Core: %s (flags: %08x)\n" + "Locked CPU freq: %d MHz\n" + "Cheats: %s, Plugins: %s\n", + CPUCoreAsString(g_Config.iCpuCore), g_Config.uJitDisableFlags, + g_Config.iLockedCPUSpeed, + CheatsInEffect() ? "Y" : "N", HLEPlugins::HasEnabled() ? "Y" : "N"); ctx->Draw()->DrawTextShadow(ubuntu24, statbuf, x, y, 0xFFFFFFFF); } @@ -1560,7 +1561,9 @@ void EmuScreen::render() { const ExceptionInfo &info = Core_GetExceptionInfo(); if (info.type != ExceptionType::NONE) { // Clear to blue background screen - thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE, 0xFF900000 }, "EmuScreen_RuntimeError"); + bool dangerousSettings = !Reporting::IsSupported(); + uint32_t color = dangerousSettings ? 0xFF900050 : 0xFF900000; + thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE, color }, "EmuScreen_RuntimeError"); // The info is drawn later in renderUI } else { // If we're stepping, it's convenient not to clear the screen entirely, so we copy display to output. diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index afe95bde63..df2adf9d48 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -17,10 +17,11 @@ #pragma once +#include #include #include -#include +#include "Common/File/Path.h" #include "Common/Input/KeyCodes.h" #include "Common/UI/Screen.h" #include "Common/UI/UIScreen.h" @@ -34,7 +35,7 @@ class OnScreenMessagesView; class EmuScreen : public UIScreen { public: - EmuScreen(const std::string &filename); + EmuScreen(const Path &filename); ~EmuScreen(); void update() override; @@ -56,8 +57,8 @@ private: UI::EventReturn OnChat(UI::EventParams ¶ms); UI::EventReturn OnResume(UI::EventParams ¶ms); - void bootGame(const std::string &filename); - bool bootAllowStorage(const std::string &filename); + void bootGame(const Path &filename); + bool bootAllowStorage(const Path &filename); void bootComplete(); bool hasVisibleUI(); void renderUI(); @@ -66,8 +67,7 @@ private: void pspKey(int pspKeyCode, int flags); void onVKeyDown(int virtualKeyCode); void onVKeyUp(int virtualKeyCode); - void setVKeyAnalogX(int stick, int virtualKeyMin, int virtualKeyMax); - void setVKeyAnalogY(int stick, int virtualKeyMin, int virtualKeyMax); + void setVKeyAnalog(char axis, int stick, int virtualKeyMin, int virtualKeyMax, bool setZero = true); void autoLoad(); void checkPowerDown(); @@ -75,7 +75,7 @@ private: UI::Event OnDevMenu; UI::Event OnChatMenu; bool bootPending_; - std::string gamePath_; + Path gamePath_; // Something invalid was loaded, don't try to emulate bool invalid_; diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index 3cea4ebcdd..64e95df69e 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -26,6 +26,7 @@ #include "Common/Thread/PrioritizedWorkQueue.h" #include "Common/File/VFS/VFS.h" #include "Common/File/FileUtil.h" +#include "Common/File/Path.h" #include "Common/StringUtils.h" #include "Common/TimeUtil.h" #include "Core/FileSystems/ISOFileSystem.h" @@ -61,16 +62,16 @@ bool GameInfo::Delete() { case IdentifiedFileType::PSP_ISO_NP: { // Just delete the one file (TODO: handle two-disk games as well somehow). - const char *fileToRemove = filePath_.c_str(); + Path fileToRemove = filePath_; File::Delete(fileToRemove); - g_Config.RemoveRecent(filePath_); + g_Config.RemoveRecent(filePath_.ToString()); return true; } case IdentifiedFileType::PSP_PBP_DIRECTORY: case IdentifiedFileType::PSP_SAVEDATA_DIRECTORY: { // TODO: This could be handled by Core/Util/GameManager too somehow. - std::string directoryToRemove = ResolvePBPDirectory(filePath_); + Path directoryToRemove = ResolvePBPDirectory(filePath_); INFO_LOG(SYSTEM, "Deleting %s", directoryToRemove.c_str()); if (!File::DeleteDirRecursively(directoryToRemove)) { ERROR_LOG(SYSTEM, "Failed to delete file"); @@ -87,17 +88,17 @@ bool GameInfo::Delete() { case IdentifiedFileType::ARCHIVE_7Z: case IdentifiedFileType::PPSSPP_GE_DUMP: { - const std::string &fileToRemove = filePath_; + const Path &fileToRemove = filePath_; File::Delete(fileToRemove); - g_Config.RemoveRecent(filePath_); + g_Config.RemoveRecent(filePath_.ToString()); return true; } case IdentifiedFileType::PPSSPP_SAVESTATE: { - const std::string &ppstPath = filePath_; + const Path &ppstPath = filePath_; File::Delete(ppstPath); - const std::string screenshotPath = ReplaceAll(filePath_, ".ppst", ".jpg"); + const Path screenshotPath = filePath_.WithReplacedExtension(".ppst", ".jpg"); if (File::Exists(screenshotPath)) { File::Delete(screenshotPath); } @@ -113,7 +114,7 @@ u64 GameInfo::GetGameSizeInBytes() { switch (fileType) { case IdentifiedFileType::PSP_PBP_DIRECTORY: case IdentifiedFileType::PSP_SAVEDATA_DIRECTORY: - return getDirectoryRecursiveSize(ResolvePBPDirectory(filePath_), nullptr, GETFILES_GETHIDDEN); + return File::GetDirectoryRecursiveSize(ResolvePBPDirectory(filePath_), nullptr, File::GETFILES_GETHIDDEN); default: return GetFileLoader()->FileSize(); @@ -121,13 +122,13 @@ u64 GameInfo::GetGameSizeInBytes() { } // Not too meaningful if the object itself is a savedata directory... -std::vector GameInfo::GetSaveDataDirectories() { - std::string memc = GetSysDirectory(DIRECTORY_SAVEDATA); +std::vector GameInfo::GetSaveDataDirectories() { + Path memc = GetSysDirectory(DIRECTORY_SAVEDATA); - std::vector dirs; - getFilesInDir(memc.c_str(), &dirs); + std::vector dirs; + File::GetFilesInDir(memc, &dirs); - std::vector directories; + std::vector directories; if (id.size() < 5) { return directories; } @@ -144,17 +145,17 @@ u64 GameInfo::GetSaveDataSizeInBytes() { if (fileType == IdentifiedFileType::PSP_SAVEDATA_DIRECTORY || fileType == IdentifiedFileType::PPSSPP_SAVESTATE) { return 0; } - std::vector saveDataDir = GetSaveDataDirectories(); + std::vector saveDataDir = GetSaveDataDirectories(); u64 totalSize = 0; u64 filesSizeInDir = 0; for (size_t j = 0; j < saveDataDir.size(); j++) { - std::vector fileInfo; - getFilesInDir(saveDataDir[j].c_str(), &fileInfo); - // Note: getFileInDir does not fill in fileSize properly. + std::vector fileInfo; + File::GetFilesInDir(saveDataDir[j], &fileInfo); + // Note: GetFilesInDir does not fill in fileSize properly. for (size_t i = 0; i < fileInfo.size(); i++) { - FileInfo finfo; - getFileInfo(fileInfo[i].fullName.c_str(), &finfo); + File::FileInfo finfo; + File::GetFileInfo(fileInfo[i].fullName, &finfo); if (!finfo.isDirectory) filesSizeInDir += finfo.size; } @@ -171,17 +172,17 @@ u64 GameInfo::GetInstallDataSizeInBytes() { if (fileType == IdentifiedFileType::PSP_SAVEDATA_DIRECTORY || fileType == IdentifiedFileType::PPSSPP_SAVESTATE) { return 0; } - std::vector saveDataDir = GetSaveDataDirectories(); + std::vector saveDataDir = GetSaveDataDirectories(); u64 totalSize = 0; u64 filesSizeInDir = 0; for (size_t j = 0; j < saveDataDir.size(); j++) { - std::vector fileInfo; - getFilesInDir(saveDataDir[j].c_str(), &fileInfo); - // Note: getFileInDir does not fill in fileSize properly. + std::vector fileInfo; + File::GetFilesInDir(saveDataDir[j], &fileInfo); + // Note: GetFilesInDir does not fill in fileSize properly. for (size_t i = 0; i < fileInfo.size(); i++) { - FileInfo finfo; - getFileInfo(fileInfo[i].fullName.c_str(), &finfo); + File::FileInfo finfo; + File::GetFileInfo(fileInfo[i].fullName, &finfo); if (!finfo.isDirectory) filesSizeInDir += finfo.size; } @@ -195,7 +196,7 @@ u64 GameInfo::GetInstallDataSizeInBytes() { return totalSize; } -bool GameInfo::LoadFromPath(const std::string &gamePath) { +bool GameInfo::LoadFromPath(const Path &gamePath) { std::lock_guard guard(lock); // No need to rebuild if we already have it loaded. if (filePath_ != gamePath) { @@ -205,7 +206,7 @@ bool GameInfo::LoadFromPath(const std::string &gamePath) { filePath_ = gamePath; // This is a fallback title, while we're loading / if unable to load. - title = File::GetFilename(filePath_); + title = filePath_.GetFilename(); } return true; @@ -228,16 +229,16 @@ void GameInfo::DisposeFileLoader() { } bool GameInfo::DeleteAllSaveData() { - std::vector saveDataDir = GetSaveDataDirectories(); + std::vector saveDataDir = GetSaveDataDirectories(); for (size_t j = 0; j < saveDataDir.size(); j++) { - std::vector fileInfo; - getFilesInDir(saveDataDir[j].c_str(), &fileInfo); + std::vector fileInfo; + File::GetFilesInDir(saveDataDir[j], &fileInfo); for (size_t i = 0; i < fileInfo.size(); i++) { - File::Delete(fileInfo[i].fullName.c_str()); + File::Delete(fileInfo[i].fullName); } - File::DeleteDir(saveDataDir[j].c_str()); + File::DeleteDir(saveDataDir[j]); } return true; } @@ -332,7 +333,7 @@ static bool ReadVFSToString(const char *filename, std::string *contents, std::mu class GameInfoWorkItem : public PrioritizedWorkQueueItem { public: - GameInfoWorkItem(const std::string &gamePath, std::shared_ptr &info) + GameInfoWorkItem(const Path &gamePath, std::shared_ptr &info) : gamePath_(gamePath), info_(info) { } @@ -359,7 +360,7 @@ public: { auto pbpLoader = info_->GetFileLoader(); if (info_->fileType == IdentifiedFileType::PSP_PBP_DIRECTORY) { - std::string ebootPath = ResolvePBPFile(gamePath_); + Path ebootPath = ResolvePBPFile(gamePath_); if (ebootPath != gamePath_) { pbpLoader.reset(ConstructFileLoader(ebootPath)); } @@ -370,7 +371,7 @@ public: if (pbp.IsELF()) { goto handleELF; } - ERROR_LOG(LOADER, "invalid pbp %s\n", pbpLoader->Path().c_str()); + ERROR_LOG(LOADER, "invalid pbp '%s'\n", pbpLoader->GetPath().ToVisualString().c_str()); info_->pending = false; info_->working = false; return; @@ -385,9 +386,9 @@ public: // Assuming PSP_PBP_DIRECTORY without ID or with disc_total < 1 in GAME dir must be homebrew if ((info_->id.empty() || !info_->disc_total) - && gamePath_.find("/PSP/GAME/") != std::string::npos + && gamePath_.FilePathContains("PSP/GAME/") && info_->fileType == IdentifiedFileType::PSP_PBP_DIRECTORY) { - info_->id = g_paramSFO.GenerateFakeID(gamePath_); + info_->id = g_paramSFO.GenerateFakeID(gamePath_.ToString()); info_->id_version = info_->id + "_1.00"; info_->region = GAMEREGION_MAX + 1; // Homebrew } @@ -398,13 +399,13 @@ public: std::lock_guard lock(info_->lock); pbp.GetSubFileAsString(PBP_ICON0_PNG, &info_->icon.data); } else { - std::string screenshot_jpg = GetSysDirectory(DIRECTORY_SCREENSHOT) + info_->id + "_00000.jpg"; - std::string screenshot_png = GetSysDirectory(DIRECTORY_SCREENSHOT) + info_->id + "_00000.png"; + Path screenshot_jpg = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.jpg"); + Path screenshot_png = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.png"); // Try using png/jpg screenshots first if (File::Exists(screenshot_png)) - readFileToString(false, screenshot_png.c_str(), info_->icon.data); + File::ReadFileToString(false, screenshot_png, info_->icon.data); else if (File::Exists(screenshot_jpg)) - readFileToString(false, screenshot_jpg.c_str(), info_->icon.data); + File::ReadFileToString(false, screenshot_jpg, info_->icon.data); else // Read standard icon ReadVFSToString("unknown.png", &info_->icon.data, &info_->lock); @@ -438,20 +439,20 @@ handleELF: // An elf on its own has no usable information, no icons, no nothing. { std::lock_guard lock(info_->lock); - info_->id = g_paramSFO.GenerateFakeID(gamePath_); + info_->id = g_paramSFO.GenerateFakeID(gamePath_.ToString()); info_->id_version = info_->id + "_1.00"; info_->region = GAMEREGION_MAX + 1; // Homebrew info_->paramSFOLoaded = true; } { - std::string screenshot_jpg = GetSysDirectory(DIRECTORY_SCREENSHOT) + info_->id + "_00000.jpg"; - std::string screenshot_png = GetSysDirectory(DIRECTORY_SCREENSHOT) + info_->id + "_00000.png"; + Path screenshot_jpg = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.jpg"); + Path screenshot_png = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.png"); // Try using png/jpg screenshots first if (File::Exists(screenshot_png)) { - readFileToString(false, screenshot_png.c_str(), info_->icon.data); + File::ReadFileToString(false, screenshot_png, info_->icon.data); } else if (File::Exists(screenshot_jpg)) { - readFileToString(false, screenshot_jpg.c_str(), info_->icon.data); + File::ReadFileToString(false, screenshot_jpg, info_->icon.data); } else { // Read standard icon VERBOSE_LOG(LOADER, "Loading unknown.png because there was an ELF"); @@ -464,7 +465,7 @@ handleELF: case IdentifiedFileType::PSP_SAVEDATA_DIRECTORY: { SequentialHandleAllocator handles; - VirtualDiscFileSystem umd(&handles, gamePath_.c_str()); + VirtualDiscFileSystem umd(&handles, gamePath_); // Alright, let's fetch the PARAM.SFO. std::string paramSFOcontents; @@ -490,9 +491,9 @@ handleELF: std::lock_guard guard(info_->lock); // Let's use the screenshot as an icon, too. - std::string screenshotPath = ReplaceAll(gamePath_, ".ppst", ".jpg"); + Path screenshotPath = gamePath_.WithReplacedExtension(".ppst", ".jpg"); if (File::Exists(screenshotPath)) { - if (readFileToString(false, screenshotPath.c_str(), info_->icon.data)) { + if (File::ReadFileToString(false, screenshotPath, info_->icon.data)) { info_->icon.dataLoaded = true; } else { ERROR_LOG(G3D, "Error loading screenshot data: '%s'", screenshotPath.c_str()); @@ -505,7 +506,7 @@ handleELF: { info_->fileType = IdentifiedFileType::PSP_ISO; SequentialHandleAllocator handles; - VirtualDiscFileSystem umd(&handles, gamePath_.c_str()); + VirtualDiscFileSystem umd(&handles, gamePath_); // Alright, let's fetch the PARAM.SFO. std::string paramSFOcontents; @@ -573,13 +574,13 @@ handleELF: // Fall back to unknown icon if ISO is broken/is a homebrew ISO, override is allowed though if (!ReadFileToString(&umd, "/PSP_GAME/ICON0.PNG", &info_->icon.data, &info_->lock)) { - std::string screenshot_jpg = GetSysDirectory(DIRECTORY_SCREENSHOT) + info_->id + "_00000.jpg"; - std::string screenshot_png = GetSysDirectory(DIRECTORY_SCREENSHOT) + info_->id + "_00000.png"; + Path screenshot_jpg = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.jpg"); + Path screenshot_png = GetSysDirectory(DIRECTORY_SCREENSHOT) / (info_->id + "_00000.png"); // Try using png/jpg screenshots first if (File::Exists(screenshot_png)) - readFileToString(false, screenshot_png.c_str(), info_->icon.data); + File::ReadFileToString(false, screenshot_png, info_->icon.data); else if (File::Exists(screenshot_jpg)) - readFileToString(false, screenshot_jpg.c_str(), info_->icon.data); + File::ReadFileToString(false, screenshot_jpg, info_->icon.data); else { DEBUG_LOG(LOADER, "Loading unknown.png because no icon was found"); ReadVFSToString("unknown.png", &info_->icon.data, &info_->lock); @@ -643,7 +644,7 @@ handleELF: } private: - std::string gamePath_; + Path gamePath_; std::shared_ptr info_; DISALLOW_COPY_AND_ASSIGN(GameInfoWorkItem); }; @@ -732,10 +733,12 @@ void GameInfoCache::WaitUntilDone(std::shared_ptr &info) { // Runs on the main thread. Only call from render() and similar, not update()! // Can also be called from the audio thread for menu background music. -std::shared_ptr GameInfoCache::GetInfo(Draw::DrawContext *draw, const std::string &gamePath, int wantFlags) { +std::shared_ptr GameInfoCache::GetInfo(Draw::DrawContext *draw, const Path &gamePath, int wantFlags) { std::shared_ptr info; - auto iter = info_.find(gamePath); + std::string pathStr = gamePath.ToString(); + + auto iter = info_.find(pathStr); if (iter != info_.end()) { info = iter->second; } @@ -776,8 +779,8 @@ std::shared_ptr GameInfoCache::GetInfo(Draw::DrawContext *draw, const gameInfoWQ_->Add(item); // Don't re-insert if we already have it. - if (info_.find(gamePath) == info_.end()) - info_[gamePath] = std::shared_ptr(info); + if (info_.find(pathStr) == info_.end()) + info_[pathStr] = std::shared_ptr(info); return info; } diff --git a/UI/GameInfoCache.h b/UI/GameInfoCache.h index 8563d111f9..f2abd58c7d 100644 --- a/UI/GameInfoCache.h +++ b/UI/GameInfoCache.h @@ -24,6 +24,7 @@ #include #include "Core/ELF/ParamSFO.h" +#include "Common/File/Path.h" #include "UI/TextureUtil.h" namespace Draw { @@ -84,7 +85,7 @@ public: bool Delete(); // Better be sure what you're doing when calling this. bool DeleteAllSaveData(); - bool LoadFromPath(const std::string &gamePath); + bool LoadFromPath(const Path &gamePath); std::shared_ptr GetFileLoader(); void DisposeFileLoader(); @@ -95,11 +96,19 @@ public: void ParseParamSFO(); - std::vector GetSaveDataDirectories(); + std::vector GetSaveDataDirectories(); std::string GetTitle(); void SetTitle(const std::string &newTitle); + GameInfoTex *GetBGPic() { + if (pic1.texture) + return &pic1; + if (pic0.texture) + return &pic0; + return nullptr; + } + // Hold this when reading or writing from the GameInfo. // Don't need to hold it when just passing around the pointer, // and obviously also not when creating it and holding the only pointer @@ -140,7 +149,7 @@ protected: // TODO: Get rid of this shared_ptr and managae lifetime better instead. std::shared_ptr fileLoader; - std::string filePath_; + Path filePath_; private: DISALLOW_COPY_AND_ASSIGN(GameInfo); @@ -159,11 +168,9 @@ public: // but filled in later asynchronously in the background. So keep calling this, // redrawing the UI often. Only set flags to GAMEINFO_WANTBG or WANTSND if you really want them // because they're big. bgTextures and sound may be discarded over time as well. - std::shared_ptr GetInfo(Draw::DrawContext *draw, const std::string &gamePath, int wantFlags); + std::shared_ptr GetInfo(Draw::DrawContext *draw, const Path &gamePath, int wantFlags); void FlushBGs(); // Gets rid of all BG textures. Also gets rid of bg sounds. - PrioritizedWorkQueue *WorkQueue() { return gameInfoWQ_; } - void CancelAll(); void WaitUntilDone(std::shared_ptr &info); diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index b42b80abf4..a9fdffa0b4 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -43,7 +43,7 @@ #include "UI/MainScreen.h" #include "UI/BackgroundAudio.h" -GameScreen::GameScreen(const std::string &gamePath) : UIDialogScreenWithGameBackground(gamePath) { +GameScreen::GameScreen(const Path &gamePath) : UIDialogScreenWithGameBackground(gamePath) { g_BackgroundAudio.SetGame(gamePath); } @@ -84,7 +84,7 @@ void GameScreen::CreateViews() { tvTitle_->SetShadow(true); infoLayout->Add(new Spacer(12)); // This one doesn't need to be updated. - infoLayout->Add(new TextView(gamePath_, ALIGN_LEFT | FLAG_WRAP_TEXT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetShadow(true); + infoLayout->Add(new TextView(gamePath_.ToVisualString(), ALIGN_LEFT | FLAG_WRAP_TEXT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetShadow(true); tvGameSize_ = infoLayout->Add(new TextView("...", ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); tvGameSize_->SetShadow(true); tvSaveDataSize_ = infoLayout->Add(new TextView("...", ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); @@ -292,8 +292,8 @@ UI::EventReturn GameScreen::OnGameSettings(UI::EventParams &e) { std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE); if (info && info->paramSFOLoaded) { std::string discID = info->paramSFO.GetValueString("DISC_ID"); - if ((discID.empty() || !info->disc_total) && gamePath_.find("/PSP/GAME/") != std::string::npos) - discID = g_paramSFO.GenerateFakeID(gamePath_); + if ((discID.empty() || !info->disc_total) && gamePath_.FilePathContains("PSP/GAME/")) + discID = g_paramSFO.GenerateFakeID(gamePath_.ToString()); screenManager()->push(new GameSettingsScreen(gamePath_, discID, true)); } return UI::EVENT_DONE; @@ -350,16 +350,16 @@ void GameScreen::CallbackDeleteGame(bool yes) { UI::EventReturn GameScreen::OnCreateShortcut(UI::EventParams &e) { std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, 0); if (info) { - host->CreateDesktopShortcut(gamePath_, info->GetTitle()); + host->CreateDesktopShortcut(gamePath_.ToString(), info->GetTitle()); } return UI::EVENT_DONE; } -bool GameScreen::isRecentGame(const std::string &gamePath) { +bool GameScreen::isRecentGame(const Path &gamePath) { if (g_Config.iMaxRecent <= 0) return false; - const std::string resolved = File::ResolvePath(gamePath); + const std::string resolved = File::ResolvePath(gamePath.ToString()); for (auto it = g_Config.recentIsos.begin(); it != g_Config.recentIsos.end(); ++it) { const std::string recent = File::ResolvePath(*it); if (resolved == recent) @@ -369,14 +369,14 @@ bool GameScreen::isRecentGame(const std::string &gamePath) { } UI::EventReturn GameScreen::OnRemoveFromRecent(UI::EventParams &e) { - g_Config.RemoveRecent(gamePath_); + g_Config.RemoveRecent(gamePath_.ToString()); screenManager()->switchScreen(new MainScreen()); return UI::EVENT_DONE; } class SetBackgroundPopupScreen : public PopupScreen { public: - SetBackgroundPopupScreen(const std::string &title, const std::string &gamePath); + SetBackgroundPopupScreen(const std::string &title, const Path &gamePath); protected: bool FillVertical() const override { return false; } @@ -385,7 +385,7 @@ protected: void update() override; private: - std::string gamePath_; + Path gamePath_; double timeStart_; double timeDone_ = 0.0; @@ -397,7 +397,7 @@ private: Status status_ = Status::PENDING; }; -SetBackgroundPopupScreen::SetBackgroundPopupScreen(const std::string &title, const std::string &gamePath) +SetBackgroundPopupScreen::SetBackgroundPopupScreen(const std::string &title, const Path &gamePath) : PopupScreen(title), gamePath_(gamePath) { timeStart_ = time_now_d(); } @@ -420,8 +420,8 @@ void SetBackgroundPopupScreen::update() { } if (pic) { - const std::string bgPng = GetSysDirectory(DIRECTORY_SYSTEM) + "background.png"; - writeStringToFile(false, pic->data, bgPng.c_str()); + const Path bgPng = GetSysDirectory(DIRECTORY_SYSTEM) / "background.png"; + File::WriteStringToFile(false, pic->data, bgPng); } NativeMessageReceived("bgImage_updated", ""); diff --git a/UI/GameScreen.h b/UI/GameScreen.h index a746250436..dfcaffc74a 100644 --- a/UI/GameScreen.h +++ b/UI/GameScreen.h @@ -21,6 +21,7 @@ #include "UI/MiscScreens.h" #include "Common/UI/UIScreen.h" +#include "Common/File/Path.h" // Game screen: Allows you to start a game, delete saves, delete the game, // set game specific settings, etc. @@ -30,7 +31,7 @@ class GameScreen : public UIDialogScreenWithGameBackground { public: - GameScreen(const std::string &gamePath); + GameScreen(const Path &gamePath); ~GameScreen(); void render() override; @@ -42,7 +43,7 @@ protected: void CallbackDeleteConfig(bool yes); void CallbackDeleteSaveData(bool yes); void CallbackDeleteGame(bool yes); - bool isRecentGame(const std::string &gamePath); + bool isRecentGame(const Path &gamePath); private: UI::Choice *AddOtherChoice(UI::Choice *choice); @@ -75,5 +76,5 @@ private: UI::Choice *btnDeleteSaveData_; UI::Choice *btnSetBackground_; std::vector otherChoices_; - std::vector saveDirs; + std::vector saveDirs; }; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 7665e96e35..3f1514d838 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -18,6 +18,7 @@ #include "ppsspp_config.h" #include +#include #include "Common/Net/Resolve.h" #include "Common/GPU/OpenGL/GLFeatures.h" @@ -78,9 +79,7 @@ #include "Windows/W32Util/ShellUtil.h" #endif -extern bool g_ShaderNameListChanged; - -GameSettingsScreen::GameSettingsScreen(std::string gamePath, std::string gameID, bool editThenRestore) +GameSettingsScreen::GameSettingsScreen(const Path &gamePath, std::string gameID, bool editThenRestore) : UIDialogScreenWithGameBackground(gamePath), gameID_(gameID), enableReports_(false), editThenRestore_(editThenRestore) { lastVertical_ = UseVerticalLayout(); prevInflightFrames_ = g_Config.iInflightFrames; @@ -315,10 +314,10 @@ void GameSettingsScreen::CreateViews() { graphicsSettings->Add(new ItemHeader(gr->T("Postprocessing effect"))); - std::vector alreadyAddedShader; - for (int i = 0; i < g_Config.vPostShaderNames.size() && i < ARRAY_SIZE(shaderNames_); ++i) { + std::set alreadyAddedShader; + for (int i = 0; i < g_Config.vPostShaderNames.size() + 1 && i < ARRAY_SIZE(shaderNames_); ++i) { // Vector element pointer get invalidated on resize, cache name to have always a valid reference in the rendering thread - shaderNames_[i] = g_Config.vPostShaderNames[i]; + shaderNames_[i] = i == g_Config.vPostShaderNames.size() ? "Off" : g_Config.vPostShaderNames[i]; postProcChoice_ = graphicsSettings->Add(new ChoiceWithValueDisplay(&shaderNames_[i], StringFromFormat("%s #%d", gr->T("Postprocessing Shader"), i + 1), &PostShaderTranslateName)); postProcChoice_->OnClick.Add([=](EventParams &e) { auto gr = GetI18NCategory("Graphics"); @@ -333,11 +332,15 @@ void GameSettingsScreen::CreateViews() { return g_Config.iRenderingMode != FB_NON_BUFFERED_MODE; }); + // No need for settings on the last one. + if (i == g_Config.vPostShaderNames.size()) + continue; + auto shaderChain = GetPostShaderChain(g_Config.vPostShaderNames[i]); for (auto shaderInfo : shaderChain) { // Disable duplicated shader slider - bool duplicated = std::find(alreadyAddedShader.begin(), alreadyAddedShader.end(), shaderInfo->section) != alreadyAddedShader.end(); - alreadyAddedShader.push_back(shaderInfo->section); + bool duplicated = alreadyAddedShader.find(shaderInfo->section) != alreadyAddedShader.end(); + alreadyAddedShader.insert(shaderInfo->section); for (size_t i = 0; i < ARRAY_SIZE(shaderInfo->settings); ++i) { auto &setting = shaderInfo->settings[i]; if (!setting.name.empty()) { @@ -893,9 +896,23 @@ void GameSettingsScreen::CreateViews() { tabHolder->AddTab(ms->T("System"), systemSettingsScroll); if (!g_Config.bSimpleUI) { - systemSettings->Add(new ItemHeader(sy->T("UI Language"))); // Should be renamed "UI"? + systemSettings->Add(new ItemHeader(sy->T("UI"))); systemSettings->Add(new Choice(dev->T("Language", "Language")))->OnClick.Handle(this, &GameSettingsScreen::OnLanguage); systemSettings->Add(new CheckBox(&g_Config.bUISound, sy->T("UI Sound"))); + const Path bgPng = GetSysDirectory(DIRECTORY_SYSTEM) / "background.png"; + const Path bgJpg = GetSysDirectory(DIRECTORY_SYSTEM) / "background.jpg"; + if (File::Exists(bgPng) || File::Exists(bgJpg)) { + backgroundChoice_ = systemSettings->Add(new Choice(sy->T("Clear UI background"))); + } else if (System_GetPropertyBool(SYSPROP_HAS_IMAGE_BROWSER)) { + backgroundChoice_ = systemSettings->Add(new Choice(sy->T("Set UI background..."))); + } else { + backgroundChoice_ = nullptr; + } + if (backgroundChoice_ != nullptr) { + backgroundChoice_->OnClick.Handle(this, &GameSettingsScreen::OnChangeBackground); + } + static const char *backgroundAnimations[] = { "No animation", "Floating symbols", "Recent games" }; + systemSettings->Add(new PopupMultiChoice(&g_Config.iBackgroundAnimation, sy->T("UI background animation"), backgroundAnimations, 0, ARRAY_SIZE(backgroundAnimations), sy->GetName(), screenManager())); systemSettings->Add(new ItemHeader(sy->T("Help the PPSSPP team"))); enableReports_ = Reporting::IsEnabled(); @@ -942,18 +959,6 @@ void GameSettingsScreen::CreateViews() { #endif if (!g_Config.bSimpleUI) { systemSettings->Add(new CheckBox(&g_Config.bCheckForNewVersion, sy->T("VersionCheck", "Check for new versions of PPSSPP"))); - const std::string bgPng = GetSysDirectory(DIRECTORY_SYSTEM) + "background.png"; - const std::string bgJpg = GetSysDirectory(DIRECTORY_SYSTEM) + "background.jpg"; - if (File::Exists(bgPng) || File::Exists(bgJpg)) { - backgroundChoice_ = systemSettings->Add(new Choice(sy->T("Clear UI background"))); - } else if (System_GetPropertyBool(SYSPROP_HAS_IMAGE_BROWSER)) { - backgroundChoice_ = systemSettings->Add(new Choice(sy->T("Set UI background..."))); - } else { - backgroundChoice_ = nullptr; - } - if (backgroundChoice_ != nullptr) { - backgroundChoice_->OnClick.Handle(this, &GameSettingsScreen::OnChangeBackground); - } } systemSettings->Add(new Choice(sy->T("Restore Default Settings")))->OnClick.Handle(this, &GameSettingsScreen::OnRestoreDefaultSettings); if (!g_Config.bSimpleUI) { @@ -964,7 +969,8 @@ void GameSettingsScreen::CreateViews() { systemSettings->Add(new CheckBox(&g_Config.bBypassOSKWithKeyboard, sy->T("Use system native keyboard"))); #endif #if PPSSPP_PLATFORM(ANDROID) - auto memstickPath = systemSettings->Add(new ChoiceWithValueDisplay(&g_Config.memStickDirectory, sy->T("Change Memory Stick folder"), (const char *)nullptr)); + memstickDisplay_ = g_Config.memStickDirectory.ToVisualString(); + auto memstickPath = systemSettings->Add(new ChoiceWithValueDisplay(&memstickDisplay_, sy->T("Change Memory Stick folder"), (const char *)nullptr)); memstickPath->SetEnabled(!PSP_IsInited()); memstickPath->OnClick.Handle(this, &GameSettingsScreen::OnChangeMemStickDir); #elif defined(_WIN32) && !PPSSPP_PLATFORM(UWP) @@ -975,14 +981,15 @@ void GameSettingsScreen::CreateViews() { SavePathInOtherChoice->SetEnabled(false); SavePathInOtherChoice->OnClick.Handle(this, &GameSettingsScreen::OnSavePathOther); const bool myDocsExists = W32Util::UserDocumentsPath().size() != 0; - const std::string PPSSPPpath = File::GetExeDirectory(); - const std::string installedFile = PPSSPPpath + "installed.txt"; + + const Path &PPSSPPpath = File::GetExeDirectory(); + const Path installedFile = PPSSPPpath / "installed.txt"; installed_ = File::Exists(installedFile); otherinstalled_ = false; if (!installed_ && myDocsExists) { - if (File::CreateEmptyFile(PPSSPPpath + "installedTEMP.txt")) { + if (File::CreateEmptyFile(PPSSPPpath / "installedTEMP.txt")) { // Disable the setting whether cannot create & delete file - if (!(File::Delete(PPSSPPpath + "installedTEMP.txt"))) + if (!(File::Delete(PPSSPPpath / "installedTEMP.txt"))) SavePathInMyDocumentChoice->SetEnabled(false); else SavePathInOtherChoice->SetEnabled(!PSP_IsInited()); @@ -990,25 +997,21 @@ void GameSettingsScreen::CreateViews() { SavePathInMyDocumentChoice->SetEnabled(false); } else { if (installed_ && myDocsExists) { -#ifdef _MSC_VER - std::ifstream inputFile(ConvertUTF8ToWString(installedFile)); -#else - std::ifstream inputFile(installedFile); -#endif - if (!inputFile.fail() && inputFile.is_open()) { - std::string tempString; - std::getline(inputFile, tempString); - + FILE *testInstalled = File::OpenCFile(installedFile, "rt"); + if (testInstalled) { + char temp[2048]; + char *tempStr = fgets(temp, sizeof(temp), testInstalled); // Skip UTF-8 encoding bytes if there are any. There are 3 of them. - if (tempString.substr(0, 3) == "\xEF\xBB\xBF") - tempString = tempString.substr(3); + if (tempStr && strncmp(tempStr, "\xEF\xBB\xBF", 3) == 0) { + tempStr += 3; + } SavePathInOtherChoice->SetEnabled(!PSP_IsInited()); - if (!(tempString == "")) { + if (tempStr && strlen(tempStr) != 0 && strcmp(tempStr, "\n") != 0) { installed_ = false; otherinstalled_ = true; } + fclose(testInstalled); } - inputFile.close(); } else if (!myDocsExists) { SavePathInMyDocumentChoice->SetEnabled(false); } @@ -1145,7 +1148,7 @@ UI::EventReturn GameSettingsScreen::OnJitAffectingSetting(UI::EventParams &e) { UI::EventReturn GameSettingsScreen::OnChangeMemStickDir(UI::EventParams &e) { auto sy = GetI18NCategory("System"); - System_InputBoxGetString(sy->T("Memory Stick Folder"), g_Config.memStickDirectory, [&](bool result, const std::string &value) { + System_InputBoxGetString(sy->T("Memory Stick Folder"), g_Config.memStickDirectory.ToString(), [&](bool result, const std::string &value) { auto sy = GetI18NCategory("System"); auto di = GetI18NCategory("Dialog"); @@ -1163,7 +1166,7 @@ UI::EventReturn GameSettingsScreen::OnChangeMemStickDir(UI::EventParams &e) { pendingMemstickFolder_ = newPath; std::string promptMessage = sy->T("ChangingMemstickPath", "Save games, save states, and other data will not be copied to this folder.\n\nChange the Memory Stick folder?"); - if (!File::Exists(newPath)) { + if (!File::Exists(Path(newPath))) { promptMessage = sy->T("ChangingMemstickPathNotExists", "That folder doesn't exist yet.\n\nSave games, save states, and other data will not be copied to this folder.\n\nCreate a new Memory Stick folder?"); } // Add the path for clarity and proper confirmation. @@ -1177,41 +1180,40 @@ UI::EventReturn GameSettingsScreen::OnChangeMemStickDir(UI::EventParams &e) { #elif defined(_WIN32) && !PPSSPP_PLATFORM(UWP) UI::EventReturn GameSettingsScreen::OnSavePathMydoc(UI::EventParams &e) { - const std::string PPSSPPpath = File::GetExeDirectory(); - const std::string installedFile = PPSSPPpath + "installed.txt"; + const Path &PPSSPPpath = File::GetExeDirectory(); + const Path installedFile = installedFile / "installed.txt"; installed_ = File::Exists(installedFile); if (otherinstalled_) { - File::Delete(PPSSPPpath + "installed.txt"); - File::CreateEmptyFile(PPSSPPpath + "installed.txt"); + File::Delete(PPSSPPpath / "installed.txt"); + File::CreateEmptyFile(PPSSPPpath / "installed.txt"); otherinstalled_ = false; const std::string myDocsPath = W32Util::UserDocumentsPath() + "/PPSSPP/"; - g_Config.memStickDirectory = myDocsPath; + g_Config.memStickDirectory = Path(myDocsPath); } else if (installed_) { - File::Delete(PPSSPPpath + "installed.txt"); + File::Delete(PPSSPPpath / "installed.txt"); installed_ = false; - g_Config.memStickDirectory = PPSSPPpath + "memstick/"; + g_Config.memStickDirectory = PPSSPPpath / "memstick"; } else { - std::ofstream myfile; - myfile.open(PPSSPPpath + "installed.txt"); - if (myfile.is_open()){ - myfile.close(); + FILE *f = File::OpenCFile(PPSSPPpath / "installed.txt", "wb"); + if (f) { + fclose(f); } const std::string myDocsPath = W32Util::UserDocumentsPath() + "/PPSSPP/"; - g_Config.memStickDirectory = myDocsPath; + g_Config.memStickDirectory = Path(myDocsPath); installed_ = true; } return UI::EVENT_DONE; } UI::EventReturn GameSettingsScreen::OnSavePathOther(UI::EventParams &e) { - const std::string PPSSPPpath = File::GetExeDirectory(); + const Path &PPSSPPpath = File::GetExeDirectory(); if (otherinstalled_) { auto di = GetI18NCategory("Dialog"); std::string folder = W32Util::BrowseForFolder(MainWindow::GetHWND(), di->T("Choose PPSSPP save folder")); if (folder.size()) { - g_Config.memStickDirectory = folder; - FILE *f = File::OpenCFile(PPSSPPpath + "installed.txt", "wb"); + g_Config.memStickDirectory = Path(folder); + FILE *f = File::OpenCFile(PPSSPPpath / "installed.txt", "wb"); if (f) { std::string utfstring("\xEF\xBB\xBF"); utfstring.append(folder); @@ -1224,11 +1226,11 @@ UI::EventReturn GameSettingsScreen::OnSavePathOther(UI::EventParams &e) { otherinstalled_ = false; } else { - File::Delete(PPSSPPpath + "installed.txt"); + File::Delete(PPSSPPpath / "installed.txt"); SavePathInMyDocumentChoice->SetEnabled(true); otherinstalled_ = false; installed_ = false; - g_Config.memStickDirectory = PPSSPPpath + "memstick/"; + g_Config.memStickDirectory = PPSSPPpath / "memstick"; } return UI::EVENT_DONE; } @@ -1236,8 +1238,9 @@ UI::EventReturn GameSettingsScreen::OnSavePathOther(UI::EventParams &e) { #endif UI::EventReturn GameSettingsScreen::OnChangeBackground(UI::EventParams &e) { - const std::string bgPng = GetSysDirectory(DIRECTORY_SYSTEM) + "background.png"; - const std::string bgJpg = GetSysDirectory(DIRECTORY_SYSTEM) + "background.jpg"; + const Path bgPng = GetSysDirectory(DIRECTORY_SYSTEM) / "background.png"; + const Path bgJpg = GetSysDirectory(DIRECTORY_SYSTEM) / "background.jpg"; + if (File::Exists(bgPng) || File::Exists(bgJpg)) { if (File::Exists(bgPng)) { File::Delete(bgPng); @@ -1299,11 +1302,6 @@ void GameSettingsScreen::update() { RecreateViews(); lastVertical_ = vertical; } - if (g_ShaderNameListChanged) { - g_ShaderNameListChanged = false; - g_Config.bShaderChainRequires60FPS = PostShaderChainRequires60FPS(GetFullPostShadersChain(g_Config.vPostShaderNames)); - RecreateViews(); - } } void GameSettingsScreen::onFinish(DialogResult result) { @@ -1331,32 +1329,38 @@ void GameSettingsScreen::onFinish(DialogResult result) { NativeMessageReceived("gpu_clearCache", ""); } -#if PPSSPP_PLATFORM(ANDROID) +void GameSettingsScreen::sendMessage(const char *message, const char *value) { + UIDialogScreenWithGameBackground::sendMessage(message, value); + if (!strcmp(message, "postshader_updated")) { + g_Config.bShaderChainRequires60FPS = PostShaderChainRequires60FPS(GetFullPostShadersChain(g_Config.vPostShaderNames)); + RecreateViews(); + } +} + void GameSettingsScreen::CallbackMemstickFolder(bool yes) { auto sy = GetI18NCategory("System"); if (yes) { - std::string memstickDirFile = g_Config.internalDataDirectory + "/memstick_dir.txt"; + Path memstickDirFile = g_Config.internalDataDirectory / "memstick_dir.txt"; std::string testWriteFile = pendingMemstickFolder_ + "/.write_verify_file"; // Already, create away. - if (!File::Exists(pendingMemstickFolder_)) { - File::CreateFullPath(pendingMemstickFolder_); + if (!File::Exists(Path(pendingMemstickFolder_))) { + File::CreateFullPath(Path(pendingMemstickFolder_)); } - if (!writeDataToFile(true, "1", 1, testWriteFile.c_str())) { + if (!File::WriteDataToFile(true, "1", 1, Path(testWriteFile))) { settingInfo_->Show(sy->T("ChangingMemstickPathInvalid", "That path couldn't be used to save Memory Stick files."), nullptr); return; } - File::Delete(testWriteFile); + File::Delete(Path(testWriteFile)); - writeDataToFile(true, pendingMemstickFolder_.c_str(), pendingMemstickFolder_.size(), memstickDirFile.c_str()); + File::WriteDataToFile(true, pendingMemstickFolder_.c_str(), (unsigned int)pendingMemstickFolder_.size(), memstickDirFile); // Save so the settings, at least, are transferred. - g_Config.memStickDirectory = pendingMemstickFolder_ + "/"; + g_Config.memStickDirectory = Path(pendingMemstickFolder_); g_Config.Save("MemstickPathChanged"); screenManager()->RecreateAllViews(); } } -#endif void GameSettingsScreen::TriggerRestart(const char *why) { // Extra save here to make sure the choice really gets saved even if there are shutdown bugs in @@ -1367,7 +1371,7 @@ void GameSettingsScreen::TriggerRestart(const char *why) { // We won't pass the gameID, so don't resume back into settings. param = ""; } else if (!gamePath_.empty()) { - param += " \"" + ReplaceAll(ReplaceAll(gamePath_, "\\", "\\\\"), "\"", "\\\"") + "\""; + param += " \"" + ReplaceAll(ReplaceAll(gamePath_.ToString(), "\\", "\\\\"), "\"", "\\\"") + "\""; } // Make sure the new instance is considered the first. ShutdownInstanceCounter(); @@ -1569,9 +1573,9 @@ UI::EventReturn GameSettingsScreen::OnLanguageChange(UI::EventParams &e) { UI::EventReturn GameSettingsScreen::OnPostProcShaderChange(UI::EventParams &e) { g_Config.vPostShaderNames.erase(std::remove(g_Config.vPostShaderNames.begin(), g_Config.vPostShaderNames.end(), "Off"), g_Config.vPostShaderNames.end()); - g_Config.vPostShaderNames.push_back("Off"); - g_ShaderNameListChanged = true; + NativeMessageReceived("gpu_resized", ""); + NativeMessageReceived("postshader_updated", ""); return UI::EVENT_DONE; } @@ -1630,7 +1634,7 @@ UI::EventReturn GameSettingsScreen::OnTiltCustomize(UI::EventParams &e) { }; UI::EventReturn GameSettingsScreen::OnSavedataManager(UI::EventParams &e) { - auto saveData = new SavedataScreen(""); + auto saveData = new SavedataScreen(Path()); screenManager()->push(saveData); return UI::EVENT_DONE; } @@ -1843,9 +1847,9 @@ UI::EventReturn DeveloperToolsScreen::OnLoadLanguageIni(UI::EventParams &e) { UI::EventReturn DeveloperToolsScreen::OnOpenTexturesIniFile(UI::EventParams &e) { std::string gameID = g_paramSFO.GetDiscID(); - std::string generatedFilename; - if (TextureReplacer::GenerateIni(gameID, &generatedFilename)) { - File::openIniFile(generatedFilename); + Path generatedFilename; + if (TextureReplacer::GenerateIni(gameID, generatedFilename)) { + File::OpenFileInEditor(generatedFilename); } return UI::EVENT_DONE; } @@ -1881,15 +1885,15 @@ UI::EventReturn DeveloperToolsScreen::OnJitAffectingSetting(UI::EventParams &e) } UI::EventReturn DeveloperToolsScreen::OnCopyStatesToRoot(UI::EventParams &e) { - std::string savestate_dir = GetSysDirectory(DIRECTORY_SAVESTATE); - std::string root_dir = GetSysDirectory(DIRECTORY_MEMSTICK_ROOT); + Path savestate_dir = GetSysDirectory(DIRECTORY_SAVESTATE); + Path root_dir = GetSysDirectory(DIRECTORY_MEMSTICK_ROOT); - std::vector files; - getFilesInDir(savestate_dir.c_str(), &files, nullptr, 0); + std::vector files; + GetFilesInDir(savestate_dir, &files, nullptr, 0); - for (const FileInfo &file : files) { - std::string src = file.fullName; - std::string dst = root_dir + file.name; + for (const File::FileInfo &file : files) { + Path src = file.fullName; + Path dst = root_dir / file.name; INFO_LOG(SYSTEM, "Copying file '%s' to '%s'", src.c_str(), dst.c_str()); File::Copy(src, dst); } @@ -1897,7 +1901,6 @@ UI::EventReturn DeveloperToolsScreen::OnCopyStatesToRoot(UI::EventParams &e) { return UI::EVENT_DONE; } - UI::EventReturn DeveloperToolsScreen::OnRemoteDebugger(UI::EventParams &e) { if (allowDebugger_) { StartWebServer(WebServerFlags::DEBUGGER); diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 0238215f54..a3761603c9 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -30,21 +30,20 @@ class SettingInfoMessage; // per game. class GameSettingsScreen : public UIDialogScreenWithGameBackground { public: - GameSettingsScreen(std::string gamePath, std::string gameID = "", bool editThenRestore = false); + GameSettingsScreen(const Path &gamePath, std::string gameID = "", bool editThenRestore = false); void update() override; void onFinish(DialogResult result) override; std::string tag() const override { return "settings"; } protected: + void sendMessage(const char *message, const char *value) override; void CreateViews() override; void CallbackRestoreDefaults(bool yes); void CallbackRenderingBackend(bool yes); void CallbackRenderingDevice(bool yes); void CallbackInflightFrames(bool yes); -#if PPSSPP_PLATFORM(ANDROID) void CallbackMemstickFolder(bool yes); -#endif bool UseVerticalLayout() const; private: @@ -68,6 +67,8 @@ private: bool otherinstalled_; #endif + std::string memstickDisplay_; + // Event handlers UI::EventReturn OnControlMapping(UI::EventParams &e); UI::EventReturn OnTouchControlLayout(UI::EventParams &e); @@ -137,9 +138,8 @@ private: //edit the game-specific settings and restore the global settings after exiting bool editThenRestore_; -#if PPSSPP_PLATFORM(ANDROID) + // Android-only std::string pendingMemstickFolder_; -#endif }; class SettingInfoMessage : public UI::LinearLayout { diff --git a/UI/InstallZipScreen.cpp b/UI/InstallZipScreen.cpp index 3f871e348f..7db798ea08 100644 --- a/UI/InstallZipScreen.cpp +++ b/UI/InstallZipScreen.cpp @@ -28,8 +28,8 @@ void InstallZipScreen::CreateViews() { using namespace UI; - FileInfo fileInfo; - bool success = getFileInfo(zipPath_.c_str(), &fileInfo); + File::FileInfo fileInfo; + bool success = File::GetFileInfo(zipPath_, &fileInfo); auto di = GetI18NCategory("Dialog"); auto iz = GetI18NCategory("InstallZip"); @@ -43,7 +43,7 @@ void InstallZipScreen::CreateViews() { root_->Add(leftColumn); root_->Add(rightColumnItems); - std::string shortFilename = GetFilenameFromPath(zipPath_); + std::string shortFilename = zipPath_.GetFilename(); // TODO: Do in the background? ZipFileInfo zipInfo; diff --git a/UI/InstallZipScreen.h b/UI/InstallZipScreen.h index e8e8d05701..daf7d4ae88 100644 --- a/UI/InstallZipScreen.h +++ b/UI/InstallZipScreen.h @@ -26,7 +26,7 @@ class InstallZipScreen : public UIDialogScreenWithBackground { public: - InstallZipScreen(std::string zipPath) : zipPath_(zipPath) {} + InstallZipScreen(const Path &zipPath) : zipPath_(zipPath) {} virtual void update() override; virtual bool key(const KeyInput &key) override; @@ -40,7 +40,7 @@ private: UI::Choice *backChoice_ = nullptr; UI::ProgressBar *progressBar_ = nullptr; UI::TextView *doneView_ = nullptr; - std::string zipPath_; + Path zipPath_; bool returnToHomebrew_ = true; bool installStarted_ = false; bool deleteZipFile_ = false; diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index e94eba6323..34f5a2e820 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -67,7 +67,7 @@ bool MainScreen::showHomebrewTab = false; -bool LaunchFile(ScreenManager *screenManager, std::string path) { +bool LaunchFile(ScreenManager *screenManager, const Path &path) { // Depending on the file type, we don't want to launch EmuScreen at all. auto loader = ConstructFileLoader(path); if (!loader) { @@ -89,11 +89,12 @@ bool LaunchFile(ScreenManager *screenManager, std::string path) { return true; } -static bool IsTempPath(const std::string &str) { - std::string item = str; +static bool IsTempPath(const Path &str) { + std::string item = str.ToString(); + #ifdef _WIN32 // Normalize slashes. - item = ReplaceAll(str, "/", "\\"); + item = ReplaceAll(item, "/", "\\"); #endif std::vector tempPaths = System_GetPropertyStringVec(SYSPROP_TEMP_DIRS); @@ -115,7 +116,7 @@ static bool IsTempPath(const std::string &str) { class GameButton : public UI::Clickable { public: - GameButton(const std::string &gamePath, bool gridStyle, UI::LayoutParams *layoutParams = 0) + GameButton(const Path &gamePath, bool gridStyle, UI::LayoutParams *layoutParams = 0) : UI::Clickable(layoutParams), gridStyle_(gridStyle), gamePath_(gamePath) {} void Draw(UIContext &dc) override; @@ -130,7 +131,7 @@ public: } } - const std::string &GamePath() const { return gamePath_; } + const Path &GamePath() const { return gamePath_; } void SetHoldEnabled(bool hold) { holdEnabled_ = hold; @@ -196,20 +197,20 @@ private: holdStart_ = 0.0; UI::EventParams e{}; e.v = this; - e.s = gamePath_; + e.s = gamePath_.ToString(); down_ = false; OnHoldClick.Trigger(e); } void TriggerOnHighlight(int focusFlags) { UI::EventParams e{}; e.v = this; - e.s = gamePath_; + e.s = gamePath_.ToString(); e.a = focusFlags; OnHighlight.Trigger(e); } bool gridStyle_; - std::string gamePath_; + Path gamePath_; std::string title_; double holdStart_ = 0.0; @@ -417,14 +418,14 @@ std::string GameButton::DescribeText() const { class DirButton : public UI::Button { public: - DirButton(const std::string &path, bool gridStyle, UI::LayoutParams *layoutParams) - : UI::Button(path, layoutParams), path_(path), gridStyle_(gridStyle), absolute_(false) {} - DirButton(const std::string &path, const std::string &text, bool gridStyle, UI::LayoutParams *layoutParams = 0) + DirButton(const Path &path, bool gridStyle, UI::LayoutParams *layoutParams) + : UI::Button(path.ToString(), layoutParams), path_(path), gridStyle_(gridStyle), absolute_(false) {} + DirButton(const Path &path, const std::string &text, bool gridStyle, UI::LayoutParams *layoutParams = 0) : UI::Button(text, layoutParams), path_(path), gridStyle_(gridStyle), absolute_(true) {} virtual void Draw(UIContext &dc); - const std::string GetPath() const { + const Path &GetPath() const { return path_; } @@ -433,7 +434,7 @@ public: } private: - std::string path_; + Path path_; bool gridStyle_; bool absolute_; }; @@ -490,21 +491,21 @@ void DirButton::Draw(UIContext &dc) { } } -GameBrowser::GameBrowser(std::string path, BrowseFlags browseFlags, bool *gridStyle, ScreenManager *screenManager, std::string lastText, std::string lastLink, UI::LayoutParams *layoutParams) +GameBrowser::GameBrowser(const Path &path, BrowseFlags browseFlags, bool *gridStyle, ScreenManager *screenManager, std::string lastText, std::string lastLink, UI::LayoutParams *layoutParams) : LinearLayout(UI::ORIENT_VERTICAL, layoutParams), path_(path), gridStyle_(gridStyle), browseFlags_(browseFlags), lastText_(lastText), lastLink_(lastLink), screenManager_(screenManager) { using namespace UI; Refresh(); } -void GameBrowser::FocusGame(const std::string &gamePath) { +void GameBrowser::FocusGame(const Path &gamePath) { focusGamePath_ = gamePath; Refresh(); focusGamePath_.clear(); } -void GameBrowser::SetPath(const std::string &path) { +void GameBrowser::SetPath(const Path &path) { path_.SetPath(path); - g_Config.currentDirectory = path_.GetPath(); + g_Config.currentDirectory = path_.GetPath().ToString(); Refresh(); } @@ -531,26 +532,30 @@ UI::EventReturn GameBrowser::StorageClick(UI::EventParams &e) { return UI::EVENT_DONE; } if (storageDirs.size() == 1) { - SetPath(storageDirs[0]); + SetPath(Path(storageDirs[0])); } else { // TODO: We should popup a dialog letting the user choose one. - SetPath(storageDirs[0]); + SetPath(Path(storageDirs[0])); } return UI::EVENT_DONE; } UI::EventReturn GameBrowser::HomeClick(UI::EventParams &e) { -#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH) || defined(USING_WIN_UI) || PPSSPP_PLATFORM(UWP) - SetPath(g_Config.memStickDirectory); -#else - SetPath(getenv("HOME")); -#endif + SetPath(HomePath()); return UI::EVENT_DONE; } +Path GameBrowser::HomePath() { +#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH) || defined(USING_WIN_UI) || PPSSPP_PLATFORM(UWP) + return g_Config.memStickDirectory; +#else + return Path(getenv("HOME")); +#endif +} + UI::EventReturn GameBrowser::PinToggleClick(UI::EventParams &e) { auto &pinnedPaths = g_Config.vPinnedPaths; - const std::string path = File::ResolvePath(path_.GetPath()); + const std::string path = File::ResolvePath(path_.GetPath().ToString()); if (IsCurrentPathPinned()) { pinnedPaths.erase(std::remove(pinnedPaths.begin(), pinnedPaths.end(), path), pinnedPaths.end()); } else { @@ -561,12 +566,15 @@ UI::EventReturn GameBrowser::PinToggleClick(UI::EventParams &e) { } bool GameBrowser::DisplayTopBar() { - return path_.GetPath() != "!RECENT"; + return path_.GetPath().ToString() != "!RECENT"; } -bool GameBrowser::HasSpecialFiles(std::vector &filenames) { - if (path_.GetPath() == "!RECENT") { - filenames = g_Config.recentIsos; +bool GameBrowser::HasSpecialFiles(std::vector &filenames) { + if (path_.GetPath().ToString() == "!RECENT") { + filenames.clear(); + for (auto &str : g_Config.recentIsos) { + filenames.push_back(Path(str)); + } return true; } return false; @@ -612,7 +620,7 @@ void GameBrowser::Draw(UIContext &dc) { } } -static bool IsValidPBP(const std::string &path, bool allowHomebrew) { +static bool IsValidPBP(const Path &path, bool allowHomebrew) { if (!File::Exists(path)) return false; @@ -706,23 +714,23 @@ void GameBrowser::Refresh() { listingPending_ = !path_.IsListingReady(); - std::vector filenames; + std::vector filenames; if (HasSpecialFiles(filenames)) { for (size_t i = 0; i < filenames.size(); i++) { gameButtons.push_back(new GameButton(filenames[i], *gridStyle_, new UI::LinearLayoutParams(*gridStyle_ == true ? UI::WRAP_CONTENT : UI::FILL_PARENT, UI::WRAP_CONTENT))); } } else if (!listingPending_) { - std::vector fileInfo; + std::vector fileInfo; path_.GetListing(fileInfo, "iso:cso:pbp:elf:prx:ppdmp:"); for (size_t i = 0; i < fileInfo.size(); i++) { bool isGame = !fileInfo[i].isDirectory; bool isSaveData = false; // Check if eboot directory - if (!isGame && path_.GetPath().size() >= 4 && IsValidPBP(path_.GetPath() + fileInfo[i].name + "/EBOOT.PBP", true)) + if (!isGame && path_.GetPath().size() >= 4 && IsValidPBP(path_.GetPath() / fileInfo[i].name / "EBOOT.PBP", true)) isGame = true; - else if (!isGame && File::Exists(path_.GetPath() + fileInfo[i].name + "/PSP_GAME/SYSDIR")) + else if (!isGame && File::Exists(path_.GetPath() / fileInfo[i].name / "PSP_GAME/SYSDIR")) isGame = true; - else if (!isGame && File::Exists(path_.GetPath() + fileInfo[i].name + "/PARAM.SFO")) + else if (!isGame && File::Exists(path_.GetPath() / fileInfo[i].name / "PARAM.SFO")) isSaveData = true; if (!isGame && !isSaveData) { @@ -756,14 +764,15 @@ void GameBrowser::Refresh() { if (browseFlags_ & BrowseFlags::NAVIGATE) { if (path_.CanNavigateUp()) { - gameList_->Add(new DirButton("..", *gridStyle_, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::FILL_PARENT)))-> + gameList_->Add(new DirButton(Path(std::string("..")), *gridStyle_, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::FILL_PARENT)))-> OnClick.Handle(this, &GameBrowser::NavigateClick); } // Add any pinned paths before other directories. auto pinnedPaths = GetPinnedPaths(); for (auto it = pinnedPaths.begin(), end = pinnedPaths.end(); it != end; ++it) { - gameList_->Add(new DirButton(*it, GetBaseName(*it), *gridStyle_, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::FILL_PARENT)))-> + // TODO(scoped): Hmm + gameList_->Add(new DirButton(*it, GetBaseName((*it).ToString()), *gridStyle_, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::FILL_PARENT)))-> OnClick.Handle(this, &GameBrowser::NavigateClick); } } @@ -810,19 +819,19 @@ void GameBrowser::Refresh() { bool GameBrowser::IsCurrentPathPinned() { const auto paths = g_Config.vPinnedPaths; - return std::find(paths.begin(), paths.end(), File::ResolvePath(path_.GetPath())) != paths.end(); + return std::find(paths.begin(), paths.end(), File::ResolvePath(path_.GetPath().ToString())) != paths.end(); } -const std::vector GameBrowser::GetPinnedPaths() { +const std::vector GameBrowser::GetPinnedPaths() { #ifndef _WIN32 static const std::string sepChars = "/"; #else static const std::string sepChars = "/\\"; #endif - const std::string currentPath = File::ResolvePath(path_.GetPath()); + const std::string currentPath = File::ResolvePath(path_.GetPath().ToString()); const std::vector paths = g_Config.vPinnedPaths; - std::vector results; + std::vector results; for (size_t i = 0; i < paths.size(); ++i) { // We want to exclude the current path, and its direct children. if (paths[i] == currentPath) { @@ -836,7 +845,7 @@ const std::vector GameBrowser::GetPinnedPaths() { } } - results.push_back(paths[i]); + results.push_back(Path(paths[i])); } return results; } @@ -867,7 +876,7 @@ const std::string GameBrowser::GetBaseName(const std::string &path) { UI::EventReturn GameBrowser::GameButtonClick(UI::EventParams &e) { GameButton *button = static_cast(e.v); UI::EventParams e2{}; - e2.s = button->GamePath(); + e2.s = button->GamePath().ToString(); // Insta-update - here we know we are already on the right thread. OnChoice.Trigger(e2); return UI::EVENT_DONE; @@ -876,7 +885,7 @@ UI::EventReturn GameBrowser::GameButtonClick(UI::EventParams &e) { UI::EventReturn GameBrowser::GameButtonHoldClick(UI::EventParams &e) { GameButton *button = static_cast(e.v); UI::EventParams e2{}; - e2.s = button->GamePath(); + e2.s = button->GamePath().ToString(); // Insta-update - here we know we are already on the right thread. OnHoldChoice.Trigger(e2); return UI::EVENT_DONE; @@ -890,13 +899,13 @@ UI::EventReturn GameBrowser::GameButtonHighlight(UI::EventParams &e) { UI::EventReturn GameBrowser::NavigateClick(UI::EventParams &e) { DirButton *button = static_cast(e.v); - std::string text = button->GetPath(); + Path text = button->GetPath(); if (button->PathAbsolute()) { path_.SetPath(text); } else { - path_.Navigate(text); + path_.Navigate(text.ToString()); } - g_Config.currentDirectory = path_.GetPath(); + g_Config.currentDirectory = path_.GetPath().ToString(); Refresh(); return UI::EVENT_DONE; } @@ -927,12 +936,12 @@ UI::EventReturn GameBrowser::OnHomebrewStore(UI::EventParams &e) { MainScreen::MainScreen() { System_SendMessage("event", "mainscreen"); - g_BackgroundAudio.SetGame(""); + g_BackgroundAudio.SetGame(Path()); lastVertical_ = UseVerticalLayout(); } MainScreen::~MainScreen() { - g_BackgroundAudio.SetGame(""); + g_BackgroundAudio.SetGame(Path()); } void MainScreen::CreateViews() { @@ -965,7 +974,7 @@ void MainScreen::CreateViews() { ScrollView *scrollRecentGames = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scrollRecentGames->SetTag("MainScreenRecentGames"); GameBrowser *tabRecentGames = new GameBrowser( - "!RECENT", BrowseFlags::NONE, &g_Config.bGridView1, screenManager(), "", "", + Path("!RECENT"), BrowseFlags::NONE, &g_Config.bGridView1, screenManager(), "", "", new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); scrollRecentGames->Add(tabRecentGames); gameBrowsers_.push_back(tabRecentGames); @@ -983,7 +992,7 @@ void MainScreen::CreateViews() { ScrollView *scrollHomebrew = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scrollHomebrew->SetTag("MainScreenHomebrew"); - GameBrowser *tabAllGames = new GameBrowser(g_Config.currentDirectory, BrowseFlags::STANDARD, &g_Config.bGridView2, screenManager(), + GameBrowser *tabAllGames = new GameBrowser(Path(g_Config.currentDirectory), BrowseFlags::STANDARD, &g_Config.bGridView2, screenManager(), mm->T("How to get games"), "https://www.ppsspp.org/getgames.html", new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); GameBrowser *tabHomebrew = new GameBrowser(GetSysDirectory(DIRECTORY_GAME), BrowseFlags::HOMEBREW_STORE, &g_Config.bGridView3, screenManager(), @@ -1187,11 +1196,11 @@ void MainScreen::sendMessage(const char *message, const char *value) { if (screenManager()->topScreen() == this) { if (!strcmp(message, "boot")) { - LaunchFile(screenManager(), std::string(value)); + LaunchFile(screenManager(), Path(std::string(value))); } if (!strcmp(message, "browse_fileSelect")) { INFO_LOG(SYSTEM, "Attempting to launch: '%s'", value); - LaunchFile(screenManager(), std::string(value)); + LaunchFile(screenManager(), Path(std::string(value))); } if (!strcmp(message, "browse_folderSelect")) { std::string filename; @@ -1209,7 +1218,7 @@ void MainScreen::sendMessage(const char *message, const char *value) { } url = "/storage/" + url; if (startsWith(url, primaryPrefix)) { - url = g_Config.memStickDirectory + url.substr(strlen(primaryPrefix)); + url = g_Config.memStickDirectory.ToString() + url.substr(strlen(primaryPrefix)); } INFO_LOG(SYSTEM, "Translated '%s' into '%s'", value, url.c_str()); } else { @@ -1224,8 +1233,9 @@ void MainScreen::sendMessage(const char *message, const char *value) { #endif INFO_LOG(SYSTEM, "Got folder: '%s'", filename.c_str()); int tab = tabHolder_->GetCurrentTab(); - if (tab >= 0 && tab < (int)gameBrowsers_.size()) { - gameBrowsers_[tab]->SetPath(filename); + // Don't allow browsing in the other tabs (I don't think it's possible to reach the option though) + if (tab == 1) { + gameBrowsers_[tab]->SetPath(Path(filename)); } } } @@ -1289,7 +1299,7 @@ void MainScreen::DrawBackground(UIContext &dc) { } } -bool MainScreen::DrawBackgroundFor(UIContext &dc, const std::string &gamePath, float progress) { +bool MainScreen::DrawBackgroundFor(UIContext &dc, const Path &gamePath, float progress) { dc.Flush(); std::shared_ptr ginfo; @@ -1306,12 +1316,8 @@ bool MainScreen::DrawBackgroundFor(UIContext &dc, const std::string &gamePath, f return false; } - Draw::Texture *texture = nullptr; - if (ginfo->pic1.texture) { - texture = ginfo->pic1.texture->GetTexture(); - } else if (ginfo->pic0.texture) { - texture = ginfo->pic0.texture->GetTexture(); - } + auto pic = ginfo->GetBGPic(); + Draw::Texture *texture = pic ? pic->texture->GetTexture() : nullptr; uint32_t color = whiteAlpha(ease(progress)) & 0xFFc0c0c0; if (texture) { @@ -1325,11 +1331,7 @@ bool MainScreen::DrawBackgroundFor(UIContext &dc, const std::string &gamePath, f UI::EventReturn MainScreen::OnGameSelected(UI::EventParams &e) { g_Config.Save("MainScreen::OnGameSelected"); -#ifdef _WIN32 - std::string path = ReplaceAll(e.s, "\\", "/"); -#else - std::string path = e.s; -#endif + Path path(e.s); std::shared_ptr ginfo = g_gameInfoCache->GetInfo(nullptr, path, GAMEINFO_WANTBG); if (ginfo && ginfo->fileType == IdentifiedFileType::PSP_SAVEDATA_DIRECTORY) { return UI::EVENT_DONE; @@ -1349,11 +1351,7 @@ UI::EventReturn MainScreen::OnGameSelected(UI::EventParams &e) { UI::EventReturn MainScreen::OnGameHighlight(UI::EventParams &e) { using namespace UI; -#ifdef _WIN32 - std::string path = ReplaceAll(e.s, "\\", "/"); -#else - std::string path = e.s; -#endif + Path path(e.s); // Don't change when re-highlighting what's already highlighted. if (path != highlightedGamePath_ || e.a == FF_LOSTFOCUS) { @@ -1380,18 +1378,13 @@ UI::EventReturn MainScreen::OnGameHighlight(UI::EventParams &e) { UI::EventReturn MainScreen::OnGameSelectedInstant(UI::EventParams &e) { g_Config.Save("MainScreen::OnGameSelectedInstant"); -#ifdef _WIN32 - std::string path = ReplaceAll(e.s, "\\", "/"); -#else - std::string path = e.s; -#endif ScreenManager *screen = screenManager(); - LaunchFile(screen, path); + LaunchFile(screen, Path(e.s)); return UI::EVENT_DONE; } UI::EventReturn MainScreen::OnGameSettings(UI::EventParams &e) { - screenManager()->push(new GameSettingsScreen("", "")); + screenManager()->push(new GameSettingsScreen(Path(), "")); return UI::EVENT_DONE; } @@ -1459,7 +1452,7 @@ void MainScreen::dialogFinished(const Screen *dialog, DialogResult result) { restoreFocusGamePath_.clear(); } else { // Not refocusing, so we need to stop the audio. - g_BackgroundAudio.SetGame(""); + g_BackgroundAudio.SetGame(Path()); } } } @@ -1483,7 +1476,7 @@ void UmdReplaceScreen::CreateViews() { ScrollView *scrollRecentGames = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scrollRecentGames->SetTag("UmdReplaceRecentGames"); GameBrowser *tabRecentGames = new GameBrowser( - "!RECENT", BrowseFlags::NONE, &g_Config.bGridView1, screenManager(), "", "", + Path("!RECENT"), BrowseFlags::NONE, &g_Config.bGridView1, screenManager(), "", "", new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); scrollRecentGames->Add(tabRecentGames); leftColumn->AddTab(mm->T("Recent"), scrollRecentGames); @@ -1493,7 +1486,7 @@ void UmdReplaceScreen::CreateViews() { ScrollView *scrollAllGames = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scrollAllGames->SetTag("UmdReplaceAllGames"); - GameBrowser *tabAllGames = new GameBrowser(g_Config.currentDirectory, BrowseFlags::STANDARD, &g_Config.bGridView2, screenManager(), + GameBrowser *tabAllGames = new GameBrowser(Path(g_Config.currentDirectory), BrowseFlags::STANDARD, &g_Config.bGridView2, screenManager(), mm->T("How to get games"), "https://www.ppsspp.org/getgames.html", new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); @@ -1525,7 +1518,7 @@ void UmdReplaceScreen::update() { } UI::EventReturn UmdReplaceScreen::OnGameSelected(UI::EventParams &e) { - __UmdReplace(e.s); + __UmdReplace(Path(e.s)); TriggerFinish(DR_OK); return UI::EVENT_DONE; } @@ -1536,12 +1529,12 @@ UI::EventReturn UmdReplaceScreen::OnCancel(UI::EventParams &e) { } UI::EventReturn UmdReplaceScreen::OnGameSettings(UI::EventParams &e) { - screenManager()->push(new GameSettingsScreen("")); + screenManager()->push(new GameSettingsScreen(Path())); return UI::EVENT_DONE; } UI::EventReturn UmdReplaceScreen::OnGameSelectedInstant(UI::EventParams &e) { - __UmdReplace(e.s); + __UmdReplace(Path(e.s)); TriggerFinish(DR_OK); return UI::EVENT_DONE; } @@ -1572,8 +1565,6 @@ void GridSettingsScreen::CreatePopupContents(UI::ViewGroup *parent) { items->Add(new Choice(sy->T("Clear Recent Games List")))->OnClick.Handle(this, &GridSettingsScreen::OnRecentClearClick); } - items->Add(new Choice(di->T("Back")))->OnClick.Handle(this, &UIScreen::OnBack); - scroll->Add(items); parent->Add(scroll); } diff --git a/UI/MainScreen.h b/UI/MainScreen.h index 9c3ca9b4a4..a8a247f515 100644 --- a/UI/MainScreen.h +++ b/UI/MainScreen.h @@ -19,6 +19,7 @@ #include +#include "Common/File/Path.h" #include "Common/UI/UIScreen.h" #include "Common/UI/ViewGroup.h" #include "UI/MiscScreens.h" @@ -38,30 +39,31 @@ enum class BrowseFlags { }; ENUM_CLASS_BITOPS(BrowseFlags); -bool LaunchFile(ScreenManager *screenManager, std::string path); +bool LaunchFile(ScreenManager *screenManager, const Path &path); class GameBrowser : public UI::LinearLayout { public: - GameBrowser(std::string path, BrowseFlags browseFlags, bool *gridStyle, ScreenManager *screenManager, std::string lastText, std::string lastLink, UI::LayoutParams *layoutParams = nullptr); + GameBrowser(const Path &path, BrowseFlags browseFlags, bool *gridStyle, ScreenManager *screenManager, std::string lastText, std::string lastLink, UI::LayoutParams *layoutParams = nullptr); UI::Event OnChoice; UI::Event OnHoldChoice; UI::Event OnHighlight; - void FocusGame(const std::string &gamePath); - void SetPath(const std::string &path); + void FocusGame(const Path &gamePath); + void SetPath(const Path &path); void Draw(UIContext &dc) override; void Update() override; protected: virtual bool DisplayTopBar(); - virtual bool HasSpecialFiles(std::vector &filenames); + virtual bool HasSpecialFiles(std::vector &filenames); + virtual Path HomePath(); void Refresh(); private: bool IsCurrentPathPinned(); - const std::vector GetPinnedPaths(); + const std::vector GetPinnedPaths(); const std::string GetBaseName(const std::string &path); UI::EventReturn GameButtonClick(UI::EventParams &e); @@ -84,7 +86,7 @@ private: BrowseFlags browseFlags_; std::string lastText_; std::string lastLink_; - std::string focusGamePath_; + Path focusGamePath_; bool listingPending_ = false; float lastScale_ = 1.0f; bool lastLayoutWasGrid_ = true; @@ -111,7 +113,7 @@ protected: void dialogFinished(const Screen *dialog, DialogResult result) override; bool UseVerticalLayout() const; - bool DrawBackgroundFor(UIContext &dc, const std::string &gamePath, float progress); + bool DrawBackgroundFor(UIContext &dc, const Path &gamePath, float progress); UI::EventReturn OnGameSelected(UI::EventParams &e); UI::EventReturn OnGameSelectedInstant(UI::EventParams &e); @@ -134,11 +136,11 @@ protected: UI::TabHolder *tabHolder_ = nullptr; UI::Button *fullscreenButton_ = nullptr; - std::string restoreFocusGamePath_; + Path restoreFocusGamePath_; std::vector gameBrowsers_; - std::string highlightedGamePath_; - std::string prevHighlightedGamePath_; + Path highlightedGamePath_; + Path prevHighlightedGamePath_; float highlightProgress_ = 0.0f; float prevHighlightProgress_ = 0.0f; bool backFromStore_ = false; diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index e6c65079e8..e67bbe64f0 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -73,45 +73,174 @@ static const uint32_t colors[4] = { static std::unique_ptr bgTexture; -static bool backgroundInited; +class Animation { +public: + virtual ~Animation() {} + virtual void Draw(UIContext &dc, double t, float alpha) = 0; +}; + +class FloatingSymbolsAnimation : public Animation { +public: + ~FloatingSymbolsAnimation() override {} + void Draw(UIContext &dc, double t, float alpha) override { + float xres = dc.GetBounds().w; + float yres = dc.GetBounds().h; + if (last_xres != xres || last_yres != yres) { + Regenerate(xres, yres); + } + + for (int i = 0; i < COUNT; i++) { + float x = xbase[i] + dc.GetBounds().x; + float y = ybase[i] + dc.GetBounds().y + 40 * cosf(i * 7.2f + t * 1.3f); + float angle = (float)sin(i + t); + int n = i & 3; + ui_draw2d.DrawImageRotated(symbols[n], x, y, 1.0f, angle, colorAlpha(colors[n], alpha * 0.1f)); + } + } + +private: + static constexpr int COUNT = 100; + + float xbase[COUNT]{}; + float ybase[COUNT]{}; + float last_xres = 0; + float last_yres = 0; + + void Regenerate(int xres, int yres) { + GMRng rng; + for (int i = 0; i < COUNT; i++) { + xbase[i] = rng.F() * xres; + ybase[i] = rng.F() * yres; + } + + last_xres = xres; + last_yres = yres; + } +}; + +class RecentGamesAnimation : public Animation { +public: + ~RecentGamesAnimation() override {} + void Draw(UIContext &dc, double t, float alpha) override { + if (lastIndex_ == nextIndex_) { + CheckNext(dc, t); + } else if (t > nextT_) { + lastIndex_ = nextIndex_; + } + + if (!g_Config.recentIsos.empty()) { + std::shared_ptr lastInfo = GetInfo(dc, lastIndex_); + std::shared_ptr nextInfo = GetInfo(dc, nextIndex_); + dc.Flush(); + + float lastAmount = Clamp((float)(nextT_ - t) * 1.0f / TRANSITION, 0.0f, 1.0f); + DrawTex(dc, lastInfo, lastAmount * alpha * 0.2f); + + float nextAmount = lastAmount <= 0.0f ? 1.0f : 1.0f - lastAmount; + DrawTex(dc, nextInfo, nextAmount * alpha * 0.2f); + + dc.RebindTexture(); + } + } + +private: + void CheckNext(UIContext &dc, double t) { + if (g_Config.recentIsos.empty()) { + return; + } + + for (int index = lastIndex_ + 1; index != lastIndex_; ++index) { + if (index < 0 || index >= (int)g_Config.recentIsos.size()) { + if (lastIndex_ == -1) + break; + index = 0; + } + + std::shared_ptr ginfo = GetInfo(dc, index); + if (ginfo && ginfo->pending) { + // Wait for it to load. It might be the next one. + break; + } + if (ginfo && (ginfo->pic1.texture || ginfo->pic0.texture)) { + nextIndex_ = index; + nextT_ = t + INTERVAL; + break; + } + + // Otherwise, keep going. This skips games with no BG. + } + } + + std::shared_ptr GetInfo(UIContext &dc, int index) { + if (index < 0) { + return nullptr; + } + return g_gameInfoCache->GetInfo(dc.GetDrawContext(), Path(g_Config.recentIsos[index]), GAMEINFO_WANTBG); + } + + void DrawTex(UIContext &dc, std::shared_ptr &ginfo, float amount) { + if (!ginfo || amount <= 0.0f) + return; + GameInfoTex *pic = ginfo->GetBGPic(); + if (!pic) + return; + + dc.GetDrawContext()->BindTexture(0, pic->texture->GetTexture()); + uint32_t color = whiteAlpha(amount) & 0xFFc0c0c0; + dc.Draw()->DrawTexRect(dc.GetBounds(), 0, 0, 1, 1, color); + dc.Flush(); + } + + static constexpr double INTERVAL = 8.0; + static constexpr float TRANSITION = 3.0f; + + int lastIndex_ = -1; + int nextIndex_ = -1; + double nextT_ = -INTERVAL; +}; + +// TODO: Add more styles. Remember to add to the enum in Config.cpp and the selector in GameSettings too. + +static BackgroundAnimation g_CurBackgroundAnimation = BackgroundAnimation::OFF; +static std::unique_ptr g_Animation; +static bool bgTextureInited = false; void UIBackgroundInit(UIContext &dc) { - const std::string bgPng = GetSysDirectory(DIRECTORY_SYSTEM) + "background.png"; - const std::string bgJpg = GetSysDirectory(DIRECTORY_SYSTEM) + "background.jpg"; + const Path bgPng = GetSysDirectory(DIRECTORY_SYSTEM) / "background.png"; + const Path bgJpg = GetSysDirectory(DIRECTORY_SYSTEM) / "background.jpg"; if (File::Exists(bgPng) || File::Exists(bgJpg)) { - const std::string &bgFile = File::Exists(bgPng) ? bgPng : bgJpg; + const Path &bgFile = File::Exists(bgPng) ? bgPng : bgJpg; bgTexture = CreateTextureFromFile(dc.GetDrawContext(), bgFile.c_str(), DETECT, true); } } void UIBackgroundShutdown() { bgTexture.reset(nullptr); - backgroundInited = false; + g_Animation.reset(nullptr); + g_CurBackgroundAnimation = BackgroundAnimation::OFF; + bgTextureInited = false; } void DrawBackground(UIContext &dc, float alpha) { - if (!backgroundInited) { + if (!bgTextureInited) { UIBackgroundInit(dc); - backgroundInited = true; + bgTextureInited = true; } + if (g_CurBackgroundAnimation != (BackgroundAnimation)g_Config.iBackgroundAnimation) { + g_CurBackgroundAnimation = (BackgroundAnimation)g_Config.iBackgroundAnimation; - static float xbase[100] = {0}; - static float ybase[100] = {0}; - float xres = dc.GetBounds().w; - float yres = dc.GetBounds().h; - static int last_xres = 0; - static int last_yres = 0; - - if (xbase[0] == 0.0f || last_xres != xres || last_yres != yres) { - GMRng rng; - for (int i = 0; i < 100; i++) { - xbase[i] = rng.F() * xres; - ybase[i] = rng.F() * yres; + switch (g_CurBackgroundAnimation) { + case BackgroundAnimation::FLOATING_SYMBOLS: + g_Animation.reset(new FloatingSymbolsAnimation()); + break; + case BackgroundAnimation::RECENT_GAMES: + g_Animation.reset(new RecentGamesAnimation()); + break; + default: + g_Animation.reset(nullptr); } - last_xres = xres; - last_yres = yres; } - + uint32_t bgColor = whiteAlpha(alpha); if (bgTexture != nullptr) { @@ -135,34 +264,24 @@ void DrawBackground(UIContext &dc, float alpha) { #else double t = time_now_d(); #endif - for (int i = 0; i < 100; i++) { - float x = xbase[i] + dc.GetBounds().x; - float y = ybase[i] + dc.GetBounds().y + 40 * cosf(i * 7.2f + t * 1.3f); - float angle = (float)sin(i + t); - int n = i & 3; - ui_draw2d.DrawImageRotated(symbols[n], x, y, 1.0f, angle, colorAlpha(colors[n], alpha * 0.1f)); + + if (g_Animation) { + g_Animation->Draw(dc, t, alpha); } } -void DrawGameBackground(UIContext &dc, const std::string &gamePath) { +void DrawGameBackground(UIContext &dc, const Path &gamePath) { std::shared_ptr ginfo; - if (gamePath.size()) + if (!gamePath.empty()) ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath, GAMEINFO_WANTBG); dc.Flush(); - bool hasPic = false; - double loadTime; - if (ginfo && ginfo->pic1.texture) { - dc.GetDrawContext()->BindTexture(0, ginfo->pic1.texture->GetTexture()); - loadTime = ginfo->pic1.timeLoaded; - hasPic = true; - } else if (ginfo && ginfo->pic0.texture) { - dc.GetDrawContext()->BindTexture(0, ginfo->pic0.texture->GetTexture()); - loadTime = ginfo->pic0.timeLoaded; - hasPic = true; + GameInfoTex *pic = ginfo ? ginfo->GetBGPic() : nullptr; + if (pic) { + dc.GetDrawContext()->BindTexture(0, pic->texture->GetTexture()); } - if (hasPic) { - uint32_t color = whiteAlpha(ease((time_now_d() - loadTime) * 3)) & 0xFFc0c0c0; + if (pic) { + uint32_t color = whiteAlpha(ease((time_now_d() - pic->timeLoaded) * 3)) & 0xFFc0c0c0; dc.Draw()->DrawTexRect(dc.GetBounds(), 0,0,1,1, color); dc.Flush(); dc.RebindTexture(); @@ -191,7 +310,7 @@ void HandleCommonMessages(const char *message, const char *value, ScreenManager manager->push(new DisplayLayoutScreen()); } else if (!strcmp(message, "settings") && isActiveScreen && activeScreen->tag() != "settings") { UpdateUIState(UISTATE_MENU); - manager->push(new GameSettingsScreen("")); + manager->push(new GameSettingsScreen(Path())); } else if (!strcmp(message, "language screen") && isActiveScreen) { auto dev = GetI18NCategory("Developer"); auto langScreen = new NewLanguageScreen(dev->T("Language")); @@ -318,10 +437,11 @@ PostProcScreen::PostProcScreen(const std::string &title, int id) : ListPopupScre shaders_ = GetAllPostShaderInfo(); std::vector items; int selected = -1; + const std::string selectedName = id_ >= g_Config.vPostShaderNames.size() ? "Off" : g_Config.vPostShaderNames[id_]; for (int i = 0; i < (int)shaders_.size(); i++) { if (!shaders_[i].visible) continue; - if (shaders_[i].section == g_Config.vPostShaderNames[id_]) + if (shaders_[i].section == selectedName) selected = i; items.push_back(ps->T(shaders_[i].section.c_str(), shaders_[i].name.c_str())); } @@ -331,7 +451,11 @@ PostProcScreen::PostProcScreen(const std::string &title, int id) : ListPopupScre void PostProcScreen::OnCompleted(DialogResult result) { if (result != DR_OK) return; - g_Config.vPostShaderNames[id_] = shaders_[listView_->GetSelected()].section; + const std::string &value = shaders_[listView_->GetSelected()].section; + if (id_ < g_Config.vPostShaderNames.size()) + g_Config.vPostShaderNames[id_] = value; + else + g_Config.vPostShaderNames.push_back(value); } TextureShaderScreen::TextureShaderScreen(const std::string &title) : ListPopupScreen(title) { @@ -361,7 +485,7 @@ NewLanguageScreen::NewLanguageScreen(const std::string &title) : ListPopupScreen #endif langValuesMapping = GetLangValuesMapping(); - std::vector tempLangs; + std::vector tempLangs; VFSGetFileListing("lang", &tempLangs, "ini"); std::vector listing; int selected = -1; @@ -384,7 +508,7 @@ NewLanguageScreen::NewLanguageScreen(const std::string &title) : ListPopupScreen } #endif - FileInfo lang = tempLangs[i]; + File::FileInfo lang = tempLangs[i]; langs_.push_back(lang); std::string code; @@ -430,7 +554,7 @@ void NewLanguageScreen::OnCompleted(DialogResult result) { bool iniLoadedSuccessfully = false; // Allow the lang directory to be overridden for testing purposes (e.g. Android, where it's hard to // test new languages without recompiling the entire app, which is a hassle). - const std::string langOverridePath = GetSysDirectory(DIRECTORY_SYSTEM) + "lang/"; + const Path langOverridePath = GetSysDirectory(DIRECTORY_SYSTEM) / "lang"; // If we run into the unlikely case that "lang" is actually a file, just use the built-in translations. if (!File::Exists(langOverridePath) || !File::IsDirectory(langOverridePath)) @@ -455,15 +579,16 @@ void NewLanguageScreen::OnCompleted(DialogResult result) { void LogoScreen::Next() { if (!switched_) { switched_ = true; + Path gamePath = boot_filename; if (gotoGameSettings_) { - if (boot_filename.size()) { - screenManager()->switchScreen(new EmuScreen(boot_filename)); + if (!gamePath.empty()) { + screenManager()->switchScreen(new EmuScreen(gamePath)); } else { screenManager()->switchScreen(new MainScreen()); } - screenManager()->push(new GameSettingsScreen(boot_filename)); + screenManager()->push(new GameSettingsScreen(gamePath)); } else if (boot_filename.size()) { - screenManager()->switchScreen(new EmuScreen(boot_filename)); + screenManager()->switchScreen(new EmuScreen(gamePath)); } else { screenManager()->switchScreen(new MainScreen()); } @@ -489,7 +614,7 @@ void LogoScreen::update() { void LogoScreen::sendMessage(const char *message, const char *value) { if (!strcmp(message, "boot") && screenManager()->topScreen() == this) { - screenManager()->switchScreen(new EmuScreen(value)); + screenManager()->switchScreen(new EmuScreen(Path(value))); } } diff --git a/UI/MiscScreens.h b/UI/MiscScreens.h index 730f8f7e24..08a142e1fc 100644 --- a/UI/MiscScreens.h +++ b/UI/MiscScreens.h @@ -24,11 +24,12 @@ #include "Common/UI/UIScreen.h" #include "Common/File/DirListing.h" +#include "Common/File/Path.h" struct ShaderInfo; struct TextureShaderInfo; -extern std::string boot_filename; +extern Path boot_filename; void UIBackgroundInit(UIContext &dc); void UIBackgroundShutdown(); @@ -49,7 +50,7 @@ public: void DrawBackground(UIContext &dc) override; void sendMessage(const char *message, const char *value) override; protected: - std::string gamePath_; + Path gamePath_; }; class UIDialogScreenWithBackground : public UIDialogScreen { @@ -64,12 +65,12 @@ protected: class UIDialogScreenWithGameBackground : public UIDialogScreenWithBackground { public: - UIDialogScreenWithGameBackground(const std::string &gamePath) + UIDialogScreenWithGameBackground(const Path &gamePath) : UIDialogScreenWithBackground(), gamePath_(gamePath) {} void DrawBackground(UIContext &dc) override; void sendMessage(const char *message, const char *value) override; protected: - std::string gamePath_; + Path gamePath_; }; class PromptScreen : public UIDialogScreenWithBackground { @@ -100,7 +101,7 @@ private: bool ShowButtons() const override { return true; } std::map> langValuesMapping; std::map titleCodeMapping; - std::vector langs_; + std::vector langs_; }; class PostProcScreen : public ListPopupScreen { diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index a8a863dda5..8246730eba 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -144,7 +144,6 @@ std::string config_filename; // Really need to clean this mess of globals up... but instead I add more :P bool g_TakeScreenshot; -bool g_ShaderNameListChanged = false; static bool isOuya; static bool resized = false; static bool restarting = false; @@ -208,7 +207,7 @@ int Win32Mix(short *buffer, int numSamples, int bits, int rate, int channels) { // globals static LogListener *logger = nullptr; -std::string boot_filename = ""; +Path boot_filename; void NativeHost::InitSound() { #if PPSSPP_PLATFORM(IOS) @@ -322,9 +321,9 @@ static void PostLoadConfig() { #ifndef _WIN32 if (g_Config.currentDirectory.empty()) { #if defined(__ANDROID__) - g_Config.currentDirectory = g_Config.externalDirectory; + g_Config.currentDirectory = g_Config.externalDirectory.ToString(); #elif PPSSPP_PLATFORM(IOS) - g_Config.currentDirectory = g_Config.internalDataDirectory; + g_Config.currentDirectory = g_Config.internalDataDirectory.ToString(); #elif PPSSPP_PLATFORM(SWITCH) g_Config.currentDirectory = "/"; #else @@ -338,7 +337,7 @@ static void PostLoadConfig() { // Allow the lang directory to be overridden for testing purposes (e.g. Android, where it's hard to // test new languages without recompiling the entire app, which is a hassle). - const std::string langOverridePath = GetSysDirectory(DIRECTORY_SYSTEM) + "lang/"; + const Path langOverridePath = GetSysDirectory(DIRECTORY_SYSTEM) / "lang"; // If we run into the unlikely case that "lang" is actually a file, just use the built-in translations. if (!File::Exists(langOverridePath) || !File::IsDirectory(langOverridePath)) @@ -350,8 +349,8 @@ static void PostLoadConfig() { void CreateDirectoriesAndroid() { // On Android, create a PSP directory tree in the external_dir, // to hopefully reduce confusion a bit. - INFO_LOG(IO, "Creating %s", (g_Config.memStickDirectory + "PSP").c_str()); - File::CreateFullPath(g_Config.memStickDirectory + "PSP"); + INFO_LOG(IO, "Creating %s", (g_Config.memStickDirectory / "PSP").c_str()); + File::CreateFullPath(g_Config.memStickDirectory / "PSP"); File::CreateFullPath(GetSysDirectory(DIRECTORY_SAVEDATA)); File::CreateFullPath(GetSysDirectory(DIRECTORY_SAVESTATE)); File::CreateFullPath(GetSysDirectory(DIRECTORY_GAME)); @@ -361,11 +360,11 @@ void CreateDirectoriesAndroid() { // Avoid media scanners in PPSSPP_STATE and SAVEDATA directories, // and in the root PSP directory as well. - File::CreateEmptyFile(GetSysDirectory(DIRECTORY_SAVESTATE) + ".nomedia"); - File::CreateEmptyFile(GetSysDirectory(DIRECTORY_SAVEDATA) + ".nomedia"); - File::CreateEmptyFile(GetSysDirectory(DIRECTORY_SYSTEM) + ".nomedia"); - File::CreateEmptyFile(GetSysDirectory(DIRECTORY_TEXTURES) + ".nomedia"); - File::CreateEmptyFile(GetSysDirectory(DIRECTORY_PLUGINS) + ".nomedia"); + File::CreateEmptyFile(GetSysDirectory(DIRECTORY_SAVESTATE) / ".nomedia"); + File::CreateEmptyFile(GetSysDirectory(DIRECTORY_SAVEDATA) / ".nomedia"); + File::CreateEmptyFile(GetSysDirectory(DIRECTORY_SYSTEM) / ".nomedia"); + File::CreateEmptyFile(GetSysDirectory(DIRECTORY_TEXTURES) / ".nomedia"); + File::CreateEmptyFile(GetSysDirectory(DIRECTORY_PLUGINS) / ".nomedia"); } static void CheckFailedGPUBackends() { @@ -383,11 +382,11 @@ static void CheckFailedGPUBackends() { } lastBackend = g_Config.iGPUBackend; - std::string cache = GetSysDirectory(DIRECTORY_APP_CACHE) + "/FailedGraphicsBackends.txt"; + Path cache = GetSysDirectory(DIRECTORY_APP_CACHE) / "FailedGraphicsBackends.txt"; if (System_GetPropertyBool(SYSPROP_SUPPORTS_PERMISSIONS)) { std::string data; - if (readFileToString(true, cache.c_str(), data)) + if (File::ReadFileToString(true, cache, data)) g_Config.sFailedGPUBackends = data; } @@ -415,7 +414,7 @@ static void CheckFailedGPUBackends() { // Let's try to create, in case it doesn't exist. if (!File::Exists(GetSysDirectory(DIRECTORY_APP_CACHE))) File::CreateDir(GetSysDirectory(DIRECTORY_APP_CACHE)); - writeStringToFile(true, g_Config.sFailedGPUBackends, cache.c_str()); + File::WriteStringToFile(true, g_Config.sFailedGPUBackends, cache); } else { // Just save immediately, since we have storage. g_Config.Save("got storage permission"); @@ -430,7 +429,7 @@ static void ClearFailedGPUBackends() { // In case they update drivers and have totally different problems much later, clear the failed list. g_Config.sFailedGPUBackends.clear(); if (System_GetPropertyBool(SYSPROP_SUPPORTS_PERMISSIONS)) { - File::Delete(GetSysDirectory(DIRECTORY_APP_CACHE) + "/FailedGraphicsBackends.txt"); + File::Delete(GetSysDirectory(DIRECTORY_APP_CACHE) / "FailedGraphicsBackends.txt"); } else { g_Config.Save("clearFailedGPUBackends"); } @@ -461,26 +460,26 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch // We want this to be FIRST. #if PPSSPP_PLATFORM(IOS) // Packed assets are included in app - VFSRegister("", new DirectoryAssetReader(external_dir)); + VFSRegister("", new DirectoryAssetReader(Path(external_dir))); #endif #if defined(ASSETS_DIR) - VFSRegister("", new DirectoryAssetReader(ASSETS_DIR)); + VFSRegister("", new DirectoryAssetReader(Path(ASSETS_DIR))); #endif #if !defined(MOBILE_DEVICE) && !defined(_WIN32) && !PPSSPP_PLATFORM(SWITCH) - VFSRegister("", new DirectoryAssetReader((File::GetExeDirectory() + "assets/").c_str())); - VFSRegister("", new DirectoryAssetReader((File::GetExeDirectory()).c_str())); - VFSRegister("", new DirectoryAssetReader("/usr/local/share/ppsspp/assets/")); - VFSRegister("", new DirectoryAssetReader("/usr/local/share/games/ppsspp/assets/")); - VFSRegister("", new DirectoryAssetReader("/usr/share/ppsspp/assets/")); - VFSRegister("", new DirectoryAssetReader("/usr/share/games/ppsspp/assets/")); + VFSRegister("", new DirectoryAssetReader(File::GetExeDirectory() / "assets")); + VFSRegister("", new DirectoryAssetReader(File::GetExeDirectory())); + VFSRegister("", new DirectoryAssetReader(Path("/usr/local/share/ppsspp/assets"))); + VFSRegister("", new DirectoryAssetReader(Path("/usr/local/share/games/ppsspp/assets"))); + VFSRegister("", new DirectoryAssetReader(Path("/usr/share/ppsspp/assets"))); + VFSRegister("", new DirectoryAssetReader(Path("/usr/share/games/ppsspp/assets"))); #endif #if PPSSPP_PLATFORM(SWITCH) - std::string assetPath = user_data_path + "assets/"; - VFSRegister("", new DirectoryAssetReader(assetPath.c_str())); + Path assetPath = Path(user_data_path) / "assets"; + VFSRegister("", new DirectoryAssetReader(assetPath)); #else - VFSRegister("", new DirectoryAssetReader("assets/")); + VFSRegister("", new DirectoryAssetReader(Path("assets"))); #endif - VFSRegister("", new DirectoryAssetReader(savegame_dir)); + VFSRegister("", new DirectoryAssetReader(Path(savegame_dir))); #if (defined(MOBILE_DEVICE) || !defined(USING_QT_UI)) && !PPSSPP_PLATFORM(UWP) if (host == nullptr) { @@ -488,8 +487,8 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch } #endif - g_Config.internalDataDirectory = savegame_dir; - g_Config.externalDirectory = external_dir; + g_Config.internalDataDirectory = Path(savegame_dir); + g_Config.externalDirectory = Path(external_dir); #if defined(__ANDROID__) // TODO: This needs to change in Android 12. @@ -497,24 +496,27 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch // Maybe there should be an option to use internal memory instead, but I think // that for most people, using external memory (SDCard/USB Storage) makes the // most sense. - g_Config.memStickDirectory = std::string(external_dir) + "/"; - g_Config.flash0Directory = std::string(external_dir) + "/flash0/"; + g_Config.memStickDirectory = Path(external_dir); + g_Config.flash0Directory = Path(external_dir) / "flash0"; - std::string memstickDirFile = g_Config.internalDataDirectory + "/memstick_dir.txt"; + Path memstickDirFile = g_Config.internalDataDirectory / "memstick_dir.txt"; if (File::Exists(memstickDirFile)) { std::string memstickDir; - readFileToString(true, memstickDirFile.c_str(), memstickDir); - if (!memstickDir.empty() && File::Exists(memstickDir)) { - g_Config.memStickDirectory = memstickDir + "/"; + File::ReadFileToString(true, memstickDirFile, memstickDir); + Path memstickPath(memstickDir); + if (!memstickPath.empty() && File::Exists(memstickPath)) { + g_Config.memStickDirectory = memstickPath; + } else { + ERROR_LOG(SYSTEM, "Couldn't read directory '%s' specified by memstick_dir.txt.", memstickDir.c_str()); } } #elif PPSSPP_PLATFORM(IOS) - g_Config.memStickDirectory = user_data_path; - g_Config.flash0Directory = std::string(external_dir) + "/flash0/"; + g_Config.memStickDirectory = Path(user_data_path); + g_Config.flash0Directory = Path(std::string(external_dir)) / "flash0"; #elif PPSSPP_PLATFORM(SWITCH) - g_Config.memStickDirectory = g_Config.internalDataDirectory + "config/ppsspp/"; - g_Config.flash0Directory = g_Config.internalDataDirectory + "assets/flash0/"; -#elif !defined(_WIN32) + g_Config.memStickDirectory = g_Config.internalDataDirectory / "config/ppsspp"; + g_Config.flash0Directory = g_Config.internalDataDirectory / "assets/flash0"; +#elif !PPSSPP_PLATFORM(WINDOWS) std::string config; if (getenv("XDG_CONFIG_HOME") != NULL) config = getenv("XDG_CONFIG_HOME"); @@ -523,20 +525,20 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch else // Just in case config = "./config"; - g_Config.memStickDirectory = config + "/ppsspp/"; - g_Config.flash0Directory = File::GetExeDirectory() + "/assets/flash0/"; + g_Config.memStickDirectory = Path(config) / "ppsspp"; + g_Config.flash0Directory = File::GetExeDirectory() / "assets/flash0"; #endif if (cache_dir && strlen(cache_dir)) { - DiskCachingFileLoaderCache::SetCacheDir(cache_dir); - g_Config.appCacheDirectory = cache_dir; + g_Config.appCacheDirectory = Path(cache_dir); + DiskCachingFileLoaderCache::SetCacheDir(g_Config.appCacheDirectory); } if (!LogManager::GetInstance()) LogManager::Init(&g_Config.bEnableLogging); -#ifndef _WIN32 - g_Config.AddSearchPath(user_data_path); +#if !PPSSPP_PLATFORM(WINDOWS) + g_Config.AddSearchPath(Path(user_data_path)); g_Config.AddSearchPath(GetSysDirectory(DIRECTORY_SYSTEM)); g_Config.SetDefaultPath(GetSysDirectory(DIRECTORY_SYSTEM)); @@ -546,18 +548,18 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch #endif LogManager *logman = LogManager::GetInstance(); -#ifdef __ANDROID__ +#if PPSSPP_PLATFORM(ANDROID) // On early versions of Android we don't need to ask permission. CreateDirectoriesAndroid(); #endif const char *fileToLog = 0; - const char *stateToLoad = 0; + Path stateToLoad; bool gotBootFilename = false; bool gotoGameSettings = false; bool gotoTouchScreenTest = false; - boot_filename = ""; + boot_filename.clear(); // Parse command line LogTypes::LOG_LEVELS logLevel = LogTypes::LINFO; @@ -598,7 +600,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch if (!strncmp(argv[i], "--log=", strlen("--log=")) && strlen(argv[i]) > strlen("--log=")) fileToLog = argv[i] + strlen("--log="); if (!strncmp(argv[i], "--state=", strlen("--state=")) && strlen(argv[i]) > strlen("--state=")) - stateToLoad = argv[i] + strlen("--state="); + stateToLoad = Path(std::string(argv[i] + strlen("--state="))); #if !defined(MOBILE_DEVICE) if (!strncmp(argv[i], "--escape-exit", strlen("--escape-exit"))) g_Config.bPauseExitsEmulator = true; @@ -642,10 +644,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch } } if (okToLoad) { - boot_filename = argv[i]; -#ifdef _WIN32 - boot_filename = ReplaceAll(boot_filename, "\\", "/"); -#endif + boot_filename = Path(std::string(argv[i])); skipLogo = true; } if (okToLoad && okToCheck) { @@ -741,7 +740,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch // TODO: Load these in the background instead of synchronously. g_BackgroundAudio.LoadSamples(); - if (!boot_filename.empty() && stateToLoad != NULL) { + if (!boot_filename.empty() && stateToLoad.Valid()) { SaveState::Load(stateToLoad, -1, [](SaveState::Status status, const std::string &message, void *) { if (!message.empty() && (!g_Config.bDumpFrames || !g_Config.bDumpVideoOutput)) { osm.Show(message, status == SaveState::Status::SUCCESS ? 2.0 : 5.0); @@ -1005,10 +1004,7 @@ void NativeShutdownGraphics() { void TakeScreenshot() { g_TakeScreenshot = false; - std::string path = GetSysDirectory(DIRECTORY_SCREENSHOT); - while (path.length() > 0 && path.back() == '/') { - path.resize(path.size() - 1); - } + Path path = GetSysDirectory(DIRECTORY_SCREENSHOT); if (!File::Exists(path)) { File::CreateDir(path); } @@ -1018,21 +1014,21 @@ void TakeScreenshot() { std::string gameId = g_paramSFO.GetDiscID(); - char filename[2048]; + Path filename; while (i < 10000){ if (g_Config.bScreenshotsAsPNG) - snprintf(filename, sizeof(filename), "%s/%s_%05d.png", path.c_str(), gameId.c_str(), i); + filename = path / StringFromFormat("%s_%05d.png", path.c_str(), gameId.c_str(), i); else - snprintf(filename, sizeof(filename), "%s/%s_%05d.jpg", path.c_str(), gameId.c_str(), i); - FileInfo info; - if (!getFileInfo(filename, &info)) + filename = path / StringFromFormat("%s_%05d.jpg", path.c_str(), gameId.c_str(), i); + File::FileInfo info; + if (!File::Exists(filename)) break; i++; } bool success = TakeGameScreenshot(filename, g_Config.bScreenshotsAsPNG ? ScreenshotFormat::PNG : ScreenshotFormat::JPG, g_Config.bScreenshotsAtRenderRes ? SCREENSHOT_DISPLAY : SCREENSHOT_OUTPUT); if (success) { - osm.Show(filename); + osm.Show(filename.ToVisualString()); } else { auto err = GetI18NCategory("Error"); osm.Show(err->T("Could not save screenshot file")); @@ -1167,11 +1163,11 @@ void HandleGlobalMessage(const std::string &msg, const std::string &value) { } if (msg == "bgImage_updated") { if (!value.empty()) { - std::string dest = GetSysDirectory(DIRECTORY_SYSTEM) + (endsWithNoCase(value, ".jpg") ? "background.jpg" : "background.png"); - File::Copy(value, dest); + Path dest = GetSysDirectory(DIRECTORY_SYSTEM) / (endsWithNoCase(value, ".jpg") ? "background.jpg" : "background.png"); + File::Copy(Path(value), dest); } UIBackgroundShutdown(); - UIBackgroundInit(*uiContext); + // It will init again automatically. We can't init outside a frame on Vulkan. } if (msg == "savestate_displayslot") { auto sy = GetI18NCategory("System"); diff --git a/UI/PauseScreen.cpp b/UI/PauseScreen.cpp index 51a43dae9b..f4108a85f8 100644 --- a/UI/PauseScreen.cpp +++ b/UI/PauseScreen.cpp @@ -48,7 +48,7 @@ #include "UI/OnScreenDisplay.h" #include "UI/GameInfoCache.h" -AsyncImageFileView::AsyncImageFileView(const std::string &filename, UI::ImageSizeMode sizeMode, PrioritizedWorkQueue *wq, UI::LayoutParams *layoutParams) +AsyncImageFileView::AsyncImageFileView(const Path &filename, UI::ImageSizeMode sizeMode, UI::LayoutParams *layoutParams) : UI::Clickable(layoutParams), canFocus_(true), filename_(filename), color_(0xFFFFFFFF), sizeMode_(sizeMode), textureFailed_(false), fixedSizeW_(0.0f), fixedSizeH_(0.0f) {} AsyncImageFileView::~AsyncImageFileView() {} @@ -102,7 +102,7 @@ void AsyncImageFileView::GetContentDimensionsBySpec(const UIContext &dc, UI::Mea } } -void AsyncImageFileView::SetFilename(std::string filename) { +void AsyncImageFileView::SetFilename(const Path &filename) { if (filename_ != filename) { textureFailed_ = false; filename_ = filename; @@ -159,7 +159,7 @@ void AsyncImageFileView::Draw(UIContext &dc) { class ScreenshotViewScreen : public PopupScreen { public: - ScreenshotViewScreen(std::string filename, std::string title, int slot, std::shared_ptr i18n) + ScreenshotViewScreen(const Path &filename, std::string title, int slot, std::shared_ptr i18n) : PopupScreen(title, i18n->T("Load State"), "Back"), filename_(filename), slot_(slot) {} // PopupScreen will translate Back on its own int GetSlot() const { @@ -179,17 +179,17 @@ protected: UI::LinearLayout *content = new UI::LinearLayout(UI::ORIENT_VERTICAL); parent->Add(content); UI::Margins contentMargins(10, 0); - content->Add(new AsyncImageFileView(filename_, UI::IS_KEEP_ASPECT, nullptr, new UI::LinearLayoutParams(480, 272, contentMargins)))->SetCanBeFocused(false); + content->Add(new AsyncImageFileView(filename_, UI::IS_KEEP_ASPECT, new UI::LinearLayoutParams(480, 272, contentMargins)))->SetCanBeFocused(false); } private: - std::string filename_; + Path filename_; int slot_; }; class SaveSlotView : public UI::LinearLayout { public: - SaveSlotView(const std::string &gamePath, int slot, UI::LayoutParams *layoutParams = nullptr); + SaveSlotView(const Path &gamePath, int slot, UI::LayoutParams *layoutParams = nullptr); void GetContentDimensions(const UIContext &dc, float &w, float &h) const override { w = 500; h = 90; @@ -201,7 +201,7 @@ public: return slot_; } - std::string GetScreenshotFilename() const { + Path GetScreenshotFilename() const { return screenshotFilename_; } @@ -222,18 +222,17 @@ private: UI::Button *loadStateButton_ = nullptr; int slot_; - std::string gamePath_; - std::string screenshotFilename_; + Path gamePath_; + Path screenshotFilename_; }; -SaveSlotView::SaveSlotView(const std::string &gameFilename, int slot, UI::LayoutParams *layoutParams) : UI::LinearLayout(UI::ORIENT_HORIZONTAL, layoutParams), slot_(slot), gamePath_(gameFilename) { +SaveSlotView::SaveSlotView(const Path &gameFilename, int slot, UI::LayoutParams *layoutParams) : UI::LinearLayout(UI::ORIENT_HORIZONTAL, layoutParams), slot_(slot), gamePath_(gameFilename) { using namespace UI; screenshotFilename_ = SaveState::GenerateSaveSlotFilename(gamePath_, slot, SaveState::SCREENSHOT_EXTENSION); - PrioritizedWorkQueue *wq = g_gameInfoCache->WorkQueue(); Add(new Spacer(5)); - AsyncImageFileView *fv = Add(new AsyncImageFileView(screenshotFilename_, IS_DEFAULT, wq, new UI::LayoutParams(80 * 2, 45 * 2))); + AsyncImageFileView *fv = Add(new AsyncImageFileView(screenshotFilename_, IS_DEFAULT, new UI::LayoutParams(82 * 2, 47 * 2))); fv->SetOverlayText(StringFromFormat("%d", slot_ + 1)); auto pa = GetI18NCategory("Pause"); @@ -262,7 +261,7 @@ SaveSlotView::SaveSlotView(const std::string &gameFilename, int slot, UI::Layout } } } else { - fv->SetFilename(""); + fv->SetFilename(Path()); } } @@ -423,7 +422,7 @@ UI::EventReturn GamePauseScreen::OnScreenshotClicked(UI::EventParams &e) { int slot = v->GetSlot(); g_Config.iCurrentStateSlot = v->GetSlot(); if (SaveState::HasSaveInSlot(gamePath_, slot)) { - std::string fn = v->GetScreenshotFilename(); + Path fn = v->GetScreenshotFilename(); std::string title = v->GetScreenshotTitle(); auto pa = GetI18NCategory("Pause"); Screen *screen = new ScreenshotViewScreen(fn, title, v->GetSlot(), pa); diff --git a/UI/PauseScreen.h b/UI/PauseScreen.h index 926302c141..e45a1a93ec 100644 --- a/UI/PauseScreen.h +++ b/UI/PauseScreen.h @@ -20,6 +20,7 @@ #include #include +#include "Common/File/Path.h" #include "Common/UI/UIScreen.h" #include "Common/UI/ViewGroup.h" #include "UI/MiscScreens.h" @@ -27,7 +28,7 @@ class GamePauseScreen : public UIDialogScreenWithGameBackground { public: - GamePauseScreen(const std::string &filename) : UIDialogScreenWithGameBackground(filename), gamePath_(filename) {} + GamePauseScreen(const Path &filename) : UIDialogScreenWithGameBackground(filename), gamePath_(filename) {} virtual ~GamePauseScreen(); virtual void dialogFinished(const Screen *dialog, DialogResult dr) override; @@ -55,16 +56,14 @@ private: // hack bool finishNextFrame_ = false; - std::string gamePath_; + Path gamePath_; }; -class PrioritizedWorkQueue; - // AsyncImageFileView loads a texture from a file, and reloads it as necessary. // TODO: Actually make async, doh. class AsyncImageFileView : public UI::Clickable { public: - AsyncImageFileView(const std::string &filename, UI::ImageSizeMode sizeMode, PrioritizedWorkQueue *wq, UI::LayoutParams *layoutParams = 0); + AsyncImageFileView(const Path &filename, UI::ImageSizeMode sizeMode, UI::LayoutParams *layoutParams = 0); ~AsyncImageFileView(); void GetContentDimensionsBySpec(const UIContext &dc, UI::MeasureSpec horiz, UI::MeasureSpec vert, float &w, float &h) const override; @@ -74,7 +73,7 @@ public: void DeviceLost() override; void DeviceRestored(Draw::DrawContext *draw) override; - void SetFilename(std::string filename); + void SetFilename(const Path &filename); void SetColor(uint32_t color) { color_ = color; } void SetOverlayText(std::string text) { text_ = text; } void SetFixedSize(float fixW, float fixH) { fixedSizeW_ = fixW; fixedSizeH_ = fixH; } @@ -82,11 +81,11 @@ public: bool CanBeFocused() const override { return canFocus_; } - const std::string &GetFilename() const { return filename_; } + const Path &GetFilename() const { return filename_; } private: bool canFocus_; - std::string filename_; + Path filename_; std::string text_; uint32_t color_; UI::ImageSizeMode sizeMode_; diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index 6ce5950fda..b5d80bf76d 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -117,6 +117,8 @@ bool RemoteISOConnectScreen::FindServer(std::string &resultHost, int &resultPort statusMessage_ = formatted; }; + http.SetUserAgent(StringFromFormat("PPSSPP/%s", PPSSPP_GIT_VERSION)); + auto TryServer = [&](const std::string &host, int port) { SetStatus("Resolving [URL]...", host, port); if (!http.Resolve(host.c_str(), port)) { @@ -134,7 +136,8 @@ bool RemoteISOConnectScreen::FindServer(std::string &resultHost, int &resultPort } SetStatus("Loading game list from [URL]...", host, port); - code = http.GET(subdir.c_str(), &result); + http::RequestProgress progress(&scanCancelled); + code = http.GET(subdir.c_str(), &result, &progress); http.Disconnect(); if (code != 200) { @@ -187,7 +190,8 @@ bool RemoteISOConnectScreen::FindServer(std::string &resultHost, int &resultPort SetStatus("Looking for peers...", "", 0); if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT)) { if (http.Connect(2, 20.0, &scanCancelled)) { - code = http.GET("/match/list", &result); + http::RequestProgress progress(&scanCancelled); + code = http.GET("/match/list", &result, &progress); http.Disconnect(); } } @@ -239,15 +243,15 @@ bool RemoteISOConnectScreen::FindServer(std::string &resultHost, int &resultPort return false; } -static bool LoadGameList(const std::string &url, std::vector &games) { +static bool LoadGameList(const Path &url, std::vector &games) { PathBrowser browser(url); - std::vector files; + std::vector files; browser.GetListing(files, "iso:cso:pbp:elf:prx:ppdmp:", &scanCancelled); if (scanCancelled) { return false; } for (auto &file : files) { - if (RemoteISOFileSupported(file.name)) { + if (file.isDirectory || RemoteISOFileSupported(file.name)) { games.push_back(file.fullName); } } @@ -478,7 +482,7 @@ ScanStatus RemoteISOConnectScreen::GetStatus() { void RemoteISOConnectScreen::ExecuteLoad() { std::string subdir = RemoteSubdir(); url_ = StringFromFormat("http://%s:%d%s", host_.c_str(), port_, subdir.c_str()); - bool result = LoadGameList(url_, games_); + bool result = LoadGameList(Path(url_), games_); if (scanAborted) { return; } @@ -494,29 +498,20 @@ void RemoteISOConnectScreen::ExecuteLoad() { class RemoteGameBrowser : public GameBrowser { public: - RemoteGameBrowser(const std::string &url, const std::vector &games, BrowseFlags browseFlags, bool *gridStyle_, ScreenManager* screenManager, std::string lastText, std::string lastLink, UI::LayoutParams *layoutParams = nullptr) - : GameBrowser(url, browseFlags, gridStyle_, screenManager, lastText, lastLink, layoutParams) { - games_ = games; - Refresh(); + RemoteGameBrowser(const Path &url, BrowseFlags browseFlags, bool *gridStyle_, ScreenManager *screenManager, std::string lastText, std::string lastLink, UI::LayoutParams *layoutParams = nullptr) + : GameBrowser(url, browseFlags, gridStyle_, screenManager, lastText, lastLink, layoutParams) { + initialPath_ = url; } protected: - bool DisplayTopBar() override { - return false; + Path HomePath() override { + return initialPath_; } - bool HasSpecialFiles(std::vector &filenames) override; - - std::string url_; - std::vector games_; + Path initialPath_; }; -bool RemoteGameBrowser::HasSpecialFiles(std::vector &filenames) { - filenames = games_; - return true; -} - -RemoteISOBrowseScreen::RemoteISOBrowseScreen(const std::string &url, const std::vector &games) +RemoteISOBrowseScreen::RemoteISOBrowseScreen(const std::string &url, const std::vector &games) : url_(url), games_(games) { } @@ -537,8 +532,8 @@ void RemoteISOBrowseScreen::CreateViews() { ScrollView *scrollRecentGames = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scrollRecentGames->SetTag("RemoteGamesTab"); - RemoteGameBrowser *tabRemoteGames = new RemoteGameBrowser( - url_, games_, BrowseFlags::PIN, &g_Config.bGridView1, screenManager(), "", "", + GameBrowser *tabRemoteGames = new RemoteGameBrowser( + Path(url_), BrowseFlags::PIN | BrowseFlags::NAVIGATE, &g_Config.bGridView1, screenManager(), "", "", new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); scrollRecentGames->Add(tabRemoteGames); gameBrowsers_.push_back(tabRemoteGames); diff --git a/UI/RemoteISOScreen.h b/UI/RemoteISOScreen.h index 7eab2270e0..bfcac815be 100644 --- a/UI/RemoteISOScreen.h +++ b/UI/RemoteISOScreen.h @@ -76,18 +76,18 @@ protected: std::string host_; int port_; std::string url_; - std::vector games_; + std::vector games_; }; class RemoteISOBrowseScreen : public MainScreen { public: - RemoteISOBrowseScreen(const std::string &url, const std::vector &games); + RemoteISOBrowseScreen(const std::string &url, const std::vector &games); protected: void CreateViews() override; std::string url_; - std::vector games_; + std::vector games_; }; class RemoteISOSettingsScreen : public UIDialogScreenWithBackground { diff --git a/UI/ReportScreen.cpp b/UI/ReportScreen.cpp index a5cf54e098..3aa52b1365 100644 --- a/UI/ReportScreen.cpp +++ b/UI/ReportScreen.cpp @@ -158,7 +158,7 @@ void CompatRatingChoice::SetupChoices() { AddChoice(4, rp->T("Nothing")); } -ReportScreen::ReportScreen(const std::string &gamePath) +ReportScreen::ReportScreen(const Path &gamePath) : UIDialogScreenWithGameBackground(gamePath) { enableReporting_ = Reporting::IsEnabled(); ratingEnabled_ = enableReporting_; @@ -168,12 +168,12 @@ void ReportScreen::postRender() { // We do this after render because we need it to be within the frame (so the screenshot works). // We could do it mid frame, but then we have to reapply viewport/scissor. if (!tookScreenshot_) { - std::string path = GetSysDirectory(DIRECTORY_SCREENSHOT); + Path path = GetSysDirectory(DIRECTORY_SCREENSHOT); if (!File::Exists(path)) { File::CreateDir(path); } - screenshotFilename_ = path + ".reporting.jpg"; - if (TakeGameScreenshot(screenshotFilename_.c_str(), ScreenshotFormat::JPG, SCREENSHOT_DISPLAY, nullptr, nullptr, 4)) { + screenshotFilename_ = path / ".reporting.jpg"; + if (TakeGameScreenshot(screenshotFilename_, ScreenshotFormat::JPG, SCREENSHOT_DISPLAY, nullptr, nullptr, 4)) { // Redo the views already, now with a screenshot included. RecreateViews(); } else { @@ -285,7 +285,7 @@ void ReportScreen::CreateViews() { if (tookScreenshot_ && !screenshotFilename_.empty()) { leftColumnItems->Add(new CheckBox(&includeScreenshot_, rp->T("FeedbackIncludeScreen", "Include a screenshot")))->SetEnabledPtr(&enableReporting_); - screenshot_ = leftColumnItems->Add(new AsyncImageFileView(screenshotFilename_, IS_KEEP_ASPECT, nullptr, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, Margins(12, 0)))); + screenshot_ = leftColumnItems->Add(new AsyncImageFileView(screenshotFilename_, IS_KEEP_ASPECT, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, Margins(12, 0)))); } else { if (tookScreenshot_) { includeScreenshot_ = false; @@ -381,7 +381,7 @@ EventReturn ReportScreen::HandleSubmit(EventParams &e) { g_Config.Save("ReportScreen::HandleSubmit"); } - std::string filename = tookScreenshot_ && includeScreenshot_ ? screenshotFilename_ : ""; + std::string filename = tookScreenshot_ && includeScreenshot_ ? screenshotFilename_.ToString() : ""; Reporting::ReportCompatibility(compat, graphics_ + 1, speed_ + 1, gameplay_ + 1, filename); TriggerFinish(DR_OK); screenManager()->push(new ReportFinishScreen(gamePath_, overall_)); @@ -400,7 +400,7 @@ EventReturn ReportScreen::HandleShowCRC(EventParams &e) { return EVENT_DONE; } -ReportFinishScreen::ReportFinishScreen(const std::string &gamePath, ReportingOverallScore score) +ReportFinishScreen::ReportFinishScreen(const Path &gamePath, ReportingOverallScore score) : UIDialogScreenWithGameBackground(gamePath), score_(score) { } diff --git a/UI/ReportScreen.h b/UI/ReportScreen.h index 4a99dd5739..a7d132b2c8 100644 --- a/UI/ReportScreen.h +++ b/UI/ReportScreen.h @@ -22,6 +22,7 @@ #include "Common/UI/UIScreen.h" #include "Common/UI/ViewGroup.h" #include "UI/MiscScreens.h" +#include "Common/File/Path.h" enum class ReportingOverallScore : int { PERFECT = 0, @@ -34,7 +35,7 @@ enum class ReportingOverallScore : int { class ReportScreen : public UIDialogScreenWithGameBackground { public: - ReportScreen(const std::string &gamePath); + ReportScreen(const Path &gamePath); protected: void postRender() override; @@ -57,7 +58,7 @@ protected: UI::TextView *overallDescription_ = nullptr; UI::TextView *crcInfo_ = nullptr; UI::Choice *showCrcButton_ = nullptr; - std::string screenshotFilename_; + Path screenshotFilename_; ReportingOverallScore overall_ = ReportingOverallScore::INVALID; int graphics_ = -1; @@ -72,7 +73,7 @@ protected: class ReportFinishScreen : public UIDialogScreenWithGameBackground { public: - ReportFinishScreen(const std::string &gamePath, ReportingOverallScore score); + ReportFinishScreen(const Path &gamePath, ReportingOverallScore score); protected: void update() override; diff --git a/UI/SavedataScreen.cpp b/UI/SavedataScreen.cpp index 245897b8f5..97fe8cb6d1 100644 --- a/UI/SavedataScreen.cpp +++ b/UI/SavedataScreen.cpp @@ -25,7 +25,7 @@ #include "Common/Math/curves.h" #include "Common/System/NativeApp.h" #include "Common/System/System.h" -#include "Common/Thread/PrioritizedWorkQueue.h" +#include "Common/Data/Encoding/Utf8.h" #include "Common/UI/Context.h" #include "Common/UI/View.h" #include "Common/UI/ViewGroup.h" @@ -46,7 +46,7 @@ class SavedataButton; -std::string GetFileDateAsString(std::string filename) { +std::string GetFileDateAsString(const Path &filename) { tm time; if (File::GetModifTime(filename, time)) { char buf[256]; @@ -106,16 +106,15 @@ public: topright->SetSpacing(1.0f); topright->Add(new TextView(savedata_title, ALIGN_LEFT | FLAG_WRAP_TEXT, false))->SetTextColor(textStyle.fgColor); topright->Add(new TextView(StringFromFormat("%lld kB", ginfo->gameSize / 1024), 0, true))->SetTextColor(textStyle.fgColor); - topright->Add(new TextView(GetFileDateAsString(savePath_ + "/PARAM.SFO"), 0, true))->SetTextColor(textStyle.fgColor); + topright->Add(new TextView(GetFileDateAsString(savePath_ / "PARAM.SFO"), 0, true))->SetTextColor(textStyle.fgColor); toprow->Add(topright); content->Add(new Spacer(3.0)); content->Add(new TextView(ReplaceAll(savedata_detail, "\r", ""), ALIGN_LEFT | FLAG_WRAP_TEXT, true, new LinearLayoutParams(Margins(10, 0))))->SetTextColor(textStyle.fgColor); content->Add(new Spacer(3.0)); } else { - std::string image_path = ReplaceAll(savePath_, ".ppst", ".jpg"); + Path image_path = savePath_.WithReplacedExtension(".ppst", ".jpg"); if (File::Exists(image_path)) { - PrioritizedWorkQueue *wq = g_gameInfoCache->WorkQueue(); - toprow->Add(new AsyncImageFileView(image_path, IS_KEEP_ASPECT, wq, new LinearLayoutParams(480, 272, Margins(10, 0)))); + toprow->Add(new AsyncImageFileView(image_path, IS_KEEP_ASPECT, new LinearLayoutParams(480, 272, Margins(10, 0)))); } else { toprow->Add(new TextView(sa->T("No screenshot"), new LinearLayoutParams(Margins(10, 5))))->SetTextColor(textStyle.fgColor); } @@ -134,7 +133,7 @@ protected: private: UI::EventReturn OnDeleteButtonClick(UI::EventParams &e); - std::string savePath_; + Path savePath_; }; class SortedLinearLayout : public UI::LinearLayoutList { @@ -170,9 +169,9 @@ void SortedLinearLayout::Update() { class SavedataButton : public UI::Clickable { public: - SavedataButton(const std::string &gamePath, UI::LayoutParams *layoutParams = 0) + SavedataButton(const Path &gamePath, UI::LayoutParams *layoutParams = 0) : UI::Clickable(layoutParams), savePath_(gamePath) { - SetTag(gamePath); + SetTag(gamePath.ToString()); } void Draw(UIContext &dc) override; @@ -183,12 +182,12 @@ public: h = 74; } - const std::string &GamePath() const { return savePath_; } + const Path &GamePath() const { return savePath_; } private: void UpdateText(const std::shared_ptr &ginfo); - std::string savePath_; + Path savePath_; std::string title_; std::string subtitle_; }; @@ -346,12 +345,13 @@ std::string SavedataButton::DescribeText() const { return ReplaceAll(u->T("%1 button"), "%1", title_) + "\n" + subtitle_; } -SavedataBrowser::SavedataBrowser(std::string path, UI::LayoutParams *layoutParams) +SavedataBrowser::SavedataBrowser(const Path &path, UI::LayoutParams *layoutParams) : LinearLayout(UI::ORIENT_VERTICAL, layoutParams), path_(path) { Refresh(); } void SavedataBrowser::Update() { + LinearLayout::Update(); if (searchPending_) { searchPending_ = false; @@ -429,7 +429,7 @@ static time_t GetTotalSize(const SavedataButton *b) { switch (Identify_File(fileLoader.get())) { case IdentifiedFileType::PSP_PBP_DIRECTORY: case IdentifiedFileType::PSP_SAVEDATA_DIRECTORY: - return getDirectoryRecursiveSize(ResolvePBPDirectory(b->GamePath()), nullptr, GETFILES_GETHIDDEN); + return File::GetDirectoryRecursiveSize(ResolvePBPDirectory(b->GamePath()), nullptr, File::GETFILES_GETHIDDEN); default: return fileLoader->FileSize(); @@ -450,7 +450,7 @@ static time_t GetDateSeconds(const SavedataButton *b) { tm datetm; bool success; if (Identify_File(fileLoader.get()) == IdentifiedFileType::PSP_SAVEDATA_DIRECTORY) { - success = File::GetModifTime(b->GamePath() + "/PARAM.SFO", datetm); + success = File::GetModifTime(b->GamePath() / "PARAM.SFO", datetm); } else { success = File::GetModifTime(b->GamePath(), datetm); } @@ -487,14 +487,14 @@ void SavedataBrowser::Refresh() { // Find games in the current directory and create new ones. std::vector savedataButtons; - std::vector fileInfo; - getFilesInDir(path_.c_str(), &fileInfo, "ppst:"); + std::vector fileInfo; + GetFilesInDir(path_, &fileInfo, "ppst:"); for (size_t i = 0; i < fileInfo.size(); i++) { bool isState = !fileInfo[i].isDirectory; bool isSaveData = false; - if (!isState && File::Exists(path_ + fileInfo[i].name + "/PARAM.SFO")) + if (!isState && File::Exists(path_ / fileInfo[i].name / "PARAM.SFO")) isSaveData = true; if (isSaveData || isState) { @@ -537,13 +537,13 @@ UI::EventReturn SavedataBrowser::SavedataButtonClick(UI::EventParams &e) { SavedataButton *button = static_cast(e.v); UI::EventParams e2{}; e2.v = e.v; - e2.s = button->GamePath(); + e2.s = button->GamePath().ToString(); // Insta-update - here we know we are already on the right thread. OnChoice.Trigger(e2); return UI::EVENT_DONE; } -SavedataScreen::SavedataScreen(std::string gamePath) : UIDialogScreenWithGameBackground(gamePath) { +SavedataScreen::SavedataScreen(const Path &gamePath) : UIDialogScreenWithGameBackground(gamePath) { } SavedataScreen::~SavedataScreen() { @@ -557,8 +557,8 @@ void SavedataScreen::CreateViews() { using namespace UI; auto sa = GetI18NCategory("Savedata"); auto di = GetI18NCategory("Dialog"); - std::string savedata_dir = GetSysDirectory(DIRECTORY_SAVEDATA); - std::string savestate_dir = GetSysDirectory(DIRECTORY_SAVESTATE); + Path savedata_dir = GetSysDirectory(DIRECTORY_SAVEDATA); + Path savestate_dir = GetSysDirectory(DIRECTORY_SAVESTATE); gridStyle_ = false; root_ = new AnchorLayout(); @@ -627,7 +627,7 @@ UI::EventReturn SavedataScreen::OnSearch(UI::EventParams &e) { } UI::EventReturn SavedataScreen::OnSavedataButtonClick(UI::EventParams &e) { - std::shared_ptr ginfo = g_gameInfoCache->GetInfo(screenManager()->getDrawContext(), e.s, 0); + std::shared_ptr ginfo = g_gameInfoCache->GetInfo(screenManager()->getDrawContext(), Path(e.s), 0); SavedataPopupScreen *popupScreen = new SavedataPopupScreen(e.s, ginfo->GetTitle()); if (e.v) { popupScreen->SetPopupOrigin(e.v); diff --git a/UI/SavedataScreen.h b/UI/SavedataScreen.h index c0909bf321..b9d057d025 100644 --- a/UI/SavedataScreen.h +++ b/UI/SavedataScreen.h @@ -20,6 +20,8 @@ #include #include +#include "Common/File/Path.h" + #include "Common/UI/UIScreen.h" #include "Common/UI/View.h" #include "Common/UI/ViewGroup.h" @@ -34,7 +36,7 @@ enum class SavedataSortOption { class SavedataBrowser : public UI::LinearLayout { public: - SavedataBrowser(std::string path, UI::LayoutParams *layoutParams = 0); + SavedataBrowser(const Path &path, UI::LayoutParams *layoutParams = 0); void Update() override; @@ -56,7 +58,7 @@ private: UI::ViewGroup *gameList_ = nullptr; UI::TextView *noMatchView_ = nullptr; UI::TextView *searchingView_ = nullptr; - std::string path_; + Path path_; std::string searchFilter_; bool searchPending_ = false; }; @@ -64,7 +66,7 @@ private: class SavedataScreen : public UIDialogScreenWithGameBackground { public: // gamePath can be empty, in that case this screen will show all savedata in the save directory. - SavedataScreen(std::string gamePath); + SavedataScreen(const Path &gamePath); ~SavedataScreen(); void dialogFinished(const Screen *dialog, DialogResult result) override; diff --git a/UI/Store.cpp b/UI/Store.cpp index 477cb7526d..3461db5a1f 100644 --- a/UI/Store.cpp +++ b/UI/Store.cpp @@ -134,7 +134,7 @@ void HttpImageFileView::DownloadCompletedCallback(http::Download &download) { void HttpImageFileView::Draw(UIContext &dc) { using namespace Draw; if (!texture_ && !textureFailed_ && !path_.empty() && !download_) { - download_ = downloader_->StartDownloadWithCallback(path_, "", std::bind(&HttpImageFileView::DownloadCompletedCallback, this, std::placeholders::_1)); + download_ = downloader_->StartDownloadWithCallback(path_, Path(), std::bind(&HttpImageFileView::DownloadCompletedCallback, this, std::placeholders::_1)); download_->SetHidden(true); } @@ -222,11 +222,13 @@ private: bool IsGameInstalled() { return g_GameManager.IsGameInstalled(entry_.file); } + std::string DownloadURL(); StoreEntry entry_; UI::Button *installButton_ = nullptr; UI::Button *launchButton_ = nullptr; UI::Button *cancelButton_ = nullptr; + UI::TextView *speedView_ = nullptr; bool wasInstalled_ = false; }; @@ -243,12 +245,19 @@ void ProductView::CreateViews() { auto st = GetI18NCategory("Store"); auto di = GetI18NCategory("Dialog"); wasInstalled_ = IsGameInstalled(); + bool isDownloading = g_GameManager.IsDownloading(DownloadURL()); if (!wasInstalled_) { launchButton_ = nullptr; - installButton_ = Add(new Button(st->T("Install"))); + LinearLayout *progressDisplay = new LinearLayout(ORIENT_HORIZONTAL); + installButton_ = progressDisplay->Add(new Button(st->T("Install"))); installButton_->OnClick.Handle(this, &ProductView::OnInstall); + + speedView_ = progressDisplay->Add(new TextView("")); + speedView_->SetVisibility(isDownloading ? V_VISIBLE : V_GONE); + Add(progressDisplay); } else { installButton_ = nullptr; + speedView_ = nullptr; Add(new TextView(st->T("Already Installed"))); Add(new Button(st->T("Uninstall")))->OnClick.Handle(this, &ProductView::OnUninstall); launchButton_ = new Button(st->T("Launch Game")); @@ -258,7 +267,7 @@ void ProductView::CreateViews() { cancelButton_ = Add(new Button(di->T("Cancel"))); cancelButton_->OnClick.Handle(this, &ProductView::OnCancel); - cancelButton_->SetVisibility(V_GONE); + cancelButton_->SetVisibility(isDownloading ? V_VISIBLE : V_GONE); // Add star rating, comments etc? Add(new TextView(entry_.description, ALIGN_LEFT | FLAG_WRAP_TEXT, false)); @@ -277,31 +286,47 @@ void ProductView::Update() { if (installButton_) { installButton_->SetEnabled(g_GameManager.GetState() == GameManagerState::IDLE); } - if (cancelButton_ && g_GameManager.GetState() != GameManagerState::DOWNLOADING) - cancelButton_->SetVisibility(UI::V_GONE); + if (g_GameManager.GetState() == GameManagerState::DOWNLOADING) { + if (speedView_) { + float speed = g_GameManager.DownloadSpeedKBps(); + speedView_->SetText(StringFromFormat("%0.1f KB/s", speed)); + } + } else { + if (cancelButton_) + cancelButton_->SetVisibility(UI::V_GONE); + if (speedView_) + speedView_->SetVisibility(UI::V_GONE); + } if (launchButton_) launchButton_->SetEnabled(g_GameManager.GetState() == GameManagerState::IDLE); View::Update(); } -UI::EventReturn ProductView::OnInstall(UI::EventParams &e) { - std::string fileUrl; +std::string ProductView::DownloadURL() { if (entry_.downloadURL.empty()) { // Construct the URL, easy to predict from our server std::string shortName = entry_.file; if (shortName.find('.') == std::string::npos) shortName += ".zip"; - fileUrl = storeBaseUrl + "files/" + shortName; + return storeBaseUrl + "files/" + shortName; } else { // Use the provided URL, for external hosting. - fileUrl = entry_.downloadURL; + return entry_.downloadURL; } +} + +UI::EventReturn ProductView::OnInstall(UI::EventParams &e) { + std::string fileUrl = DownloadURL(); if (installButton_) { installButton_->SetEnabled(false); } if (cancelButton_) { cancelButton_->SetVisibility(UI::V_VISIBLE); } + if (speedView_) { + speedView_->SetVisibility(UI::V_VISIBLE); + speedView_->SetText(""); + } INFO_LOG(SYSTEM, "Triggering install of '%s'", fileUrl.c_str()); g_GameManager.DownloadAndInstall(fileUrl); return UI::EVENT_DONE; @@ -324,14 +349,11 @@ UI::EventReturn ProductView::OnLaunchClick(UI::EventParams &e) { return UI::EVENT_DONE; } - std::string pspGame = GetSysDirectory(DIRECTORY_GAME); - std::string path = pspGame + entry_.file; -#ifdef _WIN32 - path = ReplaceAll(path, "\\", "/"); -#endif + Path pspGame = GetSysDirectory(DIRECTORY_GAME); + Path path = pspGame / entry_.file; UI::EventParams e2{}; e2.v = e.v; - e2.s = path; + e2.s = path.ToString(); // Insta-update - here we know we are already on the right thread. OnClickLaunch.Trigger(e2); return UI::EVENT_DONE; @@ -345,7 +367,7 @@ StoreScreen::StoreScreen() { std::string indexPath = storeBaseUrl + "index.json"; - listing_ = g_DownloadManager.StartDownload(indexPath, ""); + listing_ = g_DownloadManager.StartDownload(indexPath, Path()); } StoreScreen::~StoreScreen() { @@ -523,7 +545,7 @@ UI::EventReturn StoreScreen::OnGameSelected(UI::EventParams &e) { UI::EventReturn StoreScreen::OnGameLaunch(UI::EventParams &e) { std::string path = e.s; - screenManager()->switchScreen(new EmuScreen(path)); + screenManager()->switchScreen(new EmuScreen(Path(path))); return UI::EVENT_DONE; } diff --git a/UI/TextureUtil.cpp b/UI/TextureUtil.cpp index 3d986e765a..9a5b377a90 100644 --- a/UI/TextureUtil.cpp +++ b/UI/TextureUtil.cpp @@ -236,11 +236,9 @@ void GameIconView::Draw(UIContext &dc) { // Fade icon with the backgrounds. double loadTime = info->icon.timeLoaded; - if (info->pic1.texture) { - loadTime = std::max(loadTime, info->pic1.timeLoaded); - } - if (info->pic0.texture) { - loadTime = std::max(loadTime, info->pic0.timeLoaded); + auto pic = info->GetBGPic(); + if (pic) { + loadTime = std::max(loadTime, pic->timeLoaded); } uint32_t color = whiteAlpha(ease((time_now_d() - loadTime) * 3)); diff --git a/UI/TextureUtil.h b/UI/TextureUtil.h index e2d0210ae9..19d9b40cb0 100644 --- a/UI/TextureUtil.h +++ b/UI/TextureUtil.h @@ -4,6 +4,7 @@ #include "Common/GPU/thin3d.h" #include "Common/UI/View.h" +#include "Common/File/Path.h" enum ImageFileType { PNG, @@ -44,7 +45,7 @@ std::unique_ptr CreateTextureFromFileData(Draw::DrawContext *dra class GameIconView : public UI::InertView { public: - GameIconView(std::string gamePath, float scale, UI::LayoutParams *layoutParams = 0) + GameIconView(const Path &gamePath, float scale, UI::LayoutParams *layoutParams = 0) : InertView(layoutParams), gamePath_(gamePath), scale_(scale) {} void GetContentDimensions(const UIContext &dc, float &w, float &h) const override; @@ -52,7 +53,7 @@ public: std::string DescribeText() const override { return ""; } private: - std::string gamePath_; + Path gamePath_; float scale_ = 1.0f; int textureWidth_ = 0; int textureHeight_ = 0; diff --git a/UWP/AssetsGold/LockScreenLogo.scale-200.png b/UWP/AssetsGold/LockScreenLogo.scale-200.png index f03796f780..489d61d30e 100644 Binary files a/UWP/AssetsGold/LockScreenLogo.scale-200.png and b/UWP/AssetsGold/LockScreenLogo.scale-200.png differ diff --git a/UWP/AssetsGold/SplashScreen.scale-200.png b/UWP/AssetsGold/SplashScreen.scale-200.png index 4f2ce38cd3..956ae07f51 100644 Binary files a/UWP/AssetsGold/SplashScreen.scale-200.png and b/UWP/AssetsGold/SplashScreen.scale-200.png differ diff --git a/UWP/AssetsGold/Square150x150Logo.scale-200.png b/UWP/AssetsGold/Square150x150Logo.scale-200.png index 4527504979..d9d267bf82 100644 Binary files a/UWP/AssetsGold/Square150x150Logo.scale-200.png and b/UWP/AssetsGold/Square150x150Logo.scale-200.png differ diff --git a/UWP/AssetsGold/Square44x44Logo.scale-200.png b/UWP/AssetsGold/Square44x44Logo.scale-200.png index a5306af93c..f1bf11364a 100644 Binary files a/UWP/AssetsGold/Square44x44Logo.scale-200.png and b/UWP/AssetsGold/Square44x44Logo.scale-200.png differ diff --git a/UWP/AssetsGold/Square44x44Logo.targetsize-24_altform-unplated.png b/UWP/AssetsGold/Square44x44Logo.targetsize-24_altform-unplated.png index c8683d2950..a8aa33a7fc 100644 Binary files a/UWP/AssetsGold/Square44x44Logo.targetsize-24_altform-unplated.png and b/UWP/AssetsGold/Square44x44Logo.targetsize-24_altform-unplated.png differ diff --git a/UWP/AssetsGold/StoreLogo.png b/UWP/AssetsGold/StoreLogo.png index e1d69c31b1..730cc243f3 100644 Binary files a/UWP/AssetsGold/StoreLogo.png and b/UWP/AssetsGold/StoreLogo.png differ diff --git a/UWP/AssetsGold/StoreLogo.scale-100.png b/UWP/AssetsGold/StoreLogo.scale-100.png index e1d69c31b1..730cc243f3 100644 Binary files a/UWP/AssetsGold/StoreLogo.scale-100.png and b/UWP/AssetsGold/StoreLogo.scale-100.png differ diff --git a/UWP/AssetsGold/Wide310x150Logo.scale-200.png b/UWP/AssetsGold/Wide310x150Logo.scale-200.png index 1134f39a6f..2dbdf8e2f8 100644 Binary files a/UWP/AssetsGold/Wide310x150Logo.scale-200.png and b/UWP/AssetsGold/Wide310x150Logo.scale-200.png differ diff --git a/UWP/AssetsNormal/LockScreenLogo.scale-200.png b/UWP/AssetsNormal/LockScreenLogo.scale-200.png index 0e78f4b01b..aa2a941984 100644 Binary files a/UWP/AssetsNormal/LockScreenLogo.scale-200.png and b/UWP/AssetsNormal/LockScreenLogo.scale-200.png differ diff --git a/UWP/AssetsNormal/SplashScreen.scale-200.png b/UWP/AssetsNormal/SplashScreen.scale-200.png index fb75451a3d..bf834e42ad 100644 Binary files a/UWP/AssetsNormal/SplashScreen.scale-200.png and b/UWP/AssetsNormal/SplashScreen.scale-200.png differ diff --git a/UWP/AssetsNormal/Square150x150Logo.scale-200.png b/UWP/AssetsNormal/Square150x150Logo.scale-200.png index 92cdf494e4..7d779162d2 100644 Binary files a/UWP/AssetsNormal/Square150x150Logo.scale-200.png and b/UWP/AssetsNormal/Square150x150Logo.scale-200.png differ diff --git a/UWP/AssetsNormal/Square44x44Logo.scale-200.png b/UWP/AssetsNormal/Square44x44Logo.scale-200.png index 739503aa2e..6ac38ea170 100644 Binary files a/UWP/AssetsNormal/Square44x44Logo.scale-200.png and b/UWP/AssetsNormal/Square44x44Logo.scale-200.png differ diff --git a/UWP/AssetsNormal/Square44x44Logo.targetsize-24_altform-unplated.png b/UWP/AssetsNormal/Square44x44Logo.targetsize-24_altform-unplated.png index d88950ecd1..49b7608961 100644 Binary files a/UWP/AssetsNormal/Square44x44Logo.targetsize-24_altform-unplated.png and b/UWP/AssetsNormal/Square44x44Logo.targetsize-24_altform-unplated.png differ diff --git a/UWP/AssetsNormal/StoreLogo.png b/UWP/AssetsNormal/StoreLogo.png index a971eb8ca8..eabe0d7f31 100644 Binary files a/UWP/AssetsNormal/StoreLogo.png and b/UWP/AssetsNormal/StoreLogo.png differ diff --git a/UWP/AssetsNormal/Wide310x150Logo.scale-200.png b/UWP/AssetsNormal/Wide310x150Logo.scale-200.png index 297a69de14..6d32f089ed 100644 Binary files a/UWP/AssetsNormal/Wide310x150Logo.scale-200.png and b/UWP/AssetsNormal/Wide310x150Logo.scale-200.png differ diff --git a/UWP/CommonUWP/CommonUWP.vcxproj b/UWP/CommonUWP/CommonUWP.vcxproj index eec0403a85..c98fee5169 100644 --- a/UWP/CommonUWP/CommonUWP.vcxproj +++ b/UWP/CommonUWP/CommonUWP.vcxproj @@ -203,7 +203,7 @@ false false pch.h - ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) NotUsing @@ -217,7 +217,7 @@ false false - ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) pch.h NotUsing @@ -232,7 +232,7 @@ false false - ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) pch.h NotUsing @@ -248,7 +248,7 @@ false false pch.h - ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) NotUsing @@ -263,7 +263,7 @@ false false pch.h - ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) NotUsing @@ -277,7 +277,7 @@ false false - ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) pch.h NotUsing @@ -292,7 +292,7 @@ false false - ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) pch.h NotUsing @@ -307,7 +307,7 @@ false false - ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) pch.h NotUsing @@ -322,7 +322,7 @@ false false - ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) pch.h NotUsing @@ -338,7 +338,7 @@ false false pch.h - ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) NotUsing @@ -352,7 +352,7 @@ false false - ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) pch.h NotUsing @@ -367,7 +367,7 @@ false false - ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/native;../../ext/snappy;../../ext/glslang;../../ext/zlib;../../ext/libpng17;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) pch.h NotUsing @@ -386,6 +386,7 @@ + @@ -414,6 +415,7 @@ + @@ -457,8 +459,8 @@ - - + + @@ -483,7 +485,6 @@ - @@ -514,6 +515,7 @@ + @@ -534,6 +536,7 @@ + @@ -569,10 +572,11 @@ - - + + + @@ -590,7 +594,6 @@ - @@ -633,4 +636,4 @@ - \ No newline at end of file + diff --git a/UWP/CommonUWP/CommonUWP.vcxproj.filters b/UWP/CommonUWP/CommonUWP.vcxproj.filters index c0fb4df3ec..fc0d0a62cb 100644 --- a/UWP/CommonUWP/CommonUWP.vcxproj.filters +++ b/UWP/CommonUWP/CommonUWP.vcxproj.filters @@ -92,10 +92,11 @@ - - + + + @@ -123,6 +124,7 @@ Crypto + ext\libpng17 @@ -177,9 +179,6 @@ Input - - Thread - Thread @@ -243,6 +242,9 @@ File + + File + File @@ -383,8 +385,8 @@ - - + + @@ -417,6 +419,7 @@ Crypto + @@ -452,9 +455,6 @@ Input - - Thread - Thread @@ -536,6 +536,9 @@ File + + File + Data\Format @@ -694,4 +697,4 @@ Math\lin - \ No newline at end of file + diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index c72b42d206..bd5f53edeb 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -204,7 +204,7 @@ false false pch.h - ../../ffmpeg/Windows10/x86/include;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/x86/include;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -219,7 +219,7 @@ false false pch.h - ../../ffmpeg/Windows10/x86/include;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/x86/include;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -234,7 +234,7 @@ false false pch.h - ../../ffmpeg/Windows10/x86/include;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/x86/include;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -249,7 +249,7 @@ false false pch.h - ../../ffmpeg/Windows10/ARM/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/ARM/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -264,7 +264,7 @@ false false pch.h - ../../ffmpeg/Windows10/ARM64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/ARM64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -279,7 +279,7 @@ false false pch.h - ../../ffmpeg/Windows10/ARM/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/ARM/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -294,7 +294,7 @@ false false pch.h - ../../ffmpeg/Windows10/ARM64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/ARM64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -309,7 +309,7 @@ false false pch.h - ../../ffmpeg/Windows10/ARM/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/ARM/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -324,7 +324,7 @@ false false pch.h - ../../ffmpeg/Windows10/ARM64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/ARM64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -339,7 +339,7 @@ false false pch.h - ../../ffmpeg/Windows10/x64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/x64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -354,7 +354,7 @@ false false pch.h - ../../ffmpeg/Windows10/x64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/x64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -369,7 +369,7 @@ false false pch.h - ../../ffmpeg/Windows10/x64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../../ffmpeg/Windows10/x64/include;../../ffmpeg/WindowsInclude;../..;../../ext/native;../../ext/snappy;../../ext/libpng17;../../Common;../../ext/zlib;../../ext/zstd/lib;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) USE_FFMPEG;WITH_UPNP;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -404,6 +404,7 @@ + @@ -587,9 +588,6 @@ - - - @@ -637,6 +635,7 @@ + @@ -842,44 +841,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - NotUsing - NotUsing - NotUsing - NotUsing - NotUsing - NotUsing - NotUsing - NotUsing - NotUsing - NotUsing - NotUsing - NotUsing - NotUsing NotUsing @@ -956,6 +917,120 @@ + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing NotUsing @@ -1070,7 +1145,7 @@ - + NotUsing NotUsing NotUsing @@ -1108,7 +1183,45 @@ - + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing NotUsing NotUsing @@ -1412,6 +1525,82 @@ + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing NotUsing @@ -1450,7 +1639,45 @@ - + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing NotUsing NotUsing @@ -1564,6 +1791,82 @@ + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing NotUsing @@ -1602,6 +1905,234 @@ + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing NotUsing @@ -1678,6 +2209,44 @@ + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing NotUsing @@ -1716,6 +2285,44 @@ + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing NotUsing @@ -1754,7 +2361,45 @@ - + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing NotUsing NotUsing @@ -1868,6 +2513,44 @@ + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing NotUsing @@ -1944,6 +2627,44 @@ + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing NotUsing @@ -1982,6 +2703,120 @@ + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing NotUsing @@ -2134,6 +2969,120 @@ + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing NotUsing @@ -2286,6 +3235,44 @@ + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing NotUsing @@ -2324,6 +3311,44 @@ + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing NotUsing @@ -2362,6 +3387,120 @@ + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing NotUsing @@ -2400,7 +3539,7 @@ - + NotUsing NotUsing NotUsing @@ -2438,7 +3577,387 @@ - + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing NotUsing NotUsing @@ -2552,6 +4071,652 @@ + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing NotUsing @@ -2590,6 +4755,44 @@ + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing NotUsing @@ -2742,6 +4945,44 @@ + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing NotUsing @@ -2894,6 +5135,44 @@ + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing NotUsing diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index f324ec0208..7d70fe9b42 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -697,6 +697,9 @@ Debugger\WebSocket + + Debugger\WebSocket + Debugger\WebSocket @@ -757,9 +760,6 @@ Ext\jpge - - Ext\libzip - Ext\libzip @@ -775,12 +775,6 @@ Ext\libzip - - Ext\libzip - - - Ext\libzip - Ext\libzip @@ -817,9 +811,6 @@ Ext\libzip - - Ext\libzip - Ext\libzip @@ -829,9 +820,6 @@ Ext\libzip - - Ext\libzip - Ext\libzip @@ -880,12 +868,6 @@ Ext\libzip - - Ext\libzip - - - Ext\libzip - Ext\libzip @@ -919,6 +901,210 @@ Ext\libzip + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + + + Ext\libzip + @@ -1512,6 +1698,9 @@ Debugger\WebSocket + + Debugger\WebSocket + Debugger\WebSocket @@ -1551,15 +1740,6 @@ Ext\jpge - - Ext\libzip - - - Ext\libzip - - - Ext\libzip - diff --git a/UWP/GPU_UWP/GPU_UWP.vcxproj b/UWP/GPU_UWP/GPU_UWP.vcxproj index 61dfb4406d..60246c4892 100644 --- a/UWP/GPU_UWP/GPU_UWP.vcxproj +++ b/UWP/GPU_UWP/GPU_UWP.vcxproj @@ -204,7 +204,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -219,7 +219,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -234,7 +234,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -249,7 +249,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -264,7 +264,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -279,7 +279,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -294,7 +294,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -309,7 +309,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -324,7 +324,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -339,7 +339,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -354,7 +354,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -369,7 +369,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -460,7 +460,6 @@ - diff --git a/UWP/GPU_UWP/GPU_UWP.vcxproj.filters b/UWP/GPU_UWP/GPU_UWP.vcxproj.filters index 0c5f3538bb..3f1d5431cf 100644 --- a/UWP/GPU_UWP/GPU_UWP.vcxproj.filters +++ b/UWP/GPU_UWP/GPU_UWP.vcxproj.filters @@ -23,7 +23,6 @@ - diff --git a/UWP/PPSSPP_UWP.sln b/UWP/PPSSPP_UWP.sln index fc6a017b01..42c7eddb2e 100644 --- a/UWP/PPSSPP_UWP.sln +++ b/UWP/PPSSPP_UWP.sln @@ -28,6 +28,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SPIRVCross_UWP", "SPIRVCros EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Armips_UWP", "Armips_UWP\Armips_UWP.vcxproj", "{75286959-E7A2-4CBE-8B95-BF05C9C540FF}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libzstd_UWP", "libzstd_UWP\libzstd_UWP.vcxproj", "{75286959-E7A2-4CBE-8B95-BF05C9C540FE}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniupnpc_UWP", "miniupnpc_UWP\miniupnpc_UWP.vcxproj", "{D31FD4F0-53EB-477C-9DC7-149796F628E2}" EndProject Global @@ -322,6 +324,30 @@ Global {75286959-E7A2-4CBE-8B95-BF05C9C540FF}.UWP Gold|Win32.Build.0 = Release|Win32 {75286959-E7A2-4CBE-8B95-BF05C9C540FF}.UWP Gold|x64.ActiveCfg = Release|x64 {75286959-E7A2-4CBE-8B95-BF05C9C540FF}.UWP Gold|x64.Build.0 = Release|x64 + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.Debug|ARM.ActiveCfg = Debug|ARM + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.Debug|ARM.Build.0 = Debug|ARM + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.Debug|ARM64.Build.0 = Debug|ARM64 + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.Debug|Win32.ActiveCfg = Debug|Win32 + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.Debug|Win32.Build.0 = Debug|Win32 + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.Debug|x64.ActiveCfg = Debug|x64 + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.Debug|x64.Build.0 = Debug|x64 + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.Release|ARM.ActiveCfg = Release|ARM + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.Release|ARM.Build.0 = Release|ARM + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.Release|ARM64.ActiveCfg = Release|ARM64 + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.Release|ARM64.Build.0 = Release|ARM64 + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.Release|Win32.ActiveCfg = Release|Win32 + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.Release|Win32.Build.0 = Release|Win32 + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.Release|x64.ActiveCfg = Release|x64 + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.Release|x64.Build.0 = Release|x64 + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.UWP Gold|ARM.ActiveCfg = Release|ARM + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.UWP Gold|ARM.Build.0 = Release|ARM + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.UWP Gold|ARM64.ActiveCfg = Release|ARM64 + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.UWP Gold|ARM64.Build.0 = Release|ARM64 + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.UWP Gold|Win32.ActiveCfg = Release|Win32 + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.UWP Gold|Win32.Build.0 = Release|Win32 + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.UWP Gold|x64.ActiveCfg = Release|x64 + {75286959-E7A2-4CBE-8B95-BF05C9C540FE}.UWP Gold|x64.Build.0 = Release|x64 {D31FD4F0-53EB-477C-9DC7-149796F628E2}.Debug|ARM.ActiveCfg = Debug|ARM {D31FD4F0-53EB-477C-9DC7-149796F628E2}.Debug|ARM.Build.0 = Debug|ARM {D31FD4F0-53EB-477C-9DC7-149796F628E2}.Debug|ARM64.ActiveCfg = Debug|ARM64 diff --git a/UWP/PPSSPP_UWPMain.cpp b/UWP/PPSSPP_UWPMain.cpp index 18970f130e..949eaf0df1 100644 --- a/UWP/PPSSPP_UWPMain.cpp +++ b/UWP/PPSSPP_UWPMain.cpp @@ -76,9 +76,9 @@ PPSSPP_UWPMain::PPSSPP_UWPMain(App ^app, const std::shared_ptrLocalFolder->Path->Data(); - - g_Config.memStickDirectory = ReplaceAll(ConvertWStringToUTF8(memstickFolderW), "\\", "/"); - if (g_Config.memStickDirectory.back() != '/') - g_Config.memStickDirectory += "/"; + g_Config.memStickDirectory = Path(memstickFolderW); // On Win32 it makes more sense to initialize the system directories here // because the next place it was called was in the EmuThread, and it's too late by then. @@ -168,7 +165,7 @@ bool PPSSPP_UWPMain::Render() { static bool hasSetThreadName = false; if (!hasSetThreadName) { - setCurrentThreadName("UWPRenderThread"); + SetCurrentThreadName("UWPRenderThread"); hasSetThreadName = true; } @@ -314,7 +311,7 @@ void PPSSPP_UWPMain::OnSuspend() { void PPSSPP_UWPMain::LoadStorageFile(StorageFile ^file) { std::unique_ptr factory(new StorageFileLoaderFactory(file, IdentifiedFileType::PSP_ISO)); RegisterFileLoaderFactory("override://", std::move(factory)); - NativeMessageReceived("boot", "override://"); + NativeMessageReceived("boot", "override://file"); } UWPGraphicsContext::UWPGraphicsContext(std::shared_ptr resources) { diff --git a/UWP/StorageFileLoader.cpp b/UWP/StorageFileLoader.cpp index 0d566d4722..30e0b13eb7 100644 --- a/UWP/StorageFileLoader.cpp +++ b/UWP/StorageFileLoader.cpp @@ -3,6 +3,7 @@ #include "Common/Log.h" #include "Common/File/FileUtil.h" +#include "Common/File/Path.h" #include "Common/File/DirListing.h" #include "Common/Thread/ThreadUtil.h" #include "StorageFileLoader.h" @@ -19,7 +20,7 @@ static std::mutex initMutex; StorageFileLoader::StorageFileLoader(Windows::Storage::StorageFile ^file) { active_ = false; file_ = file; - path_ = FromPlatformString(file_->Path); + path_ = Path(FromPlatformString(file_->Path)); thread_.reset(new std::thread([this]() { this->threadfunc(); })); // Before we proceed, we need to block until the thread has found the size. @@ -40,7 +41,7 @@ StorageFileLoader::~StorageFileLoader() { } void StorageFileLoader::threadfunc() { - setCurrentThreadName("StorageFileLoader"); + SetCurrentThreadName("StorageFileLoader"); { @@ -131,14 +132,10 @@ s64 StorageFileLoader::FileSize() { return size_; } -std::string StorageFileLoader::Path() const { +Path StorageFileLoader::GetPath() const { return path_; } -std::string StorageFileLoader::Extension() { - return "." + getFileExtension(path_); -} - void StorageFileLoader::EnsureOpen() { while (size_ == -1) Sleep(50); @@ -179,6 +176,6 @@ size_t StorageFileLoader::ReadAt(s64 absolutePos, size_t bytes, size_t count, vo } } -FileLoader *StorageFileLoaderFactory::ConstructFileLoader(const std::string &filename) { +FileLoader *StorageFileLoaderFactory::ConstructFileLoader(const Path &filename) { return file_ ? new StorageFileLoader(file_) : nullptr; } diff --git a/UWP/StorageFileLoader.h b/UWP/StorageFileLoader.h index 0a85a533bd..b62a153485 100644 --- a/UWP/StorageFileLoader.h +++ b/UWP/StorageFileLoader.h @@ -9,6 +9,7 @@ #include #include "Common/CommonTypes.h" +#include "Common/File/Path.h" #include "Core/Loaders.h" // This thing is a terrible abomination that wraps asynchronous file access behind a synchronous interface, @@ -24,8 +25,7 @@ public: bool IsDirectory() override; s64 FileSize() override; - std::string Path() const override; - std::string Extension() override; + Path GetPath() const override; size_t ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags = Flags::NONE) override; @@ -54,7 +54,7 @@ private: Windows::Storage::StorageFile ^file_; Windows::Storage::Streams::IRandomAccessStreamWithContentType ^stream_; - std::string path_; + Path path_; bool operationRequested_ = false; Operation operation_{ OpType::NONE, 0, 0 }; @@ -74,7 +74,7 @@ private: class StorageFileLoaderFactory : public FileLoaderFactory { public: StorageFileLoaderFactory(Windows::Storage::StorageFile ^file, IdentifiedFileType fileType) : file_(file), fileType_(fileType) { } - FileLoader *ConstructFileLoader(const std::string &filename) override; + FileLoader *ConstructFileLoader(const Path &filename) override; private: Windows::Storage::StorageFile ^file_; diff --git a/UWP/StorageFolderBrowser.cpp b/UWP/StorageFolderBrowser.cpp index 4e86ed079f..00f5275dbb 100644 --- a/UWP/StorageFolderBrowser.cpp +++ b/UWP/StorageFolderBrowser.cpp @@ -20,7 +20,7 @@ StorageFolderBrowser::StorageFolderBrowser(Windows::Storage::StorageFolder ^fold } void StorageFolderBrowser::threadfunc() { - setCurrentThreadName("StorageFileLoader"); + SetCurrentThreadName("StorageFileLoader"); initMutex.lock(); diff --git a/UWP/StorageFolderBrowser.h b/UWP/StorageFolderBrowser.h index d5da640fa4..b349b4636e 100644 --- a/UWP/StorageFolderBrowser.h +++ b/UWP/StorageFolderBrowser.h @@ -21,7 +21,7 @@ public: StorageFolderBrowser(Windows::Storage::StorageFolder ^folder); ~StorageFolderBrowser(); - std::string Path() const { + std::string GetPath() const { return path_; } diff --git a/UWP/UI_UWP/UI_UWP.vcxproj b/UWP/UI_UWP/UI_UWP.vcxproj index 140288da17..23f6ed0ea3 100644 --- a/UWP/UI_UWP/UI_UWP.vcxproj +++ b/UWP/UI_UWP/UI_UWP.vcxproj @@ -203,7 +203,7 @@ Use false false - NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_UNICODE;UNICODE;%(PreprocessorDefinitions) + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h @@ -218,7 +218,7 @@ Use false false - NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_UNICODE;UNICODE;%(PreprocessorDefinitions) + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h @@ -233,7 +233,7 @@ Use false false - GOLD;NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_UNICODE;UNICODE;%(PreprocessorDefinitions) + GOLD;NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h @@ -248,7 +248,7 @@ Use false false - NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h @@ -263,7 +263,7 @@ Use false false - NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h @@ -278,7 +278,7 @@ Use false false - NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h @@ -293,7 +293,7 @@ Use false false - NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h @@ -308,7 +308,7 @@ Use false false - GOLD;NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + GOLD;NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h @@ -323,7 +323,7 @@ Use false false - GOLD;NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + GOLD;NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h @@ -338,7 +338,7 @@ Use false false - NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_UNICODE;UNICODE;%(PreprocessorDefinitions) + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h @@ -353,7 +353,7 @@ Use false false - NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_UNICODE;UNICODE;%(PreprocessorDefinitions) + NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h @@ -368,7 +368,7 @@ Use false false - GOLD;NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_UNICODE;UNICODE;%(PreprocessorDefinitions) + GOLD;NOMINMAX;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../..;../../ext/native;../../Common;../../ext;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) pch.h diff --git a/UWP/UWP.vcxproj b/UWP/UWP.vcxproj index c355418c48..65c07e1f90 100644 --- a/UWP/UWP.vcxproj +++ b/UWP/UWP.vcxproj @@ -1728,6 +1728,9 @@ {2f911c05-b341-4291-8bf5-09edecbdd5f5} + + {75286959-e7a2-4cbe-8b95-bf05c9c540fe} + {2b2d16bd-1d37-46af-a3f8-552900951b26} diff --git a/UWP/UWPHost.cpp b/UWP/UWPHost.cpp index d16b730192..5e489e161c 100644 --- a/UWP/UWPHost.cpp +++ b/UWP/UWPHost.cpp @@ -121,44 +121,27 @@ void UWPHost::BootDone() { Core_EnableStepping(false); } -static std::string SymbolMapFilename(const char *currentFilename, char* ext) { - FileInfo info; - - std::string result = currentFilename; - +static Path SymbolMapFilename(const Path ¤tFilename, const char *ext) { + File::FileInfo info; // can't fail, definitely exists if it gets this far - getFileInfo(currentFilename, &info); + File::GetFileInfo(currentFilename, &info); if (info.isDirectory) { -#ifdef _WIN32 - char* slash = "\\"; -#else - char* slash = "/"; -#endif - if (!endsWith(result, slash)) - result += slash; - - return result + ".ppsspp-symbols" + ext; - } else { - size_t dot = result.rfind('.'); - if (dot == result.npos) - return result + ext; - - result.replace(dot, result.npos, ext); - return result; + return currentFilename / (std::string(".ppsspp-symbols") + ext); } + return currentFilename.WithReplacedExtension(ext); } bool UWPHost::AttemptLoadSymbolMap() { - bool result1 = g_symbolMap->LoadSymbolMap(SymbolMapFilename(PSP_CoreParameter().fileToStart.c_str(), ".ppmap").c_str()); + bool result1 = g_symbolMap->LoadSymbolMap(SymbolMapFilename(PSP_CoreParameter().fileToStart, ".ppmap")); // Load the old-style map file. if (!result1) - result1 = g_symbolMap->LoadSymbolMap(SymbolMapFilename(PSP_CoreParameter().fileToStart.c_str(), ".map").c_str()); - bool result2 = g_symbolMap->LoadNocashSym(SymbolMapFilename(PSP_CoreParameter().fileToStart.c_str(), ".sym").c_str()); + result1 = g_symbolMap->LoadSymbolMap(SymbolMapFilename(PSP_CoreParameter().fileToStart, ".map")); + bool result2 = g_symbolMap->LoadNocashSym(SymbolMapFilename(PSP_CoreParameter().fileToStart, ".sym")); return result1 || result2; } void UWPHost::SaveSymbolMap() { - g_symbolMap->SaveSymbolMap(SymbolMapFilename(PSP_CoreParameter().fileToStart.c_str(), ".ppmap").c_str()); + g_symbolMap->SaveSymbolMap(SymbolMapFilename(PSP_CoreParameter().fileToStart, ".ppmap")); } void UWPHost::NotifySymbolMapUpdated() { diff --git a/UWP/XAudioSoundStream.cpp b/UWP/XAudioSoundStream.cpp index ec343dd664..aebc144fb1 100644 --- a/UWP/XAudioSoundStream.cpp +++ b/UWP/XAudioSoundStream.cpp @@ -103,7 +103,7 @@ bool XAudioBackend::RunSound() { thread_ = (HANDLE)_beginthreadex(0, 0, [](void* param) { - setCurrentThreadName("XAudio2"); + SetCurrentThreadName("XAudio2"); XAudioBackend *backend = (XAudioBackend *)param; backend->PollLoop(); return 0U; diff --git a/UWP/glslang_UWP/glslang_UWP.vcxproj b/UWP/glslang_UWP/glslang_UWP.vcxproj index a25f2b6a41..69bb015a2e 100644 --- a/UWP/glslang_UWP/glslang_UWP.vcxproj +++ b/UWP/glslang_UWP/glslang_UWP.vcxproj @@ -383,7 +383,6 @@ - @@ -508,4 +507,4 @@ - \ No newline at end of file + diff --git a/UWP/libzstd_UWP/libzstd_UWP.vcxproj b/UWP/libzstd_UWP/libzstd_UWP.vcxproj new file mode 100644 index 0000000000..78101ed883 --- /dev/null +++ b/UWP/libzstd_UWP/libzstd_UWP.vcxproj @@ -0,0 +1,317 @@ + + + + + Debug + ARM + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {75286959-e7a2-4cbe-8b95-bf05c9c540fe} + StaticLibrary + libzstd_UWP + en-US + 14.0 + true + Windows Store + 10.0.17763.0 + 10.0.15063.0 + 10.0 + + + + StaticLibrary + true + + + StaticLibrary + true + + + StaticLibrary + true + + + StaticLibrary + true + + + StaticLibrary + false + true + + + StaticLibrary + false + true + + + StaticLibrary + false + true + + + StaticLibrary + false + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips; + + + false + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips; + + + false + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips; + + + false + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips; + + + false + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips; + + + false + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips; + + + false + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips; + + + false + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)..\..\ext\armips; + + + + NotUsing + false + false + + + Console + false + false + + + + + NotUsing + false + false + + + Console + false + false + + + + + NotUsing + false + false + + + Console + false + false + + + + + NotUsing + false + false + + + Console + false + false + + + + + NotUsing + false + false + + + Console + false + false + + + + + NotUsing + false + false + + + Console + false + false + + + + + NotUsing + false + false + + + Console + false + false + + + + + NotUsing + false + false + + + Console + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + + + + + + \ No newline at end of file diff --git a/UWP/libzstd_UWP/libzstd_UWP.vcxproj.filters b/UWP/libzstd_UWP/libzstd_UWP.vcxproj.filters new file mode 100644 index 0000000000..1a22172909 --- /dev/null +++ b/UWP/libzstd_UWP/libzstd_UWP.vcxproj.filters @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/UWP/libzstd_UWP/pch.cpp b/UWP/libzstd_UWP/pch.cpp new file mode 100644 index 0000000000..bcb5590be1 --- /dev/null +++ b/UWP/libzstd_UWP/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" diff --git a/UWP/libzstd_UWP/pch.h b/UWP/libzstd_UWP/pch.h new file mode 100644 index 0000000000..529bbb17fe --- /dev/null +++ b/UWP/libzstd_UWP/pch.h @@ -0,0 +1,9 @@ +#pragma once + +#include "targetver.h" + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include diff --git a/UWP/libzstd_UWP/targetver.h b/UWP/libzstd_UWP/targetver.h new file mode 100644 index 0000000000..a66ecb00f1 --- /dev/null +++ b/UWP/libzstd_UWP/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/UWP/miniupnpc_UWP/miniupnpc_UWP.vcxproj b/UWP/miniupnpc_UWP/miniupnpc_UWP.vcxproj index 2ea37b7570..5e42ecf2a4 100644 --- a/UWP/miniupnpc_UWP/miniupnpc_UWP.vcxproj +++ b/UWP/miniupnpc_UWP/miniupnpc_UWP.vcxproj @@ -439,6 +439,7 @@ cd ..\..\..\..\UWP\miniupnpc_UWP + @@ -452,6 +453,7 @@ cd ..\..\..\..\UWP\miniupnpc_UWP + diff --git a/UWP/miniupnpc_UWP/miniupnpc_UWP.vcxproj.filters b/UWP/miniupnpc_UWP/miniupnpc_UWP.vcxproj.filters index b6e77744fd..929654b098 100644 --- a/UWP/miniupnpc_UWP/miniupnpc_UWP.vcxproj.filters +++ b/UWP/miniupnpc_UWP/miniupnpc_UWP.vcxproj.filters @@ -44,6 +44,9 @@ src + + src + @@ -85,6 +88,9 @@ src + + src + diff --git a/Windows/CaptureDevice.cpp b/Windows/CaptureDevice.cpp index f28d99b7b1..e522c7b24e 100644 --- a/Windows/CaptureDevice.cpp +++ b/Windows/CaptureDevice.cpp @@ -874,9 +874,9 @@ void WindowsCaptureDevice::messageHandler() { CAPTUREDEVIDE_MESSAGE message; if (type == CAPTUREDEVIDE_TYPE::VIDEO) { - setCurrentThreadName("Camera"); + SetCurrentThreadName("Camera"); } else if (type == CAPTUREDEVIDE_TYPE::AUDIO) { - setCurrentThreadName("Microphone"); + SetCurrentThreadName("Microphone"); } while ((message = getMessage()).command != CAPTUREDEVIDE_COMMAND::SHUTDOWN) { diff --git a/Windows/DSoundStream.cpp b/Windows/DSoundStream.cpp index bd8e343ad0..ba03f042fb 100644 --- a/Windows/DSoundStream.cpp +++ b/Windows/DSoundStream.cpp @@ -110,7 +110,7 @@ int DSoundAudioBackend::RunThread() { dsBuffer_->Unlock(p1, num1, 0, 0); totalRenderedBytes_ = -bufferSize_; - setCurrentThreadName("DSound"); + SetCurrentThreadName("DSound"); currentPos_ = 0; lastPos_ = 0; diff --git a/Windows/Debugger/CtrlDisAsmView.cpp b/Windows/Debugger/CtrlDisAsmView.cpp index ebbbf85354..4c3b13228f 100644 --- a/Windows/Debugger/CtrlDisAsmView.cpp +++ b/Windows/Debugger/CtrlDisAsmView.cpp @@ -1094,6 +1094,10 @@ void CtrlDisAsmView::updateStatusBarText() text[0] = 0; if (line.type == DISTYPE_OPCODE || line.type == DISTYPE_MACRO) { + if (line.info.hasRelevantAddress && IsLikelyStringAt(line.info.relevantAddress)) { + snprintf(text, sizeof(text), "[%08X] = \"%s\"", line.info.relevantAddress, Memory::GetCharPointer(line.info.relevantAddress)); + } + if (line.info.isDataAccess) { if (!Memory::IsValidAddress(line.info.dataAddress)) @@ -1188,7 +1192,7 @@ void CtrlDisAsmView::search(bool continueSearch) if (continueSearch == false || searchQuery[0] == 0) { - if (InputBox_GetString(MainWindow::GetHInstance(),MainWindow::GetHWND(),L"Search for:","",searchQuery) == false + if (InputBox_GetString(MainWindow::GetHInstance(), MainWindow::GetHWND(), L"Search for:", searchQuery, searchQuery) == false || searchQuery[0] == 0) { SetFocus(wnd); diff --git a/Windows/Debugger/CtrlMemView.cpp b/Windows/Debugger/CtrlMemView.cpp index 5b0e4400b8..1d57442c8f 100644 --- a/Windows/Debugger/CtrlMemView.cpp +++ b/Windows/Debugger/CtrlMemView.cpp @@ -533,6 +533,28 @@ void CtrlMemView::onMouseUp(WPARAM wParam, LPARAM lParam, int button) } break; + case ID_MEMVIEW_EXTENTBEGIN: + { + std::vector memRangeInfo = FindMemInfoByFlag(highlightFlags_, curAddress, 1); + uint32_t addr = curAddress; + for (MemBlockInfo info : memRangeInfo) { + addr = info.start; + } + gotoAddr(addr); + break; + } + + case ID_MEMVIEW_EXTENTEND: + { + std::vector memRangeInfo = FindMemInfoByFlag(highlightFlags_, curAddress, 1); + uint32_t addr = curAddress; + for (MemBlockInfo info : memRangeInfo) { + addr = info.start + info.size - 1; + } + gotoAddr(addr); + break; + } + case ID_MEMVIEW_COPYADDRESS: { char temp[24]; @@ -723,7 +745,7 @@ void CtrlMemView::search(bool continueSearch) u8* dataPointer = 0; if (continueSearch == false || searchQuery.empty()) { - if (InputBox_GetString(GetModuleHandle(NULL),wnd,L"Search for", "",searchQuery) == false) + if (InputBox_GetString(GetModuleHandle(NULL), wnd, L"Search for", searchQuery, searchQuery) == false) { SetFocus(wnd); return; @@ -845,6 +867,7 @@ void CtrlMemView::toggleOffsetScale(CommonToggles toggle) void CtrlMemView::setHighlightType(MemBlockFlags flags) { if (highlightFlags_ != flags) { highlightFlags_ = flags; + updateStatusBarText(); redraw(); } } diff --git a/Windows/Debugger/Debugger_Disasm.cpp b/Windows/Debugger/Debugger_Disasm.cpp index 3f0bf77907..54af241898 100644 --- a/Windows/Debugger/Debugger_Disasm.cpp +++ b/Windows/Debugger/Debugger_Disasm.cpp @@ -848,14 +848,14 @@ void CDisasm::SetDebugMode(bool _bDebug, bool switchPC) } } -void CDisasm::Show(bool bShow) { +void CDisasm::Show(bool bShow, bool includeToTop) { if (deferredSymbolFill_ && bShow) { if (g_symbolMap) { g_symbolMap->FillSymbolListBox(GetDlgItem(m_hDlg, IDC_FUNCTIONLIST), ST_FUNCTION); deferredSymbolFill_ = false; } } - Dialog::Show(bShow); + Dialog::Show(bShow, includeToTop); } void CDisasm::NotifyMapLoaded() { diff --git a/Windows/Debugger/Debugger_Disasm.h b/Windows/Debugger/Debugger_Disasm.h index c95ba39bd7..e77a9bb2ca 100644 --- a/Windows/Debugger/Debugger_Disasm.h +++ b/Windows/Debugger/Debugger_Disasm.h @@ -49,7 +49,7 @@ public: CDisasm(HINSTANCE _hInstance, HWND _hParent, DebugInterface *cpu); ~CDisasm(); - void Show(bool bShow) override; + void Show(bool bShow, bool includeToTop = true) override; void Update() override { UpdateDialog(true); diff --git a/Windows/Debugger/Debugger_MemoryDlg.cpp b/Windows/Debugger/Debugger_MemoryDlg.cpp index 6532c6a6f1..b11c53425f 100644 --- a/Windows/Debugger/Debugger_MemoryDlg.cpp +++ b/Windows/Debugger/Debugger_MemoryDlg.cpp @@ -131,9 +131,8 @@ void CMemoryDlg::searchBoxRedraw(std::vector results) { } -void CMemoryDlg::NotifyMapLoaded() -{ - if (m_hDlg) +void CMemoryDlg::NotifyMapLoaded() { + if (m_hDlg && g_symbolMap) g_symbolMap->FillSymbolListBox(symListHdl, ST_DATA); Update(); } diff --git a/Windows/DinputDevice.cpp b/Windows/DinputDevice.cpp index b87bc95537..bc5ab3e6a7 100644 --- a/Windows/DinputDevice.cpp +++ b/Windows/DinputDevice.cpp @@ -26,15 +26,11 @@ #include "Common/System/NativeApp.h" #include "Core/Config.h" #include "Core/HLE/sceCtrl.h" +#include "Core/KeyMap.h" #include "Core/Reporting.h" #include "Windows/DinputDevice.h" #pragma comment(lib,"dinput8.lib") -#ifdef min -#undef min -#undef max -#endif - //initialize static members of DinputDevice unsigned int DinputDevice::pInstances = 0; LPDIRECTINPUT8 DinputDevice::pDI = NULL; @@ -204,9 +200,6 @@ DinputDevice::~DinputDevice() { } void SendNativeAxis(int deviceId, int value, int &lastValue, int axisId) { - if (value == lastValue) - return; - AxisInput axis; axis.deviceId = deviceId; axis.axisId = axisId; @@ -227,6 +220,69 @@ inline float LinearMaps(float val, float a0, float a1, float b0, float b1) { return b0 + (((val - a0) * (b1 - b0)) / (a1 - a0)); } +static void ApplyNormalization(LONG &jsx, LONG &jsy) { + // Circle to Square mapping, cribbed from XInputDevice + float sx = (float)jsx; + float sy = (float)jsy; + float scaleFactor = sqrtf((sx * sx + sy * sy) / std::max(sx * sx, sy * sy)); + jsx = (int)(sx * scaleFactor); + jsy = (int)(sy * scaleFactor); + + // Linear range mapping (used to invert deadzones) + float dz = g_Config.fDInputAnalogDeadzone; + int idzm = g_Config.iDInputAnalogInverseMode; + float idz = g_Config.fDInputAnalogInverseDeadzone; + float md = std::max(dz, idz); + float st = g_Config.fDInputAnalogSensitivity; + + float magnitude = sqrtf((float)(jsx * jsx + jsy * jsy)); + if (magnitude > dz * 10000.0f) { + if (idzm == 1) { + int xSign = Signs(jsx); + if (xSign != 0) { + jsx = LinearMaps(jsx, xSign * (int)(dz * 10000), xSign * 10000, xSign * (int)(md * 10000), xSign * (int)(st * 10000)); + } + } else if (idzm == 2) { + int ySign = Signs(jsy); + if (ySign != 0) { + jsy = LinearMaps(jsy, ySign * (int)(dz * 10000.0f), ySign * 10000, ySign * (int)(md * 10000.0f), ySign * (int)(st * 10000)); + } + } else if (idzm == 3) { + float xNorm = (float)jsx / magnitude; + float yNorm = (float)jsy / magnitude; + float mapMag = LinearMaps(magnitude, dz * 10000.0f, 10000.0f, md * 10000.0f, 10000.0f * st); + jsx = (short)(xNorm * mapMag); + jsy = (short)(yNorm * mapMag); + } + } else { + jsx = 0; + jsy = 0; + } + + jsx = (short)std::min(10000.0f, std::max((float)jsx, -10000.0f)); + jsy = (short)std::min(10000.0f, std::max((float)jsy, -10000.0f)); +} + +static LONG *ValueForAxisId(DIJOYSTATE2 &js, int axisId) { + switch (axisId) { + case JOYSTICK_AXIS_X: return &js.lX; + case JOYSTICK_AXIS_Y: return &js.lY; + case JOYSTICK_AXIS_Z: return &js.lZ; + case JOYSTICK_AXIS_RX: return &js.lRx; + case JOYSTICK_AXIS_RY: return &js.lRy; + case JOYSTICK_AXIS_RZ: return &js.lRz; + default: return nullptr; + } +} + +static void ApplyNormalization(DIJOYSTATE2 &js, int xAxisId, int yAxisId) { + LONG *nrmX = ValueForAxisId(js, xAxisId); + LONG *nrmY = ValueForAxisId(js, yAxisId); + if (nrmX != nullptr && nrmY != nullptr) { + ApplyNormalization(*nrmX, *nrmY); + } +} + int DinputDevice::UpdateState() { if (!pJoystick) return -1; @@ -246,53 +302,11 @@ int DinputDevice::UpdateState() { AxisInput axis; axis.deviceId = DEVICE_ID_PAD_0 + pDevNum; - // Circle to Square mapping, cribbed from XInputDevice - float sx = (float)js.lX; - float sy = (float)js.lY; - float scaleFactor = sqrtf((sx * sx + sy * sy) / std::max(sx * sx, sy * sy)); - js.lX = (int)(sx * scaleFactor); - js.lY = (int)(sy * scaleFactor); - - // Linear range mapping (used to invert deadzones) - float dz = g_Config.fDInputAnalogDeadzone; - int idzm = g_Config.iDInputAnalogInverseMode; - float idz = g_Config.fDInputAnalogInverseDeadzone; - float md = std::max(dz, idz); - float st = g_Config.fDInputAnalogSensitivity; - - float magnitude = sqrtf((float)(js.lX * js.lX + js.lY * js.lY)); - if (magnitude > dz * 10000.0f) { - if (idzm == 1) - { - int xSign = Signs(js.lX); - if (xSign != 0) { - js.lX = LinearMaps(js.lX, xSign * (int)(dz * 10000), xSign * 10000, xSign * (int)(md * 10000), xSign * (int)(st * 10000)); - } - } - else if (idzm == 2) - { - int ySign = Signs(js.lY); - if (ySign != 0) { - js.lY = LinearMaps(js.lY, ySign * (int)(dz * 10000.0f), ySign * 10000, ySign * (int)(md * 10000.0f), ySign * (int)(st * 10000)); - } - } - else if (idzm == 3) - { - float xNorm = (float)js.lX / magnitude; - float yNorm = (float)js.lY / magnitude; - float mapMag = LinearMaps(magnitude, dz * 10000.0f, 10000.0f, md * 10000.0f, 10000.0f * st); - js.lX = (short)(xNorm * mapMag); - js.lY = (short)(yNorm * mapMag); - } - } - else - { - js.lX = 0; - js.lY = 0; - } - - js.lX = (short)std::min(10000.0f, std::max((float)js.lX, -10000.0f)); - js.lY = (short)std::min(10000.0f, std::max((float)js.lY, -10000.0f)); + auto axesToSquare = KeyMap::MappedAxesForDevice(axis.deviceId); + ApplyNormalization(js, axesToSquare.leftX.axisId, axesToSquare.leftY.axisId); + // Prevent double normalization. + if (axesToSquare.leftX.axisId != axesToSquare.rightX.axisId && axesToSquare.leftX.axisId != axesToSquare.rightY.axisId) + ApplyNormalization(js, axesToSquare.rightX.axisId, axesToSquare.rightY.axisId); SendNativeAxis(DEVICE_ID_PAD_0 + pDevNum, js.lX, last_lX_, JOYSTICK_AXIS_X); SendNativeAxis(DEVICE_ID_PAD_0 + pDevNum, js.lY, last_lY_, JOYSTICK_AXIS_Y); diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index a0a8a359fa..c14c9e0ccc 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -69,7 +69,7 @@ bool MainThread_Ready() { } static void EmuThreadFunc(GraphicsContext *graphicsContext) { - setCurrentThreadName("Emu"); + SetCurrentThreadName("Emu"); // There's no real requirement that NativeInit happen on this thread. // We just call the update/render loop here. @@ -111,10 +111,10 @@ static void EmuThreadJoin() { void MainThreadFunc() { if (useEmuThread) { // We'll start up a separate thread we'll call Emu - setCurrentThreadName("Render"); + SetCurrentThreadName("Render"); } else { // This is both Emu and Render. - setCurrentThreadName("Emu"); + SetCurrentThreadName("Emu"); } host = new WindowsHost(MainWindow::GetHInstance(), MainWindow::GetHWND(), MainWindow::GetDisplayHWND()); @@ -137,12 +137,12 @@ void MainThreadFunc() { if (!useEmuThread) { // Okay, we must've switched to OpenGL. Let's flip the emu thread on. useEmuThread = true; - setCurrentThreadName("Render"); + SetCurrentThreadName("Render"); } } else if (useEmuThread) { // We must've failed over from OpenGL, flip the emu thread off. useEmuThread = false; - setCurrentThreadName("Emu"); + SetCurrentThreadName("Emu"); } if (g_Config.sFailedGPUBackends.find("ALL") != std::string::npos) { @@ -263,6 +263,9 @@ void MainThreadFunc() { if (!Core_IsActive()) UpdateUIState(UISTATE_MENU); Core_Run(g_graphicsContext); + if (coreState == CORE_BOOT_ERROR) { + break; + } } } Core_Stop(); diff --git a/Windows/GEDebugger/GEDebugger.cpp b/Windows/GEDebugger/GEDebugger.cpp index e35f708eb6..706550ccbf 100644 --- a/Windows/GEDebugger/GEDebugger.cpp +++ b/Windows/GEDebugger/GEDebugger.cpp @@ -21,9 +21,9 @@ #include #include -#include "Common/Data/Text/Parsers.h" +#include "Common/Data/Convert/ColorConv.h" #include "Common/Data/Encoding/Utf8.h" -#include "Common/ColorConv.h" +#include "Common/Data/Text/Parsers.h" #include "Common/StringUtils.h" #include "Core/Config.h" #include "Core/Screenshot.h" @@ -319,6 +319,8 @@ void CGEDebugger::PreviewExport(const GPUDebugBuffer *dbgBuffer) { std::string fn; if (W32Util::BrowseForFileName(false, GetDlgHandle(), L"Save Preview Image...", nullptr, filter, L"png", fn)) { ScreenshotFormat fmt = fn.find(".jpg") != fn.npos ? ScreenshotFormat::JPG : ScreenshotFormat::PNG; + + Path filename(fn); bool saveAlpha = fmt == ScreenshotFormat::PNG; u8 *flipbuffer = nullptr; @@ -327,9 +329,9 @@ void CGEDebugger::PreviewExport(const GPUDebugBuffer *dbgBuffer) { const u8 *buffer = ConvertBufferToScreenshot(*dbgBuffer, saveAlpha, flipbuffer, w, h); if (buffer != nullptr) { if (saveAlpha) { - Save8888RGBAScreenshot(fn.c_str(), buffer, w, h); + Save8888RGBAScreenshot(filename, buffer, w, h); } else { - Save888RGBScreenshot(fn.c_str(), fmt, buffer, w, h); + Save888RGBScreenshot(filename, fmt, buffer, w, h); } } delete [] flipbuffer; diff --git a/Windows/InputDevice.cpp b/Windows/InputDevice.cpp index 28e6c2d379..850108a4b8 100644 --- a/Windows/InputDevice.cpp +++ b/Windows/InputDevice.cpp @@ -36,7 +36,7 @@ inline static void ExecuteInputPoll() { } static void RunInputThread() { - setCurrentThreadName("Input"); + SetCurrentThreadName("Input"); // NOTE: The keyboard and mouse buttons are handled via raw input, not here. // This is mainly for controllers which need to be polled, instead of generating events. diff --git a/Windows/MainWindow.cpp b/Windows/MainWindow.cpp index 00b8db86f4..980b07bca3 100644 --- a/Windows/MainWindow.cpp +++ b/Windows/MainWindow.cpp @@ -517,7 +517,6 @@ namespace MainWindow BOOL Show(HINSTANCE hInstance) { hInst = hInstance; // Store instance handle in our global variable. RECT rc = DetermineWindowRectangle(); - SavePosition(); u32 style = WS_OVERLAPPEDWINDOW; @@ -577,7 +576,7 @@ namespace MainWindow void CreateDebugWindows() { disasmWindow = new CDisasm(MainWindow::GetHInstance(), MainWindow::GetHWND(), currentDebugMIPS); DialogManager::AddDlg(disasmWindow); - disasmWindow->Show(g_Config.bShowDebuggerOnLoad); + disasmWindow->Show(g_Config.bShowDebuggerOnLoad, false); #if PPSSPP_API(ANY_GL) geDebuggerWindow = new CGEDebugger(MainWindow::GetHInstance(), MainWindow::GetHWND()); @@ -976,8 +975,6 @@ namespace MainWindow if (disasmWindow) disasmWindow->UpdateDialog(); - - SetForegroundWindow(hwndMain); break; case WM_USER_SAVESTATE_FINISH: diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index e5decc3957..6b971b7fff 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -50,7 +50,6 @@ #include "Core/Core.h" extern bool g_TakeScreenshot; -extern bool g_ShaderNameListChanged; namespace MainWindow { extern HINSTANCE hInst; @@ -169,8 +168,15 @@ namespace MainWindow { void UpdateDynamicMenuCheckmarks(HMENU menu) { int item = ID_SHADERS_BASE + 1; - for (size_t i = 0; i < availableShaders.size(); i++) - CheckMenuItem(menu, item++, ((g_Config.vPostShaderNames[0] == availableShaders[i] && (g_Config.vPostShaderNames[0] == "Off" || g_Config.vPostShaderNames[1] == "Off")) ? MF_CHECKED : MF_UNCHECKED)); + for (size_t i = 0; i < availableShaders.size(); i++) { + bool checked = false; + if (g_Config.vPostShaderNames.empty() && availableShaders[i] == "Off") + checked = true; + else if (g_Config.vPostShaderNames.size() == 1 && availableShaders[i] == g_Config.vPostShaderNames[0]) + checked = true; + + CheckMenuItem(menu, item++, checked ? MF_CHECKED : MF_UNCHECKED); + } } bool CreateShadersSubmenu(HMENU menu) { @@ -204,7 +210,9 @@ namespace MainWindow { continue; int checkedStatus = MF_UNCHECKED; availableShaders.push_back(i->section); - if (g_Config.vPostShaderNames[0] == i->section && (g_Config.vPostShaderNames[0] == "Off" || g_Config.vPostShaderNames[1] == "Off")) { + if (g_Config.vPostShaderNames.empty() && i->section == "Off") { + checkedStatus = MF_CHECKED; + } else if (g_Config.vPostShaderNames.size() == 1 && g_Config.vPostShaderNames[0] == i->section) { checkedStatus = MF_CHECKED; } @@ -432,9 +440,9 @@ namespace MainWindow { std::wstring src = ConvertUTF8ToWString(filename); std::wstring dest; if (filename.size() >= 5 && (filename.substr(filename.size() - 4) == ".jpg" || filename.substr(filename.size() - 5) == ".jpeg")) { - dest = ConvertUTF8ToWString(GetSysDirectory(DIRECTORY_SYSTEM) + "background.jpg"); + dest = (GetSysDirectory(DIRECTORY_SYSTEM) / "background.jpg").ToWString(); } else { - dest = ConvertUTF8ToWString(GetSysDirectory(DIRECTORY_SYSTEM) + "background.png"); + dest = (GetSysDirectory(DIRECTORY_SYSTEM) / "background.png").ToWString(); } CopyFileW(src.c_str(), dest.c_str(), FALSE); @@ -457,8 +465,7 @@ namespace MainWindow { } if (W32Util::BrowseForFileName(true, GetHWND(), L"Switch UMD", 0, ConvertUTF8ToWString(filter).c_str(), L"*.pbp;*.elf;*.iso;*.cso;", fn)) { - fn = ReplaceAll(fn, "\\", "/"); - __UmdReplace(fn); + __UmdReplace(Path(fn)); } } @@ -577,7 +584,7 @@ namespace MainWindow { break; case ID_FILE_LOAD_MEMSTICK: - BrowseAndBoot(GetSysDirectory(DIRECTORY_GAME)); + BrowseAndBoot(GetSysDirectory(DIRECTORY_GAME).ToString()); break; case ID_FILE_OPEN_NEW_INSTANCE: @@ -585,7 +592,7 @@ namespace MainWindow { break; case ID_FILE_MEMSTICK: - ShellExecute(NULL, L"open", ConvertUTF8ToWString(g_Config.memStickDirectory).c_str(), 0, 0, SW_SHOW); + ShellExecute(NULL, L"open", g_Config.memStickDirectory.ToWString().c_str(), 0, 0, SW_SHOW); break; case ID_TOGGLE_BREAK: @@ -653,14 +660,13 @@ namespace MainWindow { case ID_FILE_LOADSTATEFILE: if (W32Util::BrowseForFileName(true, hWnd, L"Load state", 0, L"Save States (*.ppst)\0*.ppst\0All files\0*.*\0\0", L"ppst", fn)) { SetCursor(LoadCursor(0, IDC_WAIT)); - SaveState::Load(fn, -1, SaveStateActionFinished); + SaveState::Load(Path(fn), -1, SaveStateActionFinished); } break; - case ID_FILE_SAVESTATEFILE: if (W32Util::BrowseForFileName(false, hWnd, L"Save state", 0, L"Save States (*.ppst)\0*.ppst\0All files\0*.*\0\0", L"ppst", fn)) { SetCursor(LoadCursor(0, IDC_WAIT)); - SaveState::Save(fn, -1, SaveStateActionFinished); + SaveState::Save(Path(fn), -1, SaveStateActionFinished); } break; @@ -864,7 +870,7 @@ namespace MainWindow { case ID_DEBUG_LOADMAPFILE: if (W32Util::BrowseForFileName(true, hWnd, L"Load .ppmap", 0, L"Maps\0*.ppmap\0All files\0*.*\0\0", L"ppmap", fn)) { - g_symbolMap->LoadSymbolMap(fn.c_str()); + g_symbolMap->LoadSymbolMap(Path(fn)); if (disasmWindow) disasmWindow->NotifyMapLoaded(); @@ -876,12 +882,12 @@ namespace MainWindow { case ID_DEBUG_SAVEMAPFILE: if (W32Util::BrowseForFileName(false, hWnd, L"Save .ppmap", 0, L"Maps\0*.ppmap\0All files\0*.*\0\0", L"ppmap", fn)) - g_symbolMap->SaveSymbolMap(fn.c_str()); + g_symbolMap->SaveSymbolMap(Path(fn)); break; case ID_DEBUG_LOADSYMFILE: if (W32Util::BrowseForFileName(true, hWnd, L"Load .sym", 0, L"Symbols\0*.sym\0All files\0*.*\0\0", L"sym", fn)) { - g_symbolMap->LoadNocashSym(fn.c_str()); + g_symbolMap->LoadNocashSym(Path(fn)); if (disasmWindow) disasmWindow->NotifyMapLoaded(); @@ -893,7 +899,7 @@ namespace MainWindow { case ID_DEBUG_SAVESYMFILE: if (W32Util::BrowseForFileName(false, hWnd, L"Save .sym", 0, L"Symbols\0*.sym\0All files\0*.*\0\0", L"sym", fn)) - g_symbolMap->SaveNocashSym(fn.c_str()); + g_symbolMap->SaveNocashSym(Path(fn)); break; case ID_DEBUG_RESETSYMBOLTABLE: @@ -948,7 +954,7 @@ namespace MainWindow { size_t len = pspFileSystem.SeekFile(handle, 0, FILEMOVE_END); bool isBlockMode = pspFileSystem.DevType(handle) & PSPDevType::BLOCK; - FILE *fp = File::OpenCFile(fn, "wb"); + FILE *fp = File::OpenCFile(Path(fn), "wb"); pspFileSystem.SeekFile(handle, 0, FILEMOVE_BEGIN); u8 buffer[4096]; size_t bufferSize = isBlockMode ? sizeof(buffer) / 2048 : sizeof(buffer); @@ -1077,10 +1083,10 @@ namespace MainWindow { g_Config.vPostShaderNames.clear(); if (availableShaders[index] != "Off") g_Config.vPostShaderNames.push_back(availableShaders[index]); - g_Config.vPostShaderNames.push_back("Off"); - g_ShaderNameListChanged = true; g_Config.bShaderChainRequires60FPS = PostShaderChainRequires60FPS(GetFullPostShadersChain(g_Config.vPostShaderNames)); + NativeMessageReceived("gpu_resized", ""); + NativeMessageReceived("postshader_updated", ""); break; } diff --git a/Windows/PPSSPP.sln b/Windows/PPSSPP.sln index 8859935282..d2b028722c 100644 --- a/Windows/PPSSPP.sln +++ b/Windows/PPSSPP.sln @@ -8,6 +8,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PPSSPPWindows", "PPSSPP.vcx {D8A71225-178B-424E-96C1-CC3BE2C1B047} = {D8A71225-178B-424E-96C1-CC3BE2C1B047} {129E5E2B-39C1-4D84-96FE-DFD22DBB4A25} = {129E5E2B-39C1-4D84-96FE-DFD22DBB4A25} {533F1D30-D04D-47CC-AD71-20F658907E36} = {533F1D30-D04D-47CC-AD71-20F658907E36} + {8BFD8150-94D5-4BF9-8A50-7BD9929A0850} = {8BFD8150-94D5-4BF9-8A50-7BD9929A0850} {F761046E-6C38-4428-A5F1-38391A37BB34} = {F761046E-6C38-4428-A5F1-38391A37BB34} {3BAAE095-E0AB-4B0E-B5DF-CE39C8AE31DE} = {3BAAE095-E0AB-4B0E-B5DF-CE39C8AE31DE} {457F45D2-556F-47BC-A31D-AFF0D15BEAED} = {457F45D2-556F-47BC-A31D-AFF0D15BEAED} @@ -16,6 +17,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PPSSPPWindows", "PPSSPP.vcx EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "..\Common\Common.vcxproj", "{3FCDBAE2-5103-4350-9A8E-848CE9C73195}" ProjectSection(ProjectDependencies) = postProject + {8BFD8150-94D5-4BF9-8A50-7BD9929A0850} = {8BFD8150-94D5-4BF9-8A50-7BD9929A0850} {F761046E-6C38-4428-A5F1-38391A37BB34} = {F761046E-6C38-4428-A5F1-38391A37BB34} EndProjectSection EndProject @@ -24,6 +26,7 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GPU", "..\GPU\GPU.vcxproj", "{457F45D2-556F-47BC-A31D-AFF0D15BEAED}" ProjectSection(ProjectDependencies) = postProject {4328A62C-F1E9-47ED-B816-A1A81DAF4363} = {4328A62C-F1E9-47ED-B816-A1A81DAF4363} + {8BFD8150-94D5-4BF9-8A50-7BD9929A0850} = {8BFD8150-94D5-4BF9-8A50-7BD9929A0850} {3FCDBAE2-5103-4350-9A8E-848CE9C73195} = {3FCDBAE2-5103-4350-9A8E-848CE9C73195} EndProjectSection EndProject @@ -78,6 +81,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZimTool", "..\ext\native\to EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniupnpc", "..\ext\miniupnpc.vcxproj", "{D8A71225-178B-424E-96C1-CC3BE2C1B047}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ext", "Ext", "{39FCACF8-10D9-4D8D-97AA-7507436AD932}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{999C12EA-A74F-4868-84F7-64C258043C49}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libzstd", "..\ext\libzstd.vcxproj", "{8BFD8150-94D5-4BF9-8A50-7BD9929A0850}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM @@ -332,10 +341,38 @@ Global {D8A71225-178B-424E-96C1-CC3BE2C1B047}.Release|Win32.Build.0 = Release|Win32 {D8A71225-178B-424E-96C1-CC3BE2C1B047}.Release|x64.ActiveCfg = Release|x64 {D8A71225-178B-424E-96C1-CC3BE2C1B047}.Release|x64.Build.0 = Release|x64 + {8BFD8150-94D5-4BF9-8A50-7BD9929A0850}.Debug|ARM.ActiveCfg = Debug|ARM + {8BFD8150-94D5-4BF9-8A50-7BD9929A0850}.Debug|ARM.Build.0 = Debug|ARM + {8BFD8150-94D5-4BF9-8A50-7BD9929A0850}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {8BFD8150-94D5-4BF9-8A50-7BD9929A0850}.Debug|ARM64.Build.0 = Debug|ARM64 + {8BFD8150-94D5-4BF9-8A50-7BD9929A0850}.Debug|Win32.ActiveCfg = Debug|Win32 + {8BFD8150-94D5-4BF9-8A50-7BD9929A0850}.Debug|Win32.Build.0 = Debug|Win32 + {8BFD8150-94D5-4BF9-8A50-7BD9929A0850}.Debug|x64.ActiveCfg = Debug|x64 + {8BFD8150-94D5-4BF9-8A50-7BD9929A0850}.Debug|x64.Build.0 = Debug|x64 + {8BFD8150-94D5-4BF9-8A50-7BD9929A0850}.Release|ARM.ActiveCfg = Release|ARM + {8BFD8150-94D5-4BF9-8A50-7BD9929A0850}.Release|ARM.Build.0 = Release|ARM + {8BFD8150-94D5-4BF9-8A50-7BD9929A0850}.Release|ARM64.ActiveCfg = Release|ARM64 + {8BFD8150-94D5-4BF9-8A50-7BD9929A0850}.Release|ARM64.Build.0 = Release|ARM64 + {8BFD8150-94D5-4BF9-8A50-7BD9929A0850}.Release|Win32.ActiveCfg = Release|Win32 + {8BFD8150-94D5-4BF9-8A50-7BD9929A0850}.Release|Win32.Build.0 = Release|Win32 + {8BFD8150-94D5-4BF9-8A50-7BD9929A0850}.Release|x64.ActiveCfg = Release|x64 + {8BFD8150-94D5-4BF9-8A50-7BD9929A0850}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {F761046E-6C38-4428-A5F1-38391A37BB34} = {39FCACF8-10D9-4D8D-97AA-7507436AD932} + {3BAAE095-E0AB-4B0E-B5DF-CE39C8AE31DE} = {39FCACF8-10D9-4D8D-97AA-7507436AD932} + {129E5E2B-39C1-4D84-96FE-DFD22DBB4A25} = {39FCACF8-10D9-4D8D-97AA-7507436AD932} + {EDFA2E87-8AC1-4853-95D4-D7594FF81947} = {39FCACF8-10D9-4D8D-97AA-7507436AD932} + {4328A62C-F1E9-47ED-B816-A1A81DAF4363} = {39FCACF8-10D9-4D8D-97AA-7507436AD932} + {BEB0A821-3C7F-410F-A525-63AFBC69BF8F} = {39FCACF8-10D9-4D8D-97AA-7507436AD932} + {FBE46262-7C2C-400C-92F9-F8A9E72594DD} = {999C12EA-A74F-4868-84F7-64C258043C49} + {B7DED405-40A2-48F8-9382-538F10D442F1} = {999C12EA-A74F-4868-84F7-64C258043C49} + {D8A71225-178B-424E-96C1-CC3BE2C1B047} = {39FCACF8-10D9-4D8D-97AA-7507436AD932} + {8BFD8150-94D5-4BF9-8A50-7BD9929A0850} = {39FCACF8-10D9-4D8D-97AA-7507436AD932} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2FD47774-A031-48F4-B645-A49A3140A29B} EndGlobalSection diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index bef161003e..e3b348add4 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -1686,6 +1686,9 @@ {3baae095-e0ab-4b0e-b5df-ce39c8ae31de} + + {8bfd8150-94d5-4bf9-8a50-7bd9929a0850} + {457f45d2-556f-47bc-a31d-aff0d15beaed} diff --git a/Windows/TouchInputHandler.cpp b/Windows/TouchInputHandler.cpp index f216e8ac80..d27850c759 100644 --- a/Windows/TouchInputHandler.cpp +++ b/Windows/TouchInputHandler.cpp @@ -26,11 +26,6 @@ TouchInputHandler::TouchInputHandler() { "RegisterTouchWindow"); } - -TouchInputHandler::~TouchInputHandler() -{ -} - int TouchInputHandler::ToTouchID(int windowsID, bool allowAllocate) { // Find the id for the touch. Avoid 0 (mouse.) for (int localId = 1; localId < (int)ARRAY_SIZE(touchIds); ++localId) { diff --git a/Windows/TouchInputHandler.h b/Windows/TouchInputHandler.h index 7d2761ecb7..457850298d 100644 --- a/Windows/TouchInputHandler.h +++ b/Windows/TouchInputHandler.h @@ -20,7 +20,6 @@ class TouchInputHandler { public: TouchInputHandler(); - ~TouchInputHandler(); void handleTouchEvent(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); void registerTouchWindow(HWND wnd); bool hasTouch(); diff --git a/Windows/W32Util/DialogManager.cpp b/Windows/W32Util/DialogManager.cpp index fdb89ac600..df131d9db4 100644 --- a/Windows/W32Util/DialogManager.cpp +++ b/Windows/W32Util/DialogManager.cpp @@ -30,11 +30,16 @@ void Dialog::Destroy() DestroyWindow(m_hDlg); } -void Dialog::Show(bool _bShow) +void Dialog::Show(bool _bShow, bool includeToTop) { - m_bShowState = _bShow ? SW_NORMAL : SW_HIDE; + if (_bShow && includeToTop) + m_bShowState = SW_SHOWNORMAL; + else if (_bShow) + m_bShowState = SW_SHOWNOACTIVATE; + else + m_bShowState = SW_HIDE; ShowWindow(m_hDlg, m_bShowState); - if (_bShow) + if (_bShow && includeToTop) BringWindowToTop(m_hDlg); } diff --git a/Windows/W32Util/DialogManager.h b/Windows/W32Util/DialogManager.h index f1093deb14..fc943b3ddd 100644 --- a/Windows/W32Util/DialogManager.h +++ b/Windows/W32Util/DialogManager.h @@ -8,7 +8,7 @@ public: Dialog(LPCSTR res, HINSTANCE _hInstance, HWND _hParent); virtual ~Dialog(); - virtual void Show(bool _bShow); + virtual void Show(bool _bShow, bool includeToTop = true); virtual void Update() {} HWND GetDlgHandle() { diff --git a/Windows/WASAPIStream.cpp b/Windows/WASAPIStream.cpp index 347f5e0560..e8f7fe79f7 100644 --- a/Windows/WASAPIStream.cpp +++ b/Windows/WASAPIStream.cpp @@ -513,7 +513,7 @@ void WASAPIAudioThread::Run() { int WASAPIAudioBackend::RunThread() { CoInitializeEx(nullptr, COINIT_MULTITHREADED); - setCurrentThreadName("WASAPI_audio"); + SetCurrentThreadName("WASAPI_audio"); if (threadData_ == 0) { // This will free everything once it's done. diff --git a/Windows/WindowsHost.cpp b/Windows/WindowsHost.cpp index 1bd7da7a3d..610b79890b 100644 --- a/Windows/WindowsHost.cpp +++ b/Windows/WindowsHost.cpp @@ -268,47 +268,30 @@ void WindowsHost::BootDone() { SetDebugMode(!g_Config.bAutoRun); } -static std::string SymbolMapFilename(const char *currentFilename, const char* ext) { - FileInfo info; - - std::string result = currentFilename; - +static Path SymbolMapFilename(const Path ¤tFilename, const char *ext) { + File::FileInfo info; // can't fail, definitely exists if it gets this far - getFileInfo(currentFilename, &info); + File::GetFileInfo(currentFilename, &info); if (info.isDirectory) { -#ifdef _WIN32 - const char* slash = "\\"; -#else - const char* slash = "/"; -#endif - if (!endsWith(result,slash)) - result += slash; - - return result + ".ppsspp-symbols" + ext; - } else { - const size_t dot = result.rfind('.'); - if (dot == result.npos) - return result + ext; - - result.replace(dot, result.npos, ext); - return result; + return currentFilename / (std::string(".ppsspp-symbols") + ext); } + return currentFilename.WithReplacedExtension(ext); } bool WindowsHost::AttemptLoadSymbolMap() { if (!g_symbolMap) return false; - bool result1 = g_symbolMap->LoadSymbolMap(SymbolMapFilename(PSP_CoreParameter().fileToStart.c_str(),".ppmap").c_str()); + bool result1 = g_symbolMap->LoadSymbolMap(SymbolMapFilename(PSP_CoreParameter().fileToStart, ".ppmap")); // Load the old-style map file. if (!result1) - result1 = g_symbolMap->LoadSymbolMap(SymbolMapFilename(PSP_CoreParameter().fileToStart.c_str(),".map").c_str()); - bool result2 = g_symbolMap->LoadNocashSym(SymbolMapFilename(PSP_CoreParameter().fileToStart.c_str(),".sym").c_str()); + result1 = g_symbolMap->LoadSymbolMap(SymbolMapFilename(PSP_CoreParameter().fileToStart, ".map")); + bool result2 = g_symbolMap->LoadNocashSym(SymbolMapFilename(PSP_CoreParameter().fileToStart, ".sym")); return result1 || result2; } void WindowsHost::SaveSymbolMap() { if (g_symbolMap) - g_symbolMap->SaveSymbolMap(SymbolMapFilename(PSP_CoreParameter().fileToStart.c_str(),".ppmap").c_str()); + g_symbolMap->SaveSymbolMap(SymbolMapFilename(PSP_CoreParameter().fileToStart, ".ppmap")); } void WindowsHost::NotifySymbolMapUpdated() { diff --git a/Windows/XinputDevice.cpp b/Windows/XinputDevice.cpp index 724e983819..4edbcf21a1 100644 --- a/Windows/XinputDevice.cpp +++ b/Windows/XinputDevice.cpp @@ -282,54 +282,34 @@ void XinputDevice::UpdatePad(int pad, const XINPUT_STATE &state, XINPUT_VIBRATIO const float STICK_INV_DEADZONE = g_Config.fXInputAnalogInverseDeadzone; const float STICK_SENSITIVITY = g_Config.fXInputAnalogSensitivity; + AxisInput axis; + axis.deviceId = DEVICE_ID_X360_0 + pad; + auto sendAxis = [&](AndroidJoystickAxis axisId, float value) { + axis.axisId = axisId; + axis.value = value; + NativeAxis(axis); + }; + if (NormalizedDeadzoneDiffers(prevState[pad].Gamepad.sThumbLX, prevState[pad].Gamepad.sThumbLY, state.Gamepad.sThumbLX, state.Gamepad.sThumbLY, STICK_DEADZONE)) { Stick left = NormalizedDeadzoneFilter(state.Gamepad.sThumbLX, state.Gamepad.sThumbLY, STICK_DEADZONE, STICK_INV_MODE, STICK_INV_DEADZONE, STICK_SENSITIVITY); - AxisInput axis; - axis.deviceId = DEVICE_ID_X360_0 + pad; - axis.axisId = JOYSTICK_AXIS_X; - axis.value = left.x; - if (prevState[pad].Gamepad.sThumbLX != state.Gamepad.sThumbLX) { - NativeAxis(axis); - } - axis.axisId = JOYSTICK_AXIS_Y; - axis.value = left.y; - if (prevState[pad].Gamepad.sThumbLY != state.Gamepad.sThumbLY) { - NativeAxis(axis); - } + sendAxis(JOYSTICK_AXIS_X, left.x); + sendAxis(JOYSTICK_AXIS_Y, left.y); } if (NormalizedDeadzoneDiffers(prevState[pad].Gamepad.sThumbRX, prevState[pad].Gamepad.sThumbRY, state.Gamepad.sThumbRX, state.Gamepad.sThumbRY, STICK_DEADZONE)) { Stick right = NormalizedDeadzoneFilter(state.Gamepad.sThumbRX, state.Gamepad.sThumbRY, STICK_DEADZONE, STICK_INV_MODE, STICK_INV_DEADZONE, STICK_SENSITIVITY); - AxisInput axis; - axis.deviceId = DEVICE_ID_X360_0 + pad; - axis.axisId = JOYSTICK_AXIS_Z; - axis.value = right.x; - if (prevState[pad].Gamepad.sThumbRX != state.Gamepad.sThumbRX) { - NativeAxis(axis); - } - axis.axisId = JOYSTICK_AXIS_RZ; - axis.value = right.y; - if (prevState[pad].Gamepad.sThumbRY != state.Gamepad.sThumbRY) { - NativeAxis(axis); - } + sendAxis(JOYSTICK_AXIS_Z, right.x); + sendAxis(JOYSTICK_AXIS_RZ, right.y); } if (NormalizedDeadzoneDiffers(prevState[pad].Gamepad.bLeftTrigger, state.Gamepad.bLeftTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD)) { - AxisInput axis; - axis.deviceId = DEVICE_ID_X360_0 + pad; - axis.axisId = JOYSTICK_AXIS_LTRIGGER; - axis.value = (float)state.Gamepad.bLeftTrigger / 255.0f; - NativeAxis(axis); + sendAxis(JOYSTICK_AXIS_LTRIGGER, (float)state.Gamepad.bLeftTrigger / 255.0f); } if (NormalizedDeadzoneDiffers(prevState[pad].Gamepad.bRightTrigger, state.Gamepad.bRightTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD)) { - AxisInput axis; - axis.deviceId = DEVICE_ID_X360_0 + pad; - axis.axisId = JOYSTICK_AXIS_RTRIGGER; - axis.value = (float)state.Gamepad.bRightTrigger / 255.0f; - NativeAxis(axis); + sendAxis(JOYSTICK_AXIS_RTRIGGER, (float)state.Gamepad.bRightTrigger / 255.0f); } prevState[pad] = state; diff --git a/Windows/main.cpp b/Windows/main.cpp index 34bac908a1..49d6de445e 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -517,7 +517,7 @@ static void WinMainCleanup() { } int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow) { - setCurrentThreadName("Main"); + SetCurrentThreadName("Main"); WinMainInit(); @@ -527,9 +527,9 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin bool showLog = true; #endif - const std::string &exePath = File::GetExeDirectory(); - VFSRegister("", new DirectoryAssetReader((exePath + "/assets/").c_str())); - VFSRegister("", new DirectoryAssetReader(exePath.c_str())); + const Path &exePath = File::GetExeDirectory(); + VFSRegister("", new DirectoryAssetReader(exePath / "assets")); + VFSRegister("", new DirectoryAssetReader(exePath)); langRegion = GetDefaultLangRegion(); osName = GetWindowsVersion() + " " + GetWindowsSystemArchitecture(); @@ -562,7 +562,7 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin // On Win32 it makes more sense to initialize the system directories here // because the next place it was called was in the EmuThread, and it's too late by then. - g_Config.internalDataDirectory = W32Util::UserDocumentsPath(); + g_Config.internalDataDirectory = Path(W32Util::UserDocumentsPath()); InitSysDirectories(); // Check for the Vulkan workaround before any serious init. @@ -583,7 +583,7 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin // Load config up here, because those changes below would be overwritten // if it's not loaded here first. - g_Config.AddSearchPath(""); + g_Config.AddSearchPath(Path()); g_Config.AddSearchPath(GetSysDirectory(DIRECTORY_SYSTEM)); g_Config.SetDefaultPath(GetSysDirectory(DIRECTORY_SYSTEM)); g_Config.Load(configFilename.c_str(), controlsConfigFilename.c_str()); diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index 0fe212de73..5be04b151d 100644 --- a/Windows/ppsspp.rc +++ b/Windows/ppsspp.rc @@ -680,6 +680,9 @@ BEGIN MENUITEM "Go to in Disasm", ID_MEMVIEW_GOTOINDISASM MENUITEM "Copy address", ID_MEMVIEW_COPYADDRESS MENUITEM SEPARATOR + MENUITEM "Go to Extent Begin", ID_MEMVIEW_EXTENTBEGIN + MENUITEM "Go to Extent End", ID_MEMVIEW_EXTENTEND + MENUITEM SEPARATOR MENUITEM "Copy Value (8 bit)", ID_MEMVIEW_COPYVALUE_8 MENUITEM "Copy Value (16 bit)", ID_MEMVIEW_COPYVALUE_16 MENUITEM "Copy Value (32 bit)", ID_MEMVIEW_COPYVALUE_32 diff --git a/Windows/pspmode.png b/Windows/pspmode.png index f6c67d4fac..402d250210 100644 Binary files a/Windows/pspmode.png and b/Windows/pspmode.png differ diff --git a/Windows/resource.h b/Windows/resource.h index bc4dfb7aa1..cdef7075a2 100644 --- a/Windows/resource.h +++ b/Windows/resource.h @@ -376,6 +376,8 @@ #define ID_FILE_DUMP_VIDEO_OUTPUT 40204 #define ID_EMULATION_CHAT 40205 #define IDC_MEMVIEW_STATUS 40206 +#define ID_MEMVIEW_EXTENTBEGIN 40207 +#define ID_MEMVIEW_EXTENTEND 40208 // Dummy option to let the buffered rendering hotkey cycle through all the options. #define ID_OPTIONS_BUFFEREDRENDERINGDUMMY 40500 @@ -391,7 +393,7 @@ #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 256 -#define _APS_NEXT_COMMAND_VALUE 40207 +#define _APS_NEXT_COMMAND_VALUE 40209 #define _APS_NEXT_CONTROL_VALUE 1202 #define _APS_NEXT_SYMED_VALUE 101 #endif diff --git a/Windows/stdafx.h b/Windows/stdafx.h index e8c667e29a..97093c82b5 100644 --- a/Windows/stdafx.h +++ b/Windows/stdafx.h @@ -67,6 +67,5 @@ #include #include #include -#include #include "Common/Log.h" diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 5466ddb024..e4ce3bb29f 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -3,8 +3,8 @@ xmlns:tools="http://schemas.android.com/tools" package="org.ppsspp.ppsspp" android:installLocation="auto" - android:versionCode="111020000" - android:versionName="1.11.2.0"> + android:versionCode="111030000" + android:versionName="1.11.3.0"> diff --git a/android/assets/rargray.png b/android/assets/rargray.png index 90bdf104cd..a1ae9d0b5b 100644 Binary files a/android/assets/rargray.png and b/android/assets/rargray.png differ diff --git a/android/assets/unknown.png b/android/assets/unknown.png index 1a233d9519..703da1ab70 100644 Binary files a/android/assets/unknown.png and b/android/assets/unknown.png differ diff --git a/android/assets/zip.png b/android/assets/zip.png index beb6822e5f..1ea89442fc 100644 Binary files a/android/assets/zip.png and b/android/assets/zip.png differ diff --git a/android/gold/res/drawable-hdpi/ic_launcher.png b/android/gold/res/drawable-hdpi/ic_launcher.png index bbc99d3df6..6623629741 100644 Binary files a/android/gold/res/drawable-hdpi/ic_launcher.png and b/android/gold/res/drawable-hdpi/ic_launcher.png differ diff --git a/android/gold/res/drawable-ldpi/ic_launcher.png b/android/gold/res/drawable-ldpi/ic_launcher.png index 6092852d73..2b6a409065 100644 Binary files a/android/gold/res/drawable-ldpi/ic_launcher.png and b/android/gold/res/drawable-ldpi/ic_launcher.png differ diff --git a/android/gold/res/drawable-mdpi/ic_launcher.png b/android/gold/res/drawable-mdpi/ic_launcher.png index 5c433600cf..d2df261bab 100644 Binary files a/android/gold/res/drawable-mdpi/ic_launcher.png and b/android/gold/res/drawable-mdpi/ic_launcher.png differ diff --git a/android/gold/res/drawable-xhdpi/ic_launcher.png b/android/gold/res/drawable-xhdpi/ic_launcher.png index 061b07fa18..c8e6e7d3d4 100644 Binary files a/android/gold/res/drawable-xhdpi/ic_launcher.png and b/android/gold/res/drawable-xhdpi/ic_launcher.png differ diff --git a/android/gold/res/drawable-xhdpi/tv_banner.png b/android/gold/res/drawable-xhdpi/tv_banner.png index 08d8380b08..b3a33e79aa 100644 Binary files a/android/gold/res/drawable-xhdpi/tv_banner.png and b/android/gold/res/drawable-xhdpi/tv_banner.png differ diff --git a/android/gold/res/drawable-xxhdpi/ic_launcher.png b/android/gold/res/drawable-xxhdpi/ic_launcher.png index 5e0c9b0b01..f1d666718c 100644 Binary files a/android/gold/res/drawable-xxhdpi/ic_launcher.png and b/android/gold/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/android/gold/res/drawable/ic_banner.png b/android/gold/res/drawable/ic_banner.png index f00e437672..8f0d50bca3 100644 Binary files a/android/gold/res/drawable/ic_banner.png and b/android/gold/res/drawable/ic_banner.png differ diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 7475abcf13..d699a76b80 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -16,7 +16,6 @@ ARCH_FILES := \ $(SRC)/Common/ABI.cpp \ $(SRC)/Common/x64Emitter.cpp \ $(SRC)/Common/x64Analyzer.cpp \ - $(SRC)/Common/CPUDetect.cpp \ $(SRC)/Common/Math/fast/fast_matrix_sse.c \ $(SRC)/Common/Thunk.cpp \ $(SRC)/Core/MIPS/x86/CompALU.cpp \ @@ -39,7 +38,6 @@ ARCH_FILES := \ $(SRC)/Common/ABI.cpp \ $(SRC)/Common/x64Emitter.cpp \ $(SRC)/Common/x64Analyzer.cpp \ - $(SRC)/Common/CPUDetect.cpp \ $(SRC)/Common/Math/fast/fast_matrix_sse.c \ $(SRC)/Common/Thunk.cpp \ $(SRC)/Core/MIPS/x86/CompALU.cpp \ @@ -62,8 +60,7 @@ ARCH_FILES := \ $(SRC)/GPU/Common/TextureDecoderNEON.cpp.neon \ $(SRC)/Core/Util/AudioFormatNEON.cpp.neon \ $(SRC)/Common/ArmEmitter.cpp \ - $(SRC)/Common/ArmCPUDetect.cpp \ - $(SRC)/Common/ColorConvNEON.cpp.neon \ + $(SRC)/Common/Data/Convert/ColorConvNEON.cpp.neon \ $(SRC)/Common/Math/fast/fast_matrix_neon.S.neon \ $(SRC)/Core/MIPS/ARM/ArmCompALU.cpp \ $(SRC)/Core/MIPS/ARM/ArmCompBranch.cpp \ @@ -90,8 +87,7 @@ ARCH_FILES := \ $(SRC)/GPU/Common/TextureDecoderNEON.cpp \ $(SRC)/Core/Util/AudioFormatNEON.cpp \ $(SRC)/Common/Arm64Emitter.cpp \ - $(SRC)/Common/ArmCPUDetect.cpp \ - $(SRC)/Common/ColorConvNEON.cpp \ + $(SRC)/Common/Data/Convert/ColorConvNEON.cpp \ $(SRC)/Core/MIPS/ARM64/Arm64CompALU.cpp \ $(SRC)/Core/MIPS/ARM64/Arm64CompBranch.cpp \ $(SRC)/Core/MIPS/ARM64/Arm64CompFPU.cpp \ @@ -104,6 +100,8 @@ ARCH_FILES := \ $(SRC)/Core/MIPS/ARM64/Arm64RegCacheFPU.cpp \ $(SRC)/Core/Util/DisArm64.cpp \ $(SRC)/GPU/Common/VertexDecoderArm64.cpp \ + $(SRC)/ext/libpng17/arm/arm_init.c \ + $(SRC)/ext/libpng17/arm/filter_neon_intrinsics.c \ Arm64EmitterTest.cpp endif @@ -232,6 +230,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Common/Crypto/sha1.cpp \ $(SRC)/Common/Crypto/sha256.cpp \ $(SRC)/Common/Data/Color/RGBAUtil.cpp \ + $(SRC)/Common/Data/Convert/ColorConv.cpp \ $(SRC)/Common/Data/Convert/SmallDataConvert.cpp \ $(SRC)/Common/Data/Encoding/Base64.cpp \ $(SRC)/Common/Data/Encoding/Compression.cpp \ @@ -253,6 +252,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Common/File/VFS/VFS.cpp \ $(SRC)/Common/File/VFS/AssetReader.cpp \ $(SRC)/Common/File/DiskFree.cpp \ + $(SRC)/Common/File/Path.cpp \ $(SRC)/Common/File/PathBrowser.cpp \ $(SRC)/Common/File/FileUtil.cpp \ $(SRC)/Common/File/DirListing.cpp \ @@ -277,13 +277,13 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Common/Net/HTTPClient.cpp \ $(SRC)/Common/Net/HTTPHeaders.cpp \ $(SRC)/Common/Net/HTTPServer.cpp \ + $(SRC)/Common/Net/NetBuffer.cpp \ $(SRC)/Common/Net/Resolve.cpp \ $(SRC)/Common/Net/Sinks.cpp \ $(SRC)/Common/Net/URL.cpp \ $(SRC)/Common/Net/WebsocketServer.cpp \ $(SRC)/Common/Profiler/Profiler.cpp \ $(SRC)/Common/System/Display.cpp \ - $(SRC)/Common/Thread/Executor.cpp \ $(SRC)/Common/Thread/PrioritizedWorkQueue.cpp \ $(SRC)/Common/Thread/ThreadPool.cpp \ $(SRC)/Common/Thread/ThreadUtil.cpp \ @@ -296,8 +296,10 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Common/UI/View.cpp \ $(SRC)/Common/UI/ViewGroup.cpp \ $(SRC)/Common/Serialize/Serializer.cpp \ - $(SRC)/Common/ColorConv.cpp \ + $(SRC)/Common/ArmCPUDetect.cpp \ + $(SRC)/Common/CPUDetect.cpp \ $(SRC)/Common/ExceptionHandlerSetup.cpp \ + $(SRC)/Common/FakeCPUDetect.cpp \ $(SRC)/Common/Log.cpp \ $(SRC)/Common/LogManager.cpp \ $(SRC)/Common/MemArenaAndroid.cpp \ @@ -305,6 +307,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Common/MemArenaWin32.cpp \ $(SRC)/Common/MemArenaPosix.cpp \ $(SRC)/Common/MemoryUtil.cpp \ + $(SRC)/Common/MipsCPUDetect.cpp \ $(SRC)/Common/StringUtils.cpp \ $(SRC)/Common/SysError.cpp \ $(SRC)/Common/TimeUtil.cpp \ @@ -418,6 +421,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/Debugger/WebSocket/InputSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/LogBroadcaster.cpp \ $(SRC)/Core/Debugger/WebSocket/MemorySubscriber.cpp \ + $(SRC)/Core/Debugger/WebSocket/MemoryInfoSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/SteppingBroadcaster.cpp \ $(SRC)/Core/Debugger/WebSocket/SteppingSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/WebSocketUtils.cpp \ @@ -584,7 +588,45 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) include $(LOCAL_PATH)/Locals.mk -LOCAL_STATIC_LIBRARIES += ppsspp_core libarmips + +LIBZSTD_FILES := \ + $(SRC)/ext/zstd/lib/common/debug.c \ + $(SRC)/ext/zstd/lib/common/entropy_common.c \ + $(SRC)/ext/zstd/lib/common/error_private.c \ + $(SRC)/ext/zstd/lib/common/fse_decompress.c \ + $(SRC)/ext/zstd/lib/common/pool.c \ + $(SRC)/ext/zstd/lib/common/threading.c \ + $(SRC)/ext/zstd/lib/common/xxhash.c \ + $(SRC)/ext/zstd/lib/common/zstd_common.c \ + $(SRC)/ext/zstd/lib/compress/fse_compress.c \ + $(SRC)/ext/zstd/lib/compress/hist.c \ + $(SRC)/ext/zstd/lib/compress/huf_compress.c \ + $(SRC)/ext/zstd/lib/compress/zstd_compress.c \ + $(SRC)/ext/zstd/lib/compress/zstd_compress_literals.c \ + $(SRC)/ext/zstd/lib/compress/zstd_compress_sequences.c \ + $(SRC)/ext/zstd/lib/compress/zstd_compress_superblock.c \ + $(SRC)/ext/zstd/lib/compress/zstd_double_fast.c \ + $(SRC)/ext/zstd/lib/compress/zstd_fast.c \ + $(SRC)/ext/zstd/lib/compress/zstd_lazy.c \ + $(SRC)/ext/zstd/lib/compress/zstd_ldm.c \ + $(SRC)/ext/zstd/lib/compress/zstd_opt.c \ + $(SRC)/ext/zstd/lib/compress/zstdmt_compress.c \ + $(SRC)/ext/zstd/lib/decompress/huf_decompress.c \ + $(SRC)/ext/zstd/lib/decompress/zstd_ddict.c \ + $(SRC)/ext/zstd/lib/decompress/zstd_decompress.c \ + $(SRC)/ext/zstd/lib/decompress/zstd_decompress_block.c \ + $(SRC)/ext/zstd/lib/dictBuilder/cover.c \ + $(SRC)/ext/zstd/lib/dictBuilder/divsufsort.c \ + $(SRC)/ext/zstd/lib/dictBuilder/fastcover.c \ + $(SRC)/ext/zstd/lib/dictBuilder/zdict.c + +LOCAL_MODULE := libzstd +LOCAL_SRC_FILES := $(LIBZSTD_FILES) +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +include $(LOCAL_PATH)/Locals.mk +LOCAL_STATIC_LIBRARIES += ppsspp_core libarmips libzstd # These are the files just for ppsspp_jni LOCAL_MODULE := ppsspp_jni @@ -634,7 +676,7 @@ endif ifeq ($(HEADLESS),1) include $(CLEAR_VARS) include $(LOCAL_PATH)/Locals.mk - LOCAL_STATIC_LIBRARIES += ppsspp_core libarmips + LOCAL_STATIC_LIBRARIES += ppsspp_core libarmips libzstd # Android 5.0 requires PIE for executables. Only supported on 4.1+, but this is testing anyway. LOCAL_CFLAGS += -fPIE @@ -652,7 +694,7 @@ endif ifeq ($(UNITTEST),1) include $(CLEAR_VARS) include $(LOCAL_PATH)/Locals.mk - LOCAL_STATIC_LIBRARIES += ppsspp_core libarmips + LOCAL_STATIC_LIBRARIES += ppsspp_core libarmips libzstd # Android 5.0 requires PIE for executables. Only supported on 4.1+, but this is testing anyway. LOCAL_CFLAGS += -fPIE diff --git a/android/jni/Locals.mk b/android/jni/Locals.mk index 44e8f57cf8..552d4f1f96 100644 --- a/android/jni/Locals.mk +++ b/android/jni/Locals.mk @@ -16,6 +16,7 @@ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/$(NATIVE)/base \ $(LOCAL_PATH)/../../ext/libpng17 \ $(LOCAL_PATH)/../../ext/libzip \ + $(LOCAL_PATH)/../../ext/zstd/lib \ $(LOCAL_PATH)/$(NATIVE) \ $(LOCAL_PATH) diff --git a/android/jni/TestRunner.cpp b/android/jni/TestRunner.cpp index 3f9feed41a..281f904fab 100644 --- a/android/jni/TestRunner.cpp +++ b/android/jni/TestRunner.cpp @@ -21,7 +21,6 @@ // It currently just runs one test but that can be easily changed. #include -#include #include #include @@ -60,27 +59,27 @@ static std::string TrimNewlines(const std::string &s) { bool TestsAvailable() { #if PPSSPP_PLATFORM(IOS) - std::string testDirectory = g_Config.flash0Directory + "../"; + Path testDirectory = g_Config.flash0Directory / ".."; #else - std::string testDirectory = g_Config.memStickDirectory; + Path testDirectory = g_Config.memStickDirectory; #endif // Hack to easily run the tests on Windows from the submodule - if (File::IsDirectory("../pspautotests")) { - testDirectory = "../"; + if (File::IsDirectory(Path("../pspautotests"))) { + testDirectory = Path(".."); } - return File::Exists(testDirectory + "pspautotests/tests/"); + return File::Exists(testDirectory / "pspautotests" / "tests"); } bool RunTests() { std::string output; #if PPSSPP_PLATFORM(IOS) - std::string baseDirectory = g_Config.flash0Directory + "../"; + Path baseDirectory = g_Config.flash0Directory / ".."; #else - std::string baseDirectory = g_Config.memStickDirectory; + Path baseDirectory = g_Config.memStickDirectory; // Hack to easily run the tests on Windows from the submodule - if (File::IsDirectory("../pspautotests")) { - baseDirectory = "../"; + if (File::IsDirectory(Path("../pspautotests"))) { + baseDirectory = Path(".."); } #endif @@ -93,8 +92,8 @@ bool RunTests() { coreParam.gpuCore = GPUCORE_SOFTWARE; coreParam.enableSound = g_Config.bEnableSound; coreParam.graphicsContext = nullptr; - coreParam.mountIso = ""; - coreParam.mountRoot = baseDirectory + "pspautotests/"; + coreParam.mountIso.clear(); + coreParam.mountRoot = baseDirectory / "pspautotests"; coreParam.startBreak = false; coreParam.printfEmuLog = false; coreParam.headLess = true; @@ -111,11 +110,11 @@ bool RunTests() { g_Config.sReportHost = ""; for (size_t i = 0; i < ARRAY_SIZE(testsToRun); i++) { - const char *testName = testsToRun[i]; - coreParam.fileToStart = baseDirectory + "pspautotests/tests/" + testName + ".prx"; - std::string expectedFile = baseDirectory + "pspautotests/tests/" + testName + ".expected"; + std::string testName = testsToRun[i]; + coreParam.fileToStart = baseDirectory / "pspautotests" / "tests" / (testName + ".prx"); + Path expectedFile = baseDirectory / "pspautotests" / "tests" / (testName + ".expected"); - INFO_LOG(SYSTEM, "Preparing to execute '%s'", testName); + INFO_LOG(SYSTEM, "Preparing to execute '%s'", testName.c_str()); std::string error_string; output = ""; if (!PSP_Init(coreParam, &error_string)) { @@ -139,18 +138,19 @@ bool RunTests() { // set back to running for the next frame coreState = CORE_RUNNING; } else if (coreState == CORE_POWERDOWN) { - INFO_LOG(SYSTEM, "Finished running test %s", testName); + INFO_LOG(SYSTEM, "Finished running test %s", testName.c_str()); break; } } PSP_EndHostFrame(); - - std::ifstream expected(expectedFile.c_str(), std::ios_base::in); - if (!expected) { + + std::string expect_results; + if (!File::ReadFileToString(true, expectedFile, expect_results)) { ERROR_LOG(SYSTEM, "Error opening expectedFile %s", expectedFile.c_str()); break; } + std::istringstream expected(expect_results); std::istringstream logoutput(output); int line = 0; diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 287c8b2ac8..2fad162248 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -60,6 +60,7 @@ struct JNIEnv {}; #include "Common/System/NativeApp.h" #include "Common/System/System.h" #include "Common/Thread/ThreadUtil.h" +#include "Common/File/Path.h" #include "Common/File/VFS/VFS.h" #include "Common/File/VFS/AssetReader.h" #include "Common/Input/InputState.h" @@ -248,9 +249,9 @@ int Android_OpenContentUriFd(const std::string &filename) { class ContentURIFileLoader : public ProxiedFileLoader { public: - ContentURIFileLoader(const std::string &filename) + ContentURIFileLoader(const Path &filename) : ProxiedFileLoader(nullptr) { // we overwrite the nullptr below - int fd = Android_OpenContentUriFd(filename); + int fd = Android_OpenContentUriFd(filename.ToString()); INFO_LOG(SYSTEM, "Fd %d for content URI: '%s'", fd, filename.c_str()); backend_ = new LocalFileLoader(fd, filename); } @@ -267,7 +268,7 @@ public: class AndroidContentLoaderFactory : public FileLoaderFactory { public: AndroidContentLoaderFactory() {} - FileLoader *ConstructFileLoader(const std::string &filename) override { + FileLoader *ConstructFileLoader(const Path &filename) override { return new ContentURIFileLoader(filename); } }; @@ -308,7 +309,7 @@ static void EmuThreadFunc() { JNIEnv *env; gJvm->AttachCurrentThread(&env, nullptr); - setCurrentThreadName("Emu"); + SetCurrentThreadName("Emu"); INFO_LOG(SYSTEM, "Entering emu thread"); // Wait for render loop to get started. @@ -618,7 +619,7 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init (JNIEnv *env, jclass, jstring jmodel, jint jdeviceType, jstring jlangRegion, jstring japkpath, jstring jdataDir, jstring jexternalStorageDir, jstring jexternalFilesDir, jstring jadditionalStorageDirs, jstring jlibraryDir, jstring jcacheDir, jstring jshortcutParam, jint jAndroidVersion, jstring jboard) { - setCurrentThreadName("androidInit"); + SetCurrentThreadName("androidInit"); // Makes sure we get early permission grants. ProcessFrameCommands(env); @@ -1025,7 +1026,7 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayRender(JNIEnv *env, static bool hasSetThreadName = false; if (!hasSetThreadName) { hasSetThreadName = true; - setCurrentThreadName("AndroidRender"); + SetCurrentThreadName("AndroidRender"); } if (useCPUThread) { @@ -1416,7 +1417,7 @@ extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(J static bool hasSetThreadName = false; if (!hasSetThreadName) { hasSetThreadName = true; - setCurrentThreadName("AndroidRender"); + SetCurrentThreadName("AndroidRender"); } } @@ -1459,7 +1460,7 @@ extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(J } extern "C" jstring Java_org_ppsspp_ppsspp_ShortcutActivity_queryGameName(JNIEnv *env, jclass, jstring jpath) { - std::string path = GetJavaString(env, jpath); + Path path = Path(GetJavaString(env, jpath)); std::string result = ""; GameInfoCache *cache = new GameInfoCache(); diff --git a/android/res/drawable-xhdpi/ouya_icon.png b/android/res/drawable-xhdpi/ouya_icon.png index 4c294e4ea0..8daf84f84b 100644 Binary files a/android/res/drawable-xhdpi/ouya_icon.png and b/android/res/drawable-xhdpi/ouya_icon.png differ diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index c859faf7c8..e0286d6102 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -1145,7 +1145,9 @@ public abstract class NativeActivity extends Activity { Uri selectedFile = data.getData(); if (selectedFile != null) { // Grab permanent permission so we can show it in recents list etc. - getContentResolver().takePersistableUriPermission(selectedFile, Intent.FLAG_GRANT_READ_URI_PERMISSION); + if (Build.VERSION.SDK_INT >= 19) { + getContentResolver().takePersistableUriPermission(selectedFile, Intent.FLAG_GRANT_READ_URI_PERMISSION); + } Log.i(TAG, "Browse file finished:" + selectedFile.toString()); NativeApp.sendMessage("browse_fileSelect", selectedFile.toString()); } diff --git a/assets/7z.png b/assets/7z.png index b49535297f..bbcf04cfdd 100644 Binary files a/assets/7z.png and b/assets/7z.png differ diff --git a/assets/compat.ini b/assets/compat.ini index f3466b5061..8990c24ae5 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -1105,12 +1105,6 @@ ULJM05533 = true NPJH50006 = true ULES01301 = true -[DoublePrecisionSinCos] -# Hitman Reborn Battle Arena 2 (#12900) -ULJS00218 = true -# Hitman Reborn Battle Arena -ULJS00157 = true - [DisableFirstFrameReadback] # Wipeout Pure: Temporary workaround for lens flare flicker. See #13344 UCUS98612 = true diff --git a/assets/gamecontrollerdb.txt b/assets/gamecontrollerdb.txt index 91c61ed79e..004121d612 100644 --- a/assets/gamecontrollerdb.txt +++ b/assets/gamecontrollerdb.txt @@ -198,3 +198,4 @@ bd1200000000000015d0000000000000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,b 03000000c82d00000260000011010000,8Bitdo SF30 Pro 8BitDo SN30 Pro+,platform:Linux,a:b1,b:b0,x:b4,y:b3,back:b10,guide:b2,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4, 060000007e0500000820000000000000,Nintendo Switch Combined Joy-Cons,platform:Linux,a:b0,b:b1,x:b3,y:b2,back:b9,guide:b11,start:b10,leftstick:b12,rightstick:b13,leftshoulder:b5,rightshoulder:b6,dpup:b14,dpdown:b15,dpleft:b16,dpright:b17,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b7,righttrigger:b8, 05000000491900000204000000000000,PG-9118,platform:Linux,x:b76,a:b73,b:b74,y:b77,back:b83,start:b84,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b79,lefttrigger:b81,rightshoulder:b80,righttrigger:b82,leftstick:b86,rightstick:b87,leftx:a0,lefty:a1,rightx:a2,righty:a3, +03000000d620000011a7000011010000,PowerA Nintendo Switch Wired Controller,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,leftstick:b10,rightstick:b11,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Linux, diff --git a/assets/lang b/assets/lang index ddb1246ec2..3bf6b18eb0 160000 --- a/assets/lang +++ b/assets/lang @@ -1 +1 @@ -Subproject commit ddb1246ec2ad2548711f4b966a801e80b7fbe73f +Subproject commit 3bf6b18eb0ae68861fbfbb7b43cd764771ca42a9 diff --git a/assets/rargray.png b/assets/rargray.png index 90bdf104cd..a1ae9d0b5b 100644 Binary files a/assets/rargray.png and b/assets/rargray.png differ diff --git a/assets/shaders/4xhqglsl.vsh b/assets/shaders/4xhqglsl.vsh index 128512e413..3ed7ccda8e 100644 --- a/assets/shaders/4xhqglsl.vsh +++ b/assets/shaders/4xhqglsl.vsh @@ -1,3 +1,8 @@ +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + attribute vec4 a_position; attribute vec2 a_texcoord0; uniform vec2 u_texelDelta; diff --git a/assets/shaders/5xBR.vsh b/assets/shaders/5xBR.vsh index b3649d47d0..01e85aab98 100644 --- a/assets/shaders/5xBR.vsh +++ b/assets/shaders/5xBR.vsh @@ -18,6 +18,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + attribute vec4 a_position; attribute vec2 a_texcoord0; varying vec2 v_texcoord0; diff --git a/assets/shaders/aacolor.vsh b/assets/shaders/aacolor.vsh index 85fecb4640..97199877e0 100644 --- a/assets/shaders/aacolor.vsh +++ b/assets/shaders/aacolor.vsh @@ -1,6 +1,11 @@ // by guest(r) - guest.r@gmail.com // license: GNU-GPL +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + attribute vec4 a_position; attribute vec2 a_texcoord0; uniform vec2 u_texelDelta; diff --git a/assets/shaders/cartoon.vsh b/assets/shaders/cartoon.vsh index 13a5b0ae3c..701ce8f74f 100644 --- a/assets/shaders/cartoon.vsh +++ b/assets/shaders/cartoon.vsh @@ -1,3 +1,8 @@ +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + attribute vec4 a_position; attribute vec2 a_texcoord0; uniform vec2 u_texelDelta; diff --git a/assets/shaders/fxaa.vsh b/assets/shaders/fxaa.vsh index 49295bb532..0c93df96f0 100644 --- a/assets/shaders/fxaa.vsh +++ b/assets/shaders/fxaa.vsh @@ -1,3 +1,8 @@ +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + attribute vec4 a_position; attribute vec2 a_texcoord0; varying vec2 v_texcoord0; diff --git a/assets/shaders/natural.vsh b/assets/shaders/natural.vsh index 34402d93de..cebb5f7c43 100644 --- a/assets/shaders/natural.vsh +++ b/assets/shaders/natural.vsh @@ -1,3 +1,8 @@ +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + uniform vec2 u_texelDelta; attribute vec4 a_position; diff --git a/assets/shaders/naturalA.vsh b/assets/shaders/naturalA.vsh index ebad6e2724..8e52bc055f 100644 --- a/assets/shaders/naturalA.vsh +++ b/assets/shaders/naturalA.vsh @@ -1,10 +1,15 @@ -attribute vec4 a_position; -attribute vec2 a_texcoord0; - -varying vec2 v_texcoord0; - -void main() -{ - gl_Position=a_position; - v_texcoord0=a_texcoord0; -} +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +attribute vec4 a_position; +attribute vec2 a_texcoord0; + +varying vec2 v_texcoord0; + +void main() +{ + gl_Position=a_position; + v_texcoord0=a_texcoord0; +} diff --git a/assets/shaders/upscale_spline36.vsh b/assets/shaders/upscale_spline36.vsh index 4b47d816cb..77d2f36f15 100644 --- a/assets/shaders/upscale_spline36.vsh +++ b/assets/shaders/upscale_spline36.vsh @@ -1,3 +1,8 @@ +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + attribute vec4 a_position; attribute vec2 a_texcoord0; diff --git a/assets/unknown.png b/assets/unknown.png index 1a233d9519..703da1ab70 100644 Binary files a/assets/unknown.png and b/assets/unknown.png differ diff --git a/assets/zip.png b/assets/zip.png index beb6822e5f..1ea89442fc 100644 Binary files a/assets/zip.png and b/assets/zip.png differ diff --git a/build.gradle b/build.gradle index c8274d0b58..c9644d8802 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.2' + classpath 'com.android.tools.build:gradle:4.1.3' } } diff --git a/ext/CMakeLists.txt b/ext/CMakeLists.txt index 16b4adbd7e..3597ca190e 100644 --- a/ext/CMakeLists.txt +++ b/ext/CMakeLists.txt @@ -7,6 +7,8 @@ endif() set(ENABLE_GLSLANG_BINARIES OFF CACHE BOOL "let's not build binaries we don't need" FORCE) set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS ON CACHE BOOL "let's not use exceptions" FORCE) +set(ENABLE_SPVREMAPPER OFF CACHE BOOL "we don't need spvremapper" FORCE) +set(ZSTD_BUILD_PROGRAMS OFF CACHE BOOL "we don't need zstd programs" FORCE) # This is really a workaround for an NDK 20 compiler issue (PPSSPP issue #12105), but shouldn't hurt. if(ANDROID) @@ -20,3 +22,4 @@ add_subdirectory(SPIRV-Cross-build) if(USE_DISCORD AND NOT IOS AND NOT LIBRETRO) add_subdirectory(discord-rpc-build) endif() +add_subdirectory(zstd/build/cmake EXCLUDE_FROM_ALL) diff --git a/ext/libpng17/arm/arm_init.c b/ext/libpng17/arm/arm_init.c index a672eaebb5..79702c9077 100644 --- a/ext/libpng17/arm/arm_init.c +++ b/ext/libpng17/arm/arm_init.c @@ -3,7 +3,7 @@ * * Copyright (c) 2014 Glenn Randers-Pehrson * Written by Mans Rullgard, 2011. - * Last changed in libpng 1.6.10 [(PENDING RELEASE)] + * Last changed in libpng 1.6.16 [December 22, 2014] * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer @@ -15,8 +15,10 @@ #define _POSIX_SOURCE 1 #include "../pngpriv.h" +#define PNG_SRC_FILE PNG_SRC_FILE_arm_arm_init #ifdef PNG_READ_SUPPORTED + #if PNG_ARM_NEON_OPT > 0 #ifdef PNG_ARM_NEON_CHECK_SUPPORTED /* Do run-time checks */ /* WARNING: it is strongly recommended that you do not build libpng with @@ -130,4 +132,4 @@ png_init_filter_functions_neon(png_structp pp, unsigned int bpp) } } #endif /* PNG_ARM_NEON_OPT > 0 */ -#endif /* PNG_READ_SUPPORTED */ +#endif /* READ */ diff --git a/ext/libpng17/arm/filter_neon.S b/ext/libpng17/arm/filter_neon.S index 40d97775d6..b7c0a90b43 100644 --- a/ext/libpng17/arm/filter_neon.S +++ b/ext/libpng17/arm/filter_neon.S @@ -1,18 +1,17 @@ /* filter_neon.S - NEON optimised filter functions * - * Copyright (c) 2013 Glenn Randers-Pehrson + * Copyright (c) 2014 Glenn Randers-Pehrson * Written by Mans Rullgard, 2011. - * Last changed in libpng 1.6.8 [December 19, 2013] + * Last changed in libpng 1.6.16 [(PENDING RELEASE)] * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h */ -/* This is required to get the symbol renames, which are #defines, and also - * includes the definition (or not) of PNG_ARM_NEON_OPT and - * PNG_ARM_NEON_IMPLEMENTATION. +/* This is required to get the symbol renames, which are #defines, and the + * definitions (or not) of PNG_ARM_NEON_OPT and PNG_ARM_NEON_IMPLEMENTATION. */ #define PNG_VERSION_INFO_ONLY #include "../pngpriv.h" @@ -21,6 +20,8 @@ .section .note.GNU-stack,"",%progbits /* mark stack as non-executable */ #endif +#ifdef PNG_READ_SUPPORTED + /* Assembler NEON support - only works for 32-bit ARM (i.e. it does not work for * ARM64). The code in arm/filter_neon_intrinsics.c supports ARM64, however it * only works if -mfpu=neon is specified on the GCC command line. See pngpriv.h @@ -28,7 +29,6 @@ */ #if PNG_ARM_NEON_IMPLEMENTATION == 2 /* hand-coded assembler */ -#ifdef PNG_READ_SUPPORTED #if PNG_ARM_NEON_OPT > 0 #ifdef __ELF__ @@ -47,6 +47,13 @@ ELF .size \name, . - \name .purgem endfunc .endm .text + + /* Explicitly specifying alignment here because some versions of + * GAS don't align code correctly. This is harmless in correctly + * written versions of GAS. + */ + .align 2 + .if \export .global \name .endif @@ -242,5 +249,5 @@ func png_read_filter_row_paeth3_neon, export=1 pop {r4,pc} endfunc #endif /* PNG_ARM_NEON_OPT > 0 */ -#endif /* PNG_READ_SUPPORTED */ #endif /* PNG_ARM_NEON_IMPLEMENTATION == 2 (assembler) */ +#endif /* READ */ diff --git a/ext/libpng17/arm/filter_neon_intrinsics.c b/ext/libpng17/arm/filter_neon_intrinsics.c index 09c2516a60..2788dd3391 100644 --- a/ext/libpng17/arm/filter_neon_intrinsics.c +++ b/ext/libpng17/arm/filter_neon_intrinsics.c @@ -1,11 +1,11 @@ /* filter_neon_intrinsics.c - NEON optimised filter functions * - * Copyright (c) 2013 Glenn Randers-Pehrson + * Copyright (c) 2014 Glenn Randers-Pehrson * Written by James Yu , October 2013. * Based on filter_neon.S, written by Mans Rullgard, 2011. * - * Last changed in libpng 1.6.8 [December 19, 2013] + * Last changed in libpng 1.6.16 [December 22, 2014] * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer @@ -13,6 +13,9 @@ */ #include "../pngpriv.h" +#define PNG_SRC_FILE PNG_SRC_FILE_arm_filter_neon_intrinsics + +#ifdef PNG_READ_SUPPORTED /* This code requires -mfpu=neon on the command line: */ #if PNG_ARM_NEON_IMPLEMENTATION == 1 /* intrinsics code from pngpriv.h */ @@ -39,15 +42,14 @@ #define png_ldr(type,pointer)\ (temp_pointer = png_ptr(type,pointer), *temp_pointer) -#ifdef PNG_READ_SUPPORTED #if PNG_ARM_NEON_OPT > 0 void -png_read_filter_row_up_neon(png_row_infop row_info, png_bytep row, - png_const_bytep prev_row) +png_read_filter_row_up_neon(png_alloc_size_t rowbytes, unsigned int bpp, + png_bytep row, png_const_bytep prev_row) { png_bytep rp = row; - png_bytep rp_stop = row + row_info->rowbytes; + png_bytep rp_stop = row + rowbytes; png_const_bytep pp = prev_row; for (; rp < rp_stop; rp += 16, pp += 16) @@ -59,14 +61,15 @@ png_read_filter_row_up_neon(png_row_infop row_info, png_bytep row, qrp = vaddq_u8(qrp, qpp); vst1q_u8(rp, qrp); } + + PNG_UNUSED(bpp) } void -png_read_filter_row_sub3_neon(png_row_infop row_info, png_bytep row, - png_const_bytep prev_row) +png_read_filter_row_sub3_neon(png_alloc_size_t rowbytes, unsigned int bpp, png_bytep row, png_const_bytep prev_row) { png_bytep rp = row; - png_bytep rp_stop = row + row_info->rowbytes; + png_bytep rp_stop = row + rowbytes; uint8x16_t vtmp = vld1q_u8(rp); uint8x8x2_t *vrpt = png_ptr(uint8x8x2_t, &vtmp); @@ -103,15 +106,16 @@ png_read_filter_row_sub3_neon(png_row_infop row_info, png_bytep row, rp += 3; } + PNG_UNUSED(bpp) PNG_UNUSED(prev_row) } void -png_read_filter_row_sub4_neon(png_row_infop row_info, png_bytep row, - png_const_bytep prev_row) +png_read_filter_row_sub4_neon(png_alloc_size_t rowbytes, unsigned int bpp, + png_bytep row, png_const_bytep prev_row) { png_bytep rp = row; - png_bytep rp_stop = row + row_info->rowbytes; + png_bytep rp_stop = row + rowbytes; uint8x8x4_t vdest; vdest.val[3] = vdup_n_u8(0); @@ -130,16 +134,17 @@ png_read_filter_row_sub4_neon(png_row_infop row_info, png_bytep row, vst4_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2x4_t,&vdest), 0); } + PNG_UNUSED(bpp) PNG_UNUSED(prev_row) } void -png_read_filter_row_avg3_neon(png_row_infop row_info, png_bytep row, - png_const_bytep prev_row) +png_read_filter_row_avg3_neon(png_alloc_size_t rowbytes, unsigned int bpp, + png_bytep row, png_const_bytep prev_row) { png_bytep rp = row; png_const_bytep pp = prev_row; - png_bytep rp_stop = row + row_info->rowbytes; + png_bytep rp_stop = row + rowbytes; uint8x16_t vtmp; uint8x8x2_t *vrpt; @@ -197,14 +202,16 @@ png_read_filter_row_avg3_neon(png_row_infop row_info, png_bytep row, vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0); rp += 3; } + + PNG_UNUSED(bpp) } void -png_read_filter_row_avg4_neon(png_row_infop row_info, png_bytep row, - png_const_bytep prev_row) +png_read_filter_row_avg4_neon(png_alloc_size_t rowbytes, unsigned int bpp, + png_bytep row, png_const_bytep prev_row) { png_bytep rp = row; - png_bytep rp_stop = row + row_info->rowbytes; + png_bytep rp_stop = row + rowbytes; png_const_bytep pp = prev_row; uint8x8x4_t vdest; @@ -235,6 +242,8 @@ png_read_filter_row_avg4_neon(png_row_infop row_info, png_bytep row, vst4_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2x4_t,&vdest), 0); } + + PNG_UNUSED(bpp) } static uint8x8_t @@ -265,12 +274,12 @@ paeth(uint8x8_t a, uint8x8_t b, uint8x8_t c) } void -png_read_filter_row_paeth3_neon(png_row_infop row_info, png_bytep row, - png_const_bytep prev_row) +png_read_filter_row_paeth3_neon(png_alloc_size_t rowbytes, unsigned int bpp, + png_bytep row, png_const_bytep prev_row) { png_bytep rp = row; png_const_bytep pp = prev_row; - png_bytep rp_stop = row + row_info->rowbytes; + png_bytep rp_stop = row + rowbytes; uint8x16_t vtmp; uint8x8x2_t *vrpt; @@ -328,14 +337,16 @@ png_read_filter_row_paeth3_neon(png_row_infop row_info, png_bytep row, vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0); rp += 3; } + + PNG_UNUSED(bpp) } void -png_read_filter_row_paeth4_neon(png_row_infop row_info, png_bytep row, - png_const_bytep prev_row) +png_read_filter_row_paeth4_neon(png_alloc_size_t rowbytes, unsigned int bpp, + png_bytep row, png_const_bytep prev_row) { png_bytep rp = row; - png_bytep rp_stop = row + row_info->rowbytes; + png_bytep rp_stop = row + rowbytes; png_const_bytep pp = prev_row; uint8x8_t vlast = vdup_n_u8(0); @@ -369,8 +380,9 @@ png_read_filter_row_paeth4_neon(png_row_infop row_info, png_bytep row, vst4_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2x4_t,&vdest), 0); } -} + PNG_UNUSED(bpp) +} #endif /* PNG_ARM_NEON_OPT > 0 */ -#endif /* PNG_READ_SUPPORTED */ #endif /* PNG_ARM_NEON_IMPLEMENTATION == 1 (intrinsics) */ +#endif /* READ */ diff --git a/ext/libpng17/arm/neon.h b/ext/libpng17/arm/neon.h new file mode 100644 index 0000000000..2485cd9bf1 --- /dev/null +++ b/ext/libpng17/arm/neon.h @@ -0,0 +1,124 @@ +/* Compile time options. + * ===================== + * In a multi-arch build the compiler may compile the code several times for the + * same object module, producing different binaries for different architectures. + * When this happens configure-time setting of the target host options cannot be + * done. An example is iOS vs Android compilation of ARM NEON support. + * + * NOTE: symbol prefixing does not pass $(CFLAGS) to the preprocessor, because + * this is not possible with certain compilers (Oracle SUN OS CC), as a result + * it is necessary to ensure that all extern functions that *might* be used + * regardless of $(CFLAGS) get declared in this file. The test on __ARM_NEON + * below is one example of this behavior because it is controlled by the + * presence or not of -mfpu=neon on the GCC command line, it is possible to do + * this in $(CC), e.g. "CC=gcc -mfpu=neon", but people who build libpng rarely + * do this. + */ +#ifndef PNG_ARM_NEON_OPT + /* ARM NEON optimizations are being controlled by the compiler settings, + * typically the target FPU. If the FPU supports NEON instructions then the + * compiler will define __ARM_NEON and we can rely unconditionally on NEON + * instructions not crashing, otherwise we must disable use of NEON + * instructions. + * + * NOTE: at present these optimizations depend on 'ALIGNED_MEMORY', so they + * can only be turned on automatically if that is supported too. If + * PNG_ARM_NEON_OPT is set in CPPFLAGS (to >0) then arm/arm_init.c will fail + * to compile with an appropriate #error if ALIGNED_MEMORY has been turned + * off. + * + * Note that older versions of GCC defined __ARM_NEON__; this is no longer + * supported. Also 32-bit ARM versions of GCC required the NEON FPU mode to + * be turned on explicitly on the command line. If this is not done (on + * 32-bit ARM) NEON code will not be included. + * + * To disable ARM_NEON optimizations entirely, and skip compiling the + * associated assembler code, pass --enable-arm-neon=no to configure + * or put -DPNG_ARM_NEON_OPT=0 in CPPFLAGS. + */ +# if defined(__ARM_NEON) && defined(PNG_ALIGNED_MEMORY_SUPPORTED) +# define PNG_ARM_NEON_OPT 2 +# else +# define PNG_ARM_NEON_OPT 0 +# endif +#endif + +#if PNG_ARM_NEON_OPT > 0 + /* NEON optimizations are to be at least considered by libpng, so enable the + * callbacks to do this. + */ +# define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_neon + + /* By default the 'intrinsics' code in arm/filter_neon_intrinsics.c is used + * if possible - if __ARM_NEON is set and the compiler version is not known + * to be broken. This is controlled by PNG_ARM_NEON_IMPLEMENTATION which can + * be: + * + * 1 The intrinsics code (the default with __ARM_NEON) + * 2 The hand coded assembler (the default without __ARM_NEON) + * + * It is possible to set PNG_ARM_NEON_IMPLEMENTATION in CPPFLAGS, however + * this is *NOT* supported and may cease to work even after a minor revision + * to libpng. It *is* valid to do this for testing purposes, e.g. speed + * testing or a new compiler, but the results should be communicated to the + * libpng implementation list for incorporation in the next minor release. + */ +# ifndef PNG_ARM_NEON_IMPLEMENTATION +# ifdef __ARM_NEON +# if defined(__clang__) + /* At present it is unknown by the libpng developers which versions + * of clang support the intrinsics, however some or perhaps all + * versions do not work with the assembler so this may be + * irrelevant, so just use the default (do nothing here.) + */ +# elif defined(__GNUC__) + /* GCC 4.5.4 NEON support is known to be broken. 4.6.3 is known to + * work, so if this *is* GCC, or G++, look for a version >4.5 + */ +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6) +# define PNG_ARM_NEON_IMPLEMENTATION 2 +# endif /* no GNUC support */ +# endif /* __GNUC__ */ +# else /* !defined __ARM_NEON */ + /* The 'intrinsics' code simply won't compile without compiler support + * and that support switches on __ARM_NEON, so use the assembler: + */ +# define PNG_ARM_NEON_IMPLEMENTATION 2 +# endif /* __ARM_NEON */ +# endif /* !defined PNG_ARM_NEON_IMPLEMENTATION */ + +# ifndef PNG_ARM_NEON_IMPLEMENTATION + /* Use the intrinsics code by default. */ +# define PNG_ARM_NEON_IMPLEMENTATION 1 +# endif +#endif /* PNG_ARM_NEON_OPT > 0 */ + +#ifdef PNG_SET_OPTION_SUPPORTED +#ifdef PNG_ARM_NEON_API_SUPPORTED +# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */ +#endif +#endif /* SET_OPTION */ + +#if PNG_ARM_NEON_OPT > 0 +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_neon, + (png_alloc_size_t row_bytes, unsigned int bpp, png_bytep row, + png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_neon, + (png_alloc_size_t row_bytes, unsigned int bpp, png_bytep row, + png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_neon, + (png_alloc_size_t row_bytes, unsigned int bpp, png_bytep row, + png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_neon, + (png_alloc_size_t row_bytes, unsigned int bpp, png_bytep row, + png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_neon, + (png_alloc_size_t row_bytes, unsigned int bpp, png_bytep row, + png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_neon, + (png_alloc_size_t row_bytes, unsigned int bpp, png_bytep row, + png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_neon, + (png_alloc_size_t row_bytes, unsigned int bpp, png_bytep row, + png_const_bytep prev_row),PNG_EMPTY); +#endif diff --git a/ext/libpng17/png.c b/ext/libpng17/png.c index 5ce74f8417..edf915a3fc 100644 --- a/ext/libpng17/png.c +++ b/ext/libpng17/png.c @@ -2,7 +2,7 @@ /* png.c - location for general purpose libpng functions * * Last changed in libpng 1.7.0 [(PENDING RELEASE)] - * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -12,9 +12,10 @@ */ #include "pngpriv.h" +#define PNG_SRC_FILE PNG_SRC_FILE_png /* Generate a compiler error if there is an old png.h in the search path. */ -typedef png_libpng_version_1_7_0beta35 Your_png_h_is_not_version_1_7_0beta35; +typedef png_libpng_version_1_7_0beta90 Your_png_h_is_not_version_1_7_0beta90; /* Tells libpng that we have already handled the first "num_bytes" bytes * of the PNG file signature. If the PNG data is embedded into another @@ -34,7 +35,7 @@ png_set_sig_bytes(png_structrp png_ptr, int num_bytes) if (num_bytes > 8) png_error(png_ptr, "Too many bytes for PNG signature"); - png_ptr->sig_bytes = (png_byte)(num_bytes < 0 ? 0 : num_bytes); + png_ptr->sig_bytes = png_check_byte(png_ptr, num_bytes < 0 ? 0 : num_bytes); } /* Checks whether the supplied bytes match the PNG signature. We allow @@ -65,7 +66,7 @@ png_sig_cmp(png_const_bytep sig, png_size_t start, png_size_t num_to_check) return ((int)(memcmp(&sig[start], &png_signature[start], num_to_check))); } -#endif /* PNG_READ_SUPPORTED */ +#endif /* READ */ #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) /* Function to allocate memory for zlib */ @@ -79,8 +80,8 @@ png_zalloc,(voidpf png_ptr, uInt items, uInt size),PNG_ALLOCATED) if (items >= (~(png_alloc_size_t)0)/size) { - png_warning (png_voidcast(png_structrp, png_ptr), - "Potential overflow in png_zalloc()"); + png_warning(png_voidcast(png_structrp, png_ptr), + "Potential overflow in png_zalloc()"); return NULL; } @@ -99,10 +100,26 @@ png_zfree(voidpf png_ptr, voidpf ptr) * in case CRC is > 32 bits to leave the top bits 0. */ void /* PRIVATE */ -png_reset_crc(png_structrp png_ptr) +png_reset_crc(png_structrp png_ptr, png_const_bytep chunk_tag) { - /* The cast is safe because the crc is a 32 bit value. */ - png_ptr->crc = (png_uint_32)crc32(0, Z_NULL, 0); +# ifdef PNG_READ_SUPPORTED + if (png_ptr->read_struct) + { + /* Set png_struct::current_crc appropriately. */ + if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name)) + png_ptr->current_crc = png_ptr->ancillary_crc; + + else /* critical */ + png_ptr->current_crc = png_ptr->critical_crc; + } + + else + png_ptr->current_crc = crc_error_quit; /* for write */ + + /* Now do not calculate the CRC if it isn't required: */ + if (png_ptr->current_crc != crc_quiet_use) +# endif /* READ */ + png_ptr->crc = 0xFFFFFFFFU & crc32(0, chunk_tag, 4); } /* Calculate the CRC over a section of data. We can only pass as @@ -111,111 +128,100 @@ png_reset_crc(png_structrp png_ptr) * trouble of calculating it. */ void /* PRIVATE */ -png_calculate_crc(png_structrp png_ptr, png_const_bytep ptr, png_size_t length) +png_calculate_crc(png_structrp png_ptr, png_const_voidp ptr, png_size_t length) { - int need_crc = 1; - - if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name)) - { - if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == - (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) - need_crc = 0; - } - - else /* critical */ - { - if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) - need_crc = 0; - } - /* 'uLong' is defined in zlib.h as unsigned long; this means that on some * systems it is a 64 bit value. crc32, however, returns 32 bits so the * following cast is safe. 'uInt' may be no more than 16 bits, so it is * necessary to perform a loop here. */ - if (need_crc && length > 0) +# ifdef PNG_READ_SUPPORTED + if (png_ptr->current_crc != crc_quiet_use) +# endif /* READ */ + if (length > 0) { uLong crc = png_ptr->crc; /* Should never issue a warning */ + const Bytef* rptr = png_voidcast(const Bytef*,ptr); do { - uInt safe_length = (uInt)length; - if (safe_length == 0) - safe_length = (uInt)-1; /* evil, but safe */ + uInt safe_length; - crc = crc32(crc, ptr, safe_length); + /* TODO: this uses ZLIB_IO_MAX which may be #defined to less than the + * maximum of a uInt, is this the best thing to do? + */ + if (length > ZLIB_IO_MAX) + safe_length = ZLIB_IO_MAX; + + else + safe_length = (uInt)/*SAFE*/length; + + crc = crc32(crc, PNGZ_INPUT_CAST(rptr), safe_length); /* The following should never issue compiler warnings; if they do the * target system has characteristics that will probably violate other * assumptions within the libpng code. */ - ptr += safe_length; + rptr += safe_length; length -= safe_length; } while (length > 0); /* And the following is always safe because the crc is only 32 bits. */ - png_ptr->crc = (png_uint_32)crc; + png_ptr->crc = 0xFFFFFFFFU & crc; } } /* Check a user supplied version number, called from both read and write * functions that create a png_struct. */ -int +static int png_user_version_check(png_structrp png_ptr, png_const_charp user_png_ver) { + /* Libpng versions 1.0.0 and later are binary compatible if the version + * string matches through the second '.'; we must recompile any applications + * that use any older library version. + */ if (user_png_ver != NULL) { - int i = 0; + int library_match = 1; + int i = -1; + unsigned int found_dots = 0; do { - if (user_png_ver[i] != png_libpng_ver[i]) - png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; - } while (png_libpng_ver[i++]); + i++; + if (user_png_ver[i] != PNG_LIBPNG_VER_STRING[i]) + library_match = 0; + if (user_png_ver[i] == '.') + found_dots++; + } while (library_match && found_dots < 2 && user_png_ver[i] != 0 && + PNG_LIBPNG_VER_STRING[i] != 0); + + if (library_match) + return 1; /* Library matches ok */ } - else - png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; - - if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) - { - /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so - * we must recompile any applications that use any older library version. - * For versions after libpng 1.0, we will be compatible, so we need - * only check the first and third digits (note that when we reach version - * 1.10 we will need to check the fourth symbol, namely user_png_ver[3]). - */ - if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] || - (user_png_ver[0] == '1' && (user_png_ver[2] != png_libpng_ver[2] || - user_png_ver[3] != png_libpng_ver[3])) || - (user_png_ver[0] == '0' && user_png_ver[2] < '9')) - { + /* Failure: mismatched library major version number */ #ifdef PNG_WARNINGS_SUPPORTED - size_t pos = 0; - char m[128]; + { + size_t pos = 0; + char m[128]; - pos = png_safecat(m, (sizeof m), pos, - "Application built with libpng-"); - pos = png_safecat(m, (sizeof m), pos, user_png_ver); - pos = png_safecat(m, (sizeof m), pos, " but running with "); - pos = png_safecat(m, (sizeof m), pos, png_libpng_ver); - PNG_UNUSED(pos) + pos = png_safecat(m, (sizeof m), pos, + "Application built with libpng-"); + /* This is ok if user_png_ver is NULL, it appends nothing: */ + pos = png_safecat(m, (sizeof m), pos, user_png_ver); + pos = png_safecat(m, (sizeof m), pos, " but running with "); + pos = png_safecat(m, (sizeof m), pos, PNG_LIBPNG_VER_STRING); + PNG_UNUSED(pos) - png_warning(png_ptr, m); -#endif - -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - png_ptr->flags = 0; -#endif - - return 0; - } + png_app_warning(png_ptr, m); } +#endif - /* Success return. */ - return 1; + return 0; /* Failure */ + PNG_UNUSED(png_ptr) /* if no warning */ } /* Generic function to create a png_struct for either read or write - this @@ -231,6 +237,19 @@ png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr, jmp_buf create_jmp_buf; # endif + /* This is a compile-type only test to ensure that the build satisifies the + * contraints for the row buffer stack allocations. A 'duplicate case + * statements' style of error means that one of the tests below failed: + */ + switch (0) + { + case 0: + case PNG_ROW_BUFFER_SIZE >= PNG_MIN_ROW_BUFFER_SIZE: /*1*/ + case 2*(PNG_ROW_BUFFER_SIZE <= PNG_MAX_ROW_BUFFER_SIZE): /*2*/ + default: + break; + } + /* This temporary stack-allocated structure is used to provide a place to * build enough context to allow the user provided memory allocator (if any) * to be called. @@ -274,7 +293,9 @@ png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr, # ifdef PNG_SETJMP_SUPPORTED if (!setjmp(create_jmp_buf)) +# endif { +# ifdef PNG_SETJMP_SUPPORTED /* Temporarily fake out the longjmp information until we have * successfully completed this function. This only works if we have * setjmp() support compiled in, but it is safe - this stuff should @@ -283,25 +304,16 @@ png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr, create_struct.jmp_buf_ptr = &create_jmp_buf; create_struct.jmp_buf_size = 0; /*stack allocation*/ create_struct.longjmp_fn = longjmp; -# else - { # endif /* Call the general version checker (shared with read and write code): */ - if (png_user_version_check(&create_struct, user_png_ver)) + if (png_user_version_check(&create_struct, user_png_ver) != 0) { png_structrp png_ptr = png_voidcast(png_structrp, - png_malloc_warn(&create_struct, (sizeof *png_ptr))); + png_malloc_warn(&create_struct, (sizeof *png_ptr))); if (png_ptr != NULL) { - /* png_ptr->zstream holds a back-pointer to the png_struct, so - * this can only be done now: - */ - create_struct.zstream.zalloc = png_zalloc; - create_struct.zstream.zfree = png_zfree; - create_struct.zstream.opaque = png_ptr; - # ifdef PNG_SETJMP_SUPPORTED /* Eliminate the local error handling: */ create_struct.jmp_buf_ptr = NULL; @@ -340,7 +352,7 @@ png_create_info_struct,(png_const_structrp png_ptr),PNG_ALLOCATED) * has always been done in 'example.c'. */ info_ptr = png_voidcast(png_inforp, png_malloc_base(png_ptr, - (sizeof *info_ptr))); + (sizeof *info_ptr))); if (info_ptr != NULL) memset(info_ptr, 0, (sizeof *info_ptr)); @@ -387,7 +399,7 @@ png_destroy_info_struct(png_const_structrp png_ptr, png_infopp info_ptr_ptr) void PNGAPI png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask, - int num) + int num) { png_debug(1, "in png_free_data"); @@ -396,34 +408,35 @@ png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask, #ifdef PNG_TEXT_SUPPORTED /* Free text item num or (if num == -1) all text items */ - if ((mask & PNG_FREE_TEXT) & info_ptr->free_me) + if (info_ptr->text != 0 && + ((mask & PNG_FREE_TEXT) & info_ptr->free_me) != 0) { if (num != -1) { - if (info_ptr->text && info_ptr->text[num].key) - { - png_free(png_ptr, info_ptr->text[num].key); - info_ptr->text[num].key = NULL; - } + png_free(png_ptr, info_ptr->text[num].key); + info_ptr->text[num].key = NULL; } else { int i; + for (i = 0; i < info_ptr->num_text; i++) - png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, i); + png_free(png_ptr, info_ptr->text[i].key); + png_free(png_ptr, info_ptr->text); info_ptr->text = NULL; - info_ptr->num_text=0; + info_ptr->num_text = 0; + info_ptr->max_text = 0; } } #endif #ifdef PNG_tRNS_SUPPORTED /* Free any tRNS entry */ - if ((mask & PNG_FREE_TRNS) & info_ptr->free_me) + if (((mask & PNG_FREE_TRNS) & info_ptr->free_me) != 0) { - info_ptr->valid &= ~PNG_INFO_tRNS; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_tRNS); png_free(png_ptr, info_ptr->trans_alpha); info_ptr->trans_alpha = NULL; info_ptr->num_trans = 0; @@ -432,156 +445,148 @@ png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask, #ifdef PNG_sCAL_SUPPORTED /* Free any sCAL entry */ - if ((mask & PNG_FREE_SCAL) & info_ptr->free_me) + if (((mask & PNG_FREE_SCAL) & info_ptr->free_me) != 0) { png_free(png_ptr, info_ptr->scal_s_width); png_free(png_ptr, info_ptr->scal_s_height); info_ptr->scal_s_width = NULL; info_ptr->scal_s_height = NULL; - info_ptr->valid &= ~PNG_INFO_sCAL; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_sCAL); } #endif #ifdef PNG_pCAL_SUPPORTED /* Free any pCAL entry */ - if ((mask & PNG_FREE_PCAL) & info_ptr->free_me) + if (((mask & PNG_FREE_PCAL) & info_ptr->free_me) != 0) { png_free(png_ptr, info_ptr->pcal_purpose); png_free(png_ptr, info_ptr->pcal_units); info_ptr->pcal_purpose = NULL; info_ptr->pcal_units = NULL; + if (info_ptr->pcal_params != NULL) { - unsigned int i; + int i; + for (i = 0; i < info_ptr->pcal_nparams; i++) - { png_free(png_ptr, info_ptr->pcal_params[i]); - info_ptr->pcal_params[i] = NULL; - } + png_free(png_ptr, info_ptr->pcal_params); info_ptr->pcal_params = NULL; } - info_ptr->valid &= ~PNG_INFO_pCAL; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_pCAL); } #endif #ifdef PNG_iCCP_SUPPORTED /* Free any profile entry */ - if ((mask & PNG_FREE_ICCP) & info_ptr->free_me) + if (((mask & PNG_FREE_ICCP) & info_ptr->free_me) != 0) { png_free(png_ptr, info_ptr->iccp_name); png_free(png_ptr, info_ptr->iccp_profile); info_ptr->iccp_name = NULL; info_ptr->iccp_profile = NULL; - info_ptr->valid &= ~PNG_INFO_iCCP; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_iCCP); } #endif #ifdef PNG_sPLT_SUPPORTED /* Free a given sPLT entry, or (if num == -1) all sPLT entries */ - if ((mask & PNG_FREE_SPLT) & info_ptr->free_me) + if (info_ptr->splt_palettes != 0 && + ((mask & PNG_FREE_SPLT) & info_ptr->free_me) != 0) { if (num != -1) { - if (info_ptr->splt_palettes) - { - png_free(png_ptr, info_ptr->splt_palettes[num].name); - png_free(png_ptr, info_ptr->splt_palettes[num].entries); - info_ptr->splt_palettes[num].name = NULL; - info_ptr->splt_palettes[num].entries = NULL; - } - } - - else - { - if (info_ptr->splt_palettes_num) - { - int i; - for (i = 0; i < info_ptr->splt_palettes_num; i++) - png_free_data(png_ptr, info_ptr, PNG_FREE_SPLT, (int)i); - - png_free(png_ptr, info_ptr->splt_palettes); - info_ptr->splt_palettes = NULL; - info_ptr->splt_palettes_num = 0; - } - info_ptr->valid &= ~PNG_INFO_sPLT; - } - } -#endif - -#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED - if ((mask & PNG_FREE_UNKN) & info_ptr->free_me) - { - if (num != -1) - { - if (info_ptr->unknown_chunks) - { - png_free(png_ptr, info_ptr->unknown_chunks[num].data); - info_ptr->unknown_chunks[num].data = NULL; - } + png_free(png_ptr, info_ptr->splt_palettes[num].name); + png_free(png_ptr, info_ptr->splt_palettes[num].entries); + info_ptr->splt_palettes[num].name = NULL; + info_ptr->splt_palettes[num].entries = NULL; } else { int i; - if (info_ptr->unknown_chunks_num) + for (i = 0; i < info_ptr->splt_palettes_num; i++) { - for (i = 0; i < info_ptr->unknown_chunks_num; i++) - png_free_data(png_ptr, info_ptr, PNG_FREE_UNKN, (int)i); - - png_free(png_ptr, info_ptr->unknown_chunks); - info_ptr->unknown_chunks = NULL; - info_ptr->unknown_chunks_num = 0; + png_free(png_ptr, info_ptr->splt_palettes[i].name); + png_free(png_ptr, info_ptr->splt_palettes[i].entries); } + + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes = NULL; + info_ptr->splt_palettes_num = 0; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_sPLT); + } + } +#endif + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED + if (info_ptr->unknown_chunks != 0 && + ((mask & PNG_FREE_UNKN) & info_ptr->free_me) != 0) + { + if (num != -1) + { + png_free(png_ptr, info_ptr->unknown_chunks[num].data); + info_ptr->unknown_chunks[num].data = NULL; + } + + else + { + int i; + + for (i = 0; i < info_ptr->unknown_chunks_num; i++) + png_free(png_ptr, info_ptr->unknown_chunks[i].data); + + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks = NULL; + info_ptr->unknown_chunks_num = 0; } } #endif #ifdef PNG_hIST_SUPPORTED /* Free any hIST entry */ - if ((mask & PNG_FREE_HIST) & info_ptr->free_me) + if (((mask & PNG_FREE_HIST) & info_ptr->free_me) != 0) { png_free(png_ptr, info_ptr->hist); info_ptr->hist = NULL; - info_ptr->valid &= ~PNG_INFO_hIST; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_hIST); } #endif /* Free any PLTE entry that was internally allocated */ - if ((mask & PNG_FREE_PLTE) & info_ptr->free_me) + if (((mask & PNG_FREE_PLTE) & info_ptr->free_me) != 0) { png_free(png_ptr, info_ptr->palette); info_ptr->palette = NULL; - info_ptr->valid &= ~PNG_INFO_PLTE; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_PLTE); info_ptr->num_palette = 0; } #ifdef PNG_INFO_IMAGE_SUPPORTED /* Free any image bits attached to the info structure */ - if ((mask & PNG_FREE_ROWS) & info_ptr->free_me) + if (((mask & PNG_FREE_ROWS) & info_ptr->free_me) != 0) { - if (info_ptr->row_pointers) + if (info_ptr->row_pointers != 0) { png_uint_32 row; for (row = 0; row < info_ptr->height; row++) - { png_free(png_ptr, info_ptr->row_pointers[row]); - info_ptr->row_pointers[row] = NULL; - } + png_free(png_ptr, info_ptr->row_pointers); info_ptr->row_pointers = NULL; } - info_ptr->valid &= ~PNG_INFO_IDAT; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_IDAT); } #endif if (num != -1) - mask &= ~PNG_FREE_MUL; + mask &= PNG_BIC_MASK(PNG_FREE_MUL); info_ptr->free_me &= ~mask; } -#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ +#endif /* READ || WRITE */ /* This function returns a pointer to the io_ptr associated with the user * functions. The application should free any memory associated with this @@ -612,26 +617,30 @@ png_init_io(png_structrp png_ptr, png_FILE_p fp) if (png_ptr == NULL) return; - png_ptr->io_ptr = (png_voidp)fp; -} -# endif + if (png_ptr->rw_data_fn == NULL) + { +# ifdef PNG_READ_SUPPORTED + if (png_ptr->read_struct) + png_set_read_fn(png_ptr, fp, png_default_read_data); +# ifdef PNG_WRITE_SUPPORTED + else +# endif /* WRITE */ +# endif /* READ */ +# ifdef PNG_WRITE_SUPPORTED + if (!png_ptr->read_struct) +# ifdef PNG_WRITE_FLUSH_SUPPORTED + png_set_write_fn(png_ptr, fp, png_default_write_data, + png_default_flush); +# else + png_set_write_fn(png_ptr, fp, png_default_write_data, NULL); +# endif +# endif /* WRITE */ + } -#ifdef PNG_SAVE_INT_32_SUPPORTED -/* The png_save_int_32 function assumes integers are stored in two's - * complement format. If this isn't the case, then this routine needs to - * be modified to write data in two's complement format. Note that, - * the following works correctly even if png_int_32 has more than 32 bits - * (compare the more complex code required on read for sign extension.) - */ -void PNGAPI -png_save_int_32(png_bytep buf, png_int_32 i) -{ - buf[0] = (png_byte)((i >> 24) & 0xff); - buf[1] = (png_byte)((i >> 16) & 0xff); - buf[2] = (png_byte)((i >> 8) & 0xff); - buf[3] = (png_byte)(i & 0xff); + else + png_ptr->io_ptr = fp; } -#endif +# endif /* STDIO */ # ifdef PNG_TIME_RFC1123_SUPPORTED /* Convert the supplied time into an RFC 1123 string suitable for use in @@ -683,9 +692,9 @@ png_convert_to_rfc1123_buffer(char out[29], png_const_timep ptime) return 1; } -# endif /* PNG_TIME_RFC1123_SUPPORTED */ +# endif /* TIME_RFC1123 */ -#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ +#endif /* READ || WRITE */ png_const_charp PNGAPI png_get_copyright(png_const_structrp png_ptr) @@ -696,14 +705,15 @@ png_get_copyright(png_const_structrp png_ptr) #else # ifdef __STDC__ return PNG_STRING_NEWLINE \ - "libpng version 1.7.0beta35 - March 22, 2014" PNG_STRING_NEWLINE \ - "Copyright (c) 1998-2014 Glenn Randers-Pehrson" PNG_STRING_NEWLINE \ + "libpng version 1.7.0beta90 - August 28, 2017" PNG_STRING_NEWLINE \ + "Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson" \ + PNG_STRING_NEWLINE \ "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \ "Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \ PNG_STRING_NEWLINE; # else - return "libpng version 1.7.0beta35 - March 22, 2014\ - Copyright (c) 1998-2014 Glenn Randers-Pehrson\ + return "libpng version 1.7.0beta90 - August 28, 2017\ + Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson\ Copyright (c) 1996-1997 Andreas Dilger\ Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc."; # endif @@ -739,11 +749,11 @@ png_get_header_version(png_const_structrp png_ptr) /* Returns longer string containing both version and date */ PNG_UNUSED(png_ptr) /* Silence compiler warning about unused png_ptr */ #ifdef __STDC__ - return PNG_HEADER_VERSION_STRING # ifndef PNG_READ_SUPPORTED - " (NO READ SUPPORT)" + return PNG_HEADER_VERSION_STRING " (NO READ SUPPORT)" PNG_STRING_NEWLINE; +# else + return PNG_HEADER_VERSION_STRING PNG_STRING_NEWLINE; # endif - PNG_STRING_NEWLINE; #else return PNG_HEADER_VERSION_STRING; #endif @@ -760,9 +770,9 @@ void PNGAPI png_build_grayscale_palette(int bit_depth, png_colorp palette) { int num_palette; - int color_inc; + png_byte color_inc; int i; - int v; + png_byte v; png_debug(1, "in png_do_build_grayscale_palette"); @@ -797,12 +807,8 @@ png_build_grayscale_palette(int bit_depth, png_colorp palette) break; } - for (i = 0, v = 0; i < num_palette; i++, v += color_inc) - { - palette[i].red = (png_byte)v; - palette[i].green = (png_byte)v; - palette[i].blue = (png_byte)v; - } + for (i = 0, v = 0; i < num_palette; ++i, v = PNG_BYTE(v+color_inc)) + palette[i].red = palette[i].green = palette[i].blue = v; } #endif @@ -827,7 +833,7 @@ png_handle_as_unknown(png_const_structrp png_ptr, png_const_bytep chunk_name) { p -= 5; - if (!memcmp(chunk_name, p, 4)) + if (memcmp(chunk_name, p, 4) == 0) return p[4]; } while (p > p_end); @@ -839,18 +845,6 @@ png_handle_as_unknown(png_const_structrp png_ptr, png_const_bytep chunk_name) */ return PNG_HANDLE_CHUNK_AS_DEFAULT; } - -#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) ||\ - defined(PNG_HANDLE_AS_UNKNOWN_SUPPORTED) -int /* PRIVATE */ -png_chunk_unknown_handling(png_const_structrp png_ptr, png_uint_32 chunk_name) -{ - png_byte chunk_string[5]; - - PNG_CSTRING_FROM_CHUNK(chunk_string, chunk_name); - return png_handle_as_unknown(png_ptr, chunk_string); -} -#endif /* READ_UNKNOWN_CHUNKS || HANDLE_AS_UNKNOWN */ #endif /* SET_UNKNOWN_CHUNKS */ /* This function was added to libpng-1.0.7 */ @@ -867,58 +861,58 @@ png_access_version_number(void) * like Z_OK or Z_STREAM_END where the error code is apparently a success code. */ void /* PRIVATE */ -png_zstream_error(png_structrp png_ptr, int ret) +png_zstream_error(z_stream *zstream, int ret) { /* Translate 'ret' into an appropriate error string, priority is given to the * one in zstream if set. This always returns a string, even in cases like * Z_OK or Z_STREAM_END where the error code is a success code. */ - if (png_ptr->zstream.msg == NULL) switch (ret) + if (zstream->msg == NULL) switch (ret) { default: case Z_OK: - png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return code"); + zstream->msg = PNGZ_MSG_CAST("unexpected zlib return code"); break; case Z_STREAM_END: /* Normal exit */ - png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected end of LZ stream"); + zstream->msg = PNGZ_MSG_CAST("unexpected end of LZ stream"); break; case Z_NEED_DICT: /* This means the deflate stream did not have a dictionary; this * indicates a bogus PNG. */ - png_ptr->zstream.msg = PNGZ_MSG_CAST("missing LZ dictionary"); + zstream->msg = PNGZ_MSG_CAST("missing LZ dictionary"); break; case Z_ERRNO: /* gz APIs only: should not happen */ - png_ptr->zstream.msg = PNGZ_MSG_CAST("zlib IO error"); + zstream->msg = PNGZ_MSG_CAST("zlib IO error"); break; case Z_STREAM_ERROR: /* internal libpng error */ - png_ptr->zstream.msg = PNGZ_MSG_CAST("bad parameters to zlib"); + zstream->msg = PNGZ_MSG_CAST("bad parameters to zlib"); break; case Z_DATA_ERROR: - png_ptr->zstream.msg = PNGZ_MSG_CAST("damaged LZ stream"); + zstream->msg = PNGZ_MSG_CAST("damaged LZ stream"); break; case Z_MEM_ERROR: - png_ptr->zstream.msg = PNGZ_MSG_CAST("insufficient memory"); + zstream->msg = PNGZ_MSG_CAST("insufficient memory"); break; case Z_BUF_ERROR: /* End of input or output; not a problem if the caller is doing * incremental read or write. */ - png_ptr->zstream.msg = PNGZ_MSG_CAST("truncated"); + zstream->msg = PNGZ_MSG_CAST("truncated"); break; case Z_VERSION_ERROR: - png_ptr->zstream.msg = PNGZ_MSG_CAST("unsupported zlib version"); + zstream->msg = PNGZ_MSG_CAST("unsupported zlib version"); break; case PNG_UNEXPECTED_ZLIB_RETURN: @@ -927,7 +921,7 @@ png_zstream_error(png_structrp png_ptr, int ret) * and change pngpriv.h. Note that this message is "... return", * whereas the default/Z_OK one is "... return code". */ - png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return"); + zstream->msg = PNGZ_MSG_CAST("unexpected zlib return"); break; } } @@ -940,7 +934,7 @@ png_zstream_error(png_structrp png_ptr, int ret) #ifdef PNG_GAMMA_SUPPORTED /* always set if COLORSPACE */ static int png_colorspace_check_gamma(png_const_structrp png_ptr, - png_colorspacerp colorspace, png_fixed_point gAMA, int from) + png_colorspacerp colorspace, png_fixed_point gAMA, int from) /* This is called to check a new gamma value against an existing one. The * routine returns false if the new gamma value should not be written. * @@ -949,13 +943,19 @@ png_colorspace_check_gamma(png_const_structrp png_ptr, * 0: the new gamma value is the libpng estimate for an ICC profile * 1: the new gamma value comes from a gAMA chunk * 2: the new gamma value comes from an sRGB chunk + * + * API CHANGE: libpng 1.7.0: prior to 1.7 the check below used the build-time + * constant PNG_GAMMA_THRESHOLD_FIXED and the results would therefore depend + * on a parameter that was intended for tuning the READ_GAMMA support. In + * 1.7 a fixed value of +/-1% is used instead; this reflects the fact that + * gamma values are rarely quoted to more than 2 decimal digits of precision. */ { png_fixed_point gtest; if ((colorspace->flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 && - (!png_muldiv(>est, colorspace->gamma, PNG_FP_1, gAMA) || - png_gamma_significant(gtest))) + (png_muldiv(>est, colorspace->gamma, PNG_FP_1, gAMA) == 0 || + gtest < PNG_FP_1 - 1000 || gtest > PNG_FP_1 + 1000)) { /* Either this is an sRGB image, in which case the calculated gamma * approximation should match, or this is an image with a profile and the @@ -966,7 +966,7 @@ png_colorspace_check_gamma(png_const_structrp png_ptr, if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0 || from == 2) { png_chunk_report(png_ptr, "gamma value does not match sRGB", - PNG_CHUNK_ERROR); + PNG_CHUNK_ERROR); /* Do not overwrite an sRGB value */ return from == 2; } @@ -974,7 +974,7 @@ png_colorspace_check_gamma(png_const_structrp png_ptr, else /* sRGB tag not involved */ { png_chunk_report(png_ptr, "gamma value does not match libpng estimate", - PNG_CHUNK_WARNING); + PNG_CHUNK_WARNING); return from == 1; } } @@ -984,10 +984,10 @@ png_colorspace_check_gamma(png_const_structrp png_ptr, void /* PRIVATE */ png_colorspace_set_gamma(png_const_structrp png_ptr, - png_colorspacerp colorspace, png_fixed_point gAMA) + png_colorspacerp colorspace, png_fixed_point gAMA) { /* Changed in libpng-1.5.4 to limit the values to ensure overflow can't - * occur. Since the fixed point representation is assymetrical it is + * occur. Since the fixed point representation is asymmetrical it is * possible for 1/gamma to overflow the limit of 21474 and this means the * gamma value must be at least 5/100000 and hence at most 20000.0. For * safety the limits here are a little narrower. The values are 0.00016 to @@ -997,25 +997,43 @@ png_colorspace_set_gamma(png_const_structrp png_ptr, * In 1.6.0 this test replaces the ones in pngrutil.c, in the gAMA chunk * handling code, which only required the value to be >0. */ - png_const_charp errmsg; +# if (defined PNG_TRANSFORM_MECH_SUPPORTED) &&\ + (defined PNG_ERROR_TEXT_SUPPORTED) +# define ERRMSG 1 +# else +# define ERRMSG 0 +# endif + +# if ERRMSG + png_const_charp errmsg; +# endif if (gAMA < 16 || gAMA > 625000000) - errmsg = "gamma value out of range"; + { +# if ERRMSG + errmsg = "gamma value out of range"; +# endif + } # ifdef PNG_READ_gAMA_SUPPORTED /* Allow the application to set the gamma value more than once */ - else if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 && + else if (png_ptr->read_struct && (colorspace->flags & PNG_COLORSPACE_FROM_gAMA) != 0) + { +# if ERRMSG errmsg = "duplicate"; +# endif + } # endif /* Do nothing if the colorspace is already invalid */ - else if (colorspace->flags & PNG_COLORSPACE_INVALID) + else if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0) return; else { - if (png_colorspace_check_gamma(png_ptr, colorspace, gAMA, 1/*from gAMA*/)) + if (png_colorspace_check_gamma(png_ptr, colorspace, gAMA, + 1/*from gAMA*/) != 0) { /* Store this gamma value. */ colorspace->gamma = gAMA; @@ -1032,6 +1050,7 @@ png_colorspace_set_gamma(png_const_structrp png_ptr, } /* Error exit - errmsg has been set. */ +# undef ERRMSG colorspace->flags |= PNG_COLORSPACE_INVALID; png_chunk_report(png_ptr, errmsg, PNG_CHUNK_WRITE_ERROR); } @@ -1039,10 +1058,10 @@ png_colorspace_set_gamma(png_const_structrp png_ptr, void /* PRIVATE */ png_colorspace_sync_info(png_const_structrp png_ptr, png_inforp info_ptr) { - if (info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) + if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0) { /* Everything is invalid */ - info_ptr->valid &= ~(PNG_INFO_gAMA|PNG_INFO_cHRM|PNG_INFO_sRGB| + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_gAMA|PNG_INFO_cHRM|PNG_INFO_sRGB| PNG_INFO_iCCP); # ifdef PNG_COLORSPACE_SUPPORTED @@ -1060,24 +1079,24 @@ png_colorspace_sync_info(png_const_structrp png_ptr, png_inforp info_ptr) * it; this allows a PNG to contain a profile which matches sRGB and * yet still have that profile retrievable by the application. */ - if (info_ptr->colorspace.flags & PNG_COLORSPACE_MATCHES_sRGB) + if ((info_ptr->colorspace.flags & PNG_COLORSPACE_MATCHES_sRGB) != 0) info_ptr->valid |= PNG_INFO_sRGB; else - info_ptr->valid &= ~PNG_INFO_sRGB; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_sRGB); - if (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) + if ((info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) info_ptr->valid |= PNG_INFO_cHRM; else - info_ptr->valid &= ~PNG_INFO_cHRM; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_cHRM); # endif - if (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) + if ((info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0) info_ptr->valid |= PNG_INFO_gAMA; else - info_ptr->valid &= ~PNG_INFO_gAMA; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_gAMA); } } @@ -1095,6 +1114,25 @@ png_colorspace_sync(png_const_structrp png_ptr, png_inforp info_ptr) #endif #ifdef PNG_COLORSPACE_SUPPORTED +/* Calculate a reciprocal, return 0 on div-by-zero or overflow. */ +static png_fixed_point +png_reciprocal(png_fixed_point a) +{ +#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + double r = floor(1E10/a+.5); + + if (r <= 2147483647. && r >= -2147483648.) + return (png_fixed_point)r; +#else + png_fixed_point res; + + if (png_muldiv(&res, PNG_FP_1, PNG_FP_1, a) != 0) + return res; +#endif + + return 0; /* error/overflow */ +} + /* Added at libpng-1.5.5 to support read and write of true CIEXYZ values for * cHRM, as opposed to using chromaticities. These internal APIs return * non-zero on a parameter error. The X, Y and Z values are required to be @@ -1106,22 +1144,28 @@ png_xy_from_XYZ(png_xy *xy, const png_XYZ *XYZ) png_int_32 d, dwhite, whiteX, whiteY; d = XYZ->red_X + XYZ->red_Y + XYZ->red_Z; - if (!png_muldiv(&xy->redx, XYZ->red_X, PNG_FP_1, d)) return 1; - if (!png_muldiv(&xy->redy, XYZ->red_Y, PNG_FP_1, d)) return 1; + if (png_muldiv(&xy->redx, XYZ->red_X, PNG_FP_1, d) == 0) + return 1; + if (png_muldiv(&xy->redy, XYZ->red_Y, PNG_FP_1, d) == 0) + return 1; dwhite = d; whiteX = XYZ->red_X; whiteY = XYZ->red_Y; d = XYZ->green_X + XYZ->green_Y + XYZ->green_Z; - if (!png_muldiv(&xy->greenx, XYZ->green_X, PNG_FP_1, d)) return 1; - if (!png_muldiv(&xy->greeny, XYZ->green_Y, PNG_FP_1, d)) return 1; + if (png_muldiv(&xy->greenx, XYZ->green_X, PNG_FP_1, d) == 0) + return 1; + if (png_muldiv(&xy->greeny, XYZ->green_Y, PNG_FP_1, d) == 0) + return 1; dwhite += d; whiteX += XYZ->green_X; whiteY += XYZ->green_Y; d = XYZ->blue_X + XYZ->blue_Y + XYZ->blue_Z; - if (!png_muldiv(&xy->bluex, XYZ->blue_X, PNG_FP_1, d)) return 1; - if (!png_muldiv(&xy->bluey, XYZ->blue_Y, PNG_FP_1, d)) return 1; + if (png_muldiv(&xy->bluex, XYZ->blue_X, PNG_FP_1, d) == 0) + return 1; + if (png_muldiv(&xy->bluey, XYZ->blue_Y, PNG_FP_1, d) == 0) + return 1; dwhite += d; whiteX += XYZ->blue_X; whiteY += XYZ->blue_Y; @@ -1129,8 +1173,10 @@ png_xy_from_XYZ(png_xy *xy, const png_XYZ *XYZ) /* The reference white is simply the sum of the end-point (X,Y,Z) vectors, * thus: */ - if (!png_muldiv(&xy->whitex, whiteX, PNG_FP_1, dwhite)) return 1; - if (!png_muldiv(&xy->whitey, whiteY, PNG_FP_1, dwhite)) return 1; + if (png_muldiv(&xy->whitex, whiteX, PNG_FP_1, dwhite) == 0) + return 1; + if (png_muldiv(&xy->whitey, whiteY, PNG_FP_1, dwhite) == 0) + return 1; return 0; } @@ -1143,16 +1189,17 @@ png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy) /* Check xy and, implicitly, z. Note that wide gamut color spaces typically * have end points with 0 tristimulus values (these are impossible end - * points, but they are used to cover the possible colors.) + * points, but they are used to cover the possible colors). We check + * xy->whitey against 5, not 0, to avoid a possible integer overflow. */ - if (xy->redx < 0 || xy->redx > PNG_FP_1) return 1; - if (xy->redy < 0 || xy->redy > PNG_FP_1-xy->redx) return 1; + if (xy->redx < 0 || xy->redx > PNG_FP_1) return 1; + if (xy->redy < 0 || xy->redy > PNG_FP_1-xy->redx) return 1; if (xy->greenx < 0 || xy->greenx > PNG_FP_1) return 1; if (xy->greeny < 0 || xy->greeny > PNG_FP_1-xy->greenx) return 1; - if (xy->bluex < 0 || xy->bluex > PNG_FP_1) return 1; - if (xy->bluey < 0 || xy->bluey > PNG_FP_1-xy->bluex) return 1; + if (xy->bluex < 0 || xy->bluex > PNG_FP_1) return 1; + if (xy->bluey < 0 || xy->bluey > PNG_FP_1-xy->bluex) return 1; if (xy->whitex < 0 || xy->whitex > PNG_FP_1) return 1; - if (xy->whitey < 0 || xy->whitey > PNG_FP_1-xy->whitex) return 1; + if (xy->whitey < 5 || xy->whitey > PNG_FP_1-xy->whitex) return 1; /* The reverse calculation is more difficult because the original tristimulus * value had 9 independent values (red,green,blue)x(X,Y,Z) however only 8 @@ -1227,7 +1274,7 @@ png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy) * (1/white-y), so we can immediately see that as white-y approaches 0 the * accuracy inherent in the cHRM chunk drops off substantially. * - * libpng arithmetic: a simple invertion of the above equations + * libpng arithmetic: a simple inversion of the above equations * ------------------------------------------------------------ * * white_scale = 1/white-y @@ -1333,16 +1380,16 @@ png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy) /* By the argument, above overflow should be impossible here. The return * value of 2 indicates an internal error to the caller. */ - if (!png_muldiv(&left, xy->greenx-xy->bluex, xy->redy - xy->bluey, 7)) + if (png_muldiv(&left, xy->greenx-xy->bluex, xy->redy - xy->bluey, 7) == 0) return 2; - if (!png_muldiv(&right, xy->greeny-xy->bluey, xy->redx - xy->bluex, 7)) + if (png_muldiv(&right, xy->greeny-xy->bluey, xy->redx - xy->bluex, 7) == 0) return 2; denominator = left - right; /* Now find the red numerator. */ - if (!png_muldiv(&left, xy->greenx-xy->bluex, xy->whitey-xy->bluey, 7)) + if (png_muldiv(&left, xy->greenx-xy->bluex, xy->whitey-xy->bluey, 7) == 0) return 2; - if (!png_muldiv(&right, xy->greeny-xy->bluey, xy->whitex-xy->bluex, 7)) + if (png_muldiv(&right, xy->greeny-xy->bluey, xy->whitex-xy->bluex, 7) == 0) return 2; /* Overflow is possible here and it indicates an extreme set of PNG cHRM @@ -1350,16 +1397,16 @@ png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy) * scale value because this allows us to delay the multiplication of white-y * into the denominator, which tends to produce a small number. */ - if (!png_muldiv(&red_inverse, xy->whitey, denominator, left-right) || + if (png_muldiv(&red_inverse, xy->whitey, denominator, left-right) == 0 || red_inverse <= xy->whitey /* r+g+b scales = white scale */) return 1; /* Similarly for green_inverse: */ - if (!png_muldiv(&left, xy->redy-xy->bluey, xy->whitex-xy->bluex, 7)) + if (png_muldiv(&left, xy->redy-xy->bluey, xy->whitex-xy->bluex, 7) == 0) return 2; - if (!png_muldiv(&right, xy->redx-xy->bluex, xy->whitey-xy->bluey, 7)) + if (png_muldiv(&right, xy->redx-xy->bluex, xy->whitey-xy->bluey, 7) == 0) return 2; - if (!png_muldiv(&green_inverse, xy->whitey, denominator, left-right) || + if (png_muldiv(&green_inverse, xy->whitey, denominator, left-right) == 0 || green_inverse <= xy->whitey) return 1; @@ -1367,29 +1414,34 @@ png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy) * can still produce 0 for extreme cHRM values. */ blue_scale = png_reciprocal(xy->whitey) - png_reciprocal(red_inverse) - - png_reciprocal(green_inverse); - if (blue_scale <= 0) return 1; + png_reciprocal(green_inverse); + if (blue_scale <= 0) + return 1; /* And fill in the png_XYZ: */ - if (!png_muldiv(&XYZ->red_X, xy->redx, PNG_FP_1, red_inverse)) return 1; - if (!png_muldiv(&XYZ->red_Y, xy->redy, PNG_FP_1, red_inverse)) return 1; - if (!png_muldiv(&XYZ->red_Z, PNG_FP_1 - xy->redx - xy->redy, PNG_FP_1, - red_inverse)) + if (png_muldiv(&XYZ->red_X, xy->redx, PNG_FP_1, red_inverse) == 0) + return 1; + if (png_muldiv(&XYZ->red_Y, xy->redy, PNG_FP_1, red_inverse) == 0) + return 1; + if (png_muldiv(&XYZ->red_Z, PNG_FP_1 - xy->redx - xy->redy, PNG_FP_1, + red_inverse) == 0) return 1; - if (!png_muldiv(&XYZ->green_X, xy->greenx, PNG_FP_1, green_inverse)) + if (png_muldiv(&XYZ->green_X, xy->greenx, PNG_FP_1, green_inverse) == 0) return 1; - if (!png_muldiv(&XYZ->green_Y, xy->greeny, PNG_FP_1, green_inverse)) + if (png_muldiv(&XYZ->green_Y, xy->greeny, PNG_FP_1, green_inverse) == 0) return 1; - if (!png_muldiv(&XYZ->green_Z, PNG_FP_1 - xy->greenx - xy->greeny, PNG_FP_1, - green_inverse)) + if (png_muldiv(&XYZ->green_Z, PNG_FP_1 - xy->greenx - xy->greeny, PNG_FP_1, + green_inverse) == 0) return 1; - if (!png_muldiv(&XYZ->blue_X, xy->bluex, blue_scale, PNG_FP_1)) return 1; - if (!png_muldiv(&XYZ->blue_Y, xy->bluey, blue_scale, PNG_FP_1)) return 1; - if (!png_muldiv(&XYZ->blue_Z, PNG_FP_1 - xy->bluex - xy->bluey, blue_scale, - PNG_FP_1)) + if (png_muldiv(&XYZ->blue_X, xy->bluex, blue_scale, PNG_FP_1) == 0) + return 1; + if (png_muldiv(&XYZ->blue_Y, xy->bluey, blue_scale, PNG_FP_1) == 0) + return 1; + if (png_muldiv(&XYZ->blue_Z, PNG_FP_1 - xy->bluex - xy->bluey, blue_scale, + PNG_FP_1) == 0) return 1; return 0; /*success*/ @@ -1411,24 +1463,35 @@ png_XYZ_normalize(png_XYZ *XYZ) * safe. */ Y = XYZ->red_Y; - if (0x7fffffff - Y < XYZ->green_X) return 1; + if (0x7fffffff - Y < XYZ->green_X) + return 1; Y += XYZ->green_Y; - if (0x7fffffff - Y < XYZ->blue_X) return 1; + if (0x7fffffff - Y < XYZ->blue_X) + return 1; Y += XYZ->blue_Y; if (Y != PNG_FP_1) { - if (!png_muldiv(&XYZ->red_X, XYZ->red_X, PNG_FP_1, Y)) return 1; - if (!png_muldiv(&XYZ->red_Y, XYZ->red_Y, PNG_FP_1, Y)) return 1; - if (!png_muldiv(&XYZ->red_Z, XYZ->red_Z, PNG_FP_1, Y)) return 1; + if (png_muldiv(&XYZ->red_X, XYZ->red_X, PNG_FP_1, Y) == 0) + return 1; + if (png_muldiv(&XYZ->red_Y, XYZ->red_Y, PNG_FP_1, Y) == 0) + return 1; + if (png_muldiv(&XYZ->red_Z, XYZ->red_Z, PNG_FP_1, Y) == 0) + return 1; - if (!png_muldiv(&XYZ->green_X, XYZ->green_X, PNG_FP_1, Y)) return 1; - if (!png_muldiv(&XYZ->green_Y, XYZ->green_Y, PNG_FP_1, Y)) return 1; - if (!png_muldiv(&XYZ->green_Z, XYZ->green_Z, PNG_FP_1, Y)) return 1; + if (png_muldiv(&XYZ->green_X, XYZ->green_X, PNG_FP_1, Y) == 0) + return 1; + if (png_muldiv(&XYZ->green_Y, XYZ->green_Y, PNG_FP_1, Y) == 0) + return 1; + if (png_muldiv(&XYZ->green_Z, XYZ->green_Z, PNG_FP_1, Y) == 0) + return 1; - if (!png_muldiv(&XYZ->blue_X, XYZ->blue_X, PNG_FP_1, Y)) return 1; - if (!png_muldiv(&XYZ->blue_Y, XYZ->blue_Y, PNG_FP_1, Y)) return 1; - if (!png_muldiv(&XYZ->blue_Z, XYZ->blue_Z, PNG_FP_1, Y)) return 1; + if (png_muldiv(&XYZ->blue_X, XYZ->blue_X, PNG_FP_1, Y) == 0) + return 1; + if (png_muldiv(&XYZ->blue_Y, XYZ->blue_Y, PNG_FP_1, Y) == 0) + return 1; + if (png_muldiv(&XYZ->blue_Z, XYZ->blue_Z, PNG_FP_1, Y) == 0) + return 1; } return 0; @@ -1438,14 +1501,16 @@ static int png_colorspace_endpoints_match(const png_xy *xy1, const png_xy *xy2, int delta) { /* Allow an error of +/-0.01 (absolute value) on each chromaticity */ - return !(PNG_OUT_OF_RANGE(xy1->whitex, xy2->whitex,delta) || - PNG_OUT_OF_RANGE(xy1->whitey, xy2->whitey,delta) || - PNG_OUT_OF_RANGE(xy1->redx, xy2->redx, delta) || - PNG_OUT_OF_RANGE(xy1->redy, xy2->redy, delta) || - PNG_OUT_OF_RANGE(xy1->greenx, xy2->greenx,delta) || - PNG_OUT_OF_RANGE(xy1->greeny, xy2->greeny,delta) || - PNG_OUT_OF_RANGE(xy1->bluex, xy2->bluex, delta) || - PNG_OUT_OF_RANGE(xy1->bluey, xy2->bluey, delta)); + if (PNG_OUT_OF_RANGE(xy1->whitex, xy2->whitex,delta) || + PNG_OUT_OF_RANGE(xy1->whitey, xy2->whitey,delta) || + PNG_OUT_OF_RANGE(xy1->redx, xy2->redx, delta) || + PNG_OUT_OF_RANGE(xy1->redy, xy2->redy, delta) || + PNG_OUT_OF_RANGE(xy1->greenx, xy2->greenx,delta) || + PNG_OUT_OF_RANGE(xy1->greeny, xy2->greeny,delta) || + PNG_OUT_OF_RANGE(xy1->bluex, xy2->bluex, delta) || + PNG_OUT_OF_RANGE(xy1->bluey, xy2->bluey, delta)) + return 0; + return 1; } /* Added in libpng-1.6.0, a different check for the validity of a set of cHRM @@ -1466,13 +1531,15 @@ png_colorspace_check_xy(png_XYZ *XYZ, const png_xy *xy) /* As a side-effect this routine also returns the XYZ endpoints. */ result = png_XYZ_from_xy(XYZ, xy); - if (result != 0) return result; + if (result != 0) + return result; result = png_xy_from_XYZ(&xy_test, XYZ); - if (result != 0) return result; + if (result != 0) + return result; if (png_colorspace_endpoints_match(xy, &xy_test, - 5/*actually, the math is pretty accurate*/)) + 5/*actually, the math is pretty accurate*/) != 0) return 0; /* Too much slip */ @@ -1489,10 +1556,12 @@ png_colorspace_check_XYZ(png_xy *xy, png_XYZ *XYZ) png_XYZ XYZtemp; result = png_XYZ_normalize(XYZ); - if (result != 0) return result; + if (result != 0) + return result; result = png_xy_from_XYZ(xy, XYZ); - if (result != 0) return result; + if (result != 0) + return result; XYZtemp = *XYZ; return png_colorspace_check_xy(&XYZtemp, xy); @@ -1510,22 +1579,24 @@ static const png_xy sRGB_xy = /* From ITU-R BT.709-3 */ static int png_colorspace_set_xy_and_XYZ(png_const_structrp png_ptr, - png_colorspacerp colorspace, const png_xy *xy, const png_XYZ *XYZ, - int preferred) + png_colorspacerp colorspace, const png_xy *xy, const png_XYZ *XYZ, + int preferred) { - if (colorspace->flags & PNG_COLORSPACE_INVALID) + if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0) return 0; /* The consistency check is performed on the chromaticities; this factors out * variations because of the normalization (or not) of the end point Y * values. */ - if (preferred < 2 && (colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS)) + if (preferred < 2 && + (colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) { /* The end points must be reasonably close to any we already have. The * following allows an error of up to +/-.001 */ - if (!png_colorspace_endpoints_match(xy, &colorspace->end_points_xy, 100)) + if (png_colorspace_endpoints_match(xy, &colorspace->end_points_xy, + 100) == 0) { colorspace->flags |= PNG_COLORSPACE_INVALID; png_benign_error(png_ptr, "inconsistent chromaticities"); @@ -1544,7 +1615,7 @@ png_colorspace_set_xy_and_XYZ(png_const_structrp png_ptr, /* The end points are normally quoted to two decimal digits, so allow +/-0.01 * on this test. */ - if (png_colorspace_endpoints_match(xy, &sRGB_xy, 1000)) + if (png_colorspace_endpoints_match(xy, &sRGB_xy, 1000) != 0) colorspace->flags |= PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB; else @@ -1556,7 +1627,7 @@ png_colorspace_set_xy_and_XYZ(png_const_structrp png_ptr, int /* PRIVATE */ png_colorspace_set_chromaticities(png_const_structrp png_ptr, - png_colorspacerp colorspace, const png_xy *xy, int preferred) + png_colorspacerp colorspace, const png_xy *xy, int preferred) { /* We must check the end points to ensure they are reasonable - in the past * color management systems have crashed as a result of getting bogus @@ -1570,7 +1641,7 @@ png_colorspace_set_chromaticities(png_const_structrp png_ptr, { case 0: /* success */ return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, xy, &XYZ, - preferred); + preferred); case 1: /* We can't invert the chromaticities so we can't produce value XYZ @@ -1585,7 +1656,7 @@ png_colorspace_set_chromaticities(png_const_structrp png_ptr, * want error reports so for the moment it is an error. */ colorspace->flags |= PNG_COLORSPACE_INVALID; - png_error(png_ptr, "internal error checking chromaticities"); + impossible("error checking chromaticities"); break; } @@ -1594,7 +1665,7 @@ png_colorspace_set_chromaticities(png_const_structrp png_ptr, int /* PRIVATE */ png_colorspace_set_endpoints(png_const_structrp png_ptr, - png_colorspacerp colorspace, const png_XYZ *XYZ_in, int preferred) + png_colorspacerp colorspace, const png_XYZ *XYZ_in, int preferred) { png_XYZ XYZ = *XYZ_in; png_xy xy; @@ -1603,7 +1674,7 @@ png_colorspace_set_endpoints(png_const_structrp png_ptr, { case 0: return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, &xy, &XYZ, - preferred); + preferred); case 1: /* End points are invalid. */ @@ -1613,7 +1684,7 @@ png_colorspace_set_endpoints(png_const_structrp png_ptr, default: colorspace->flags |= PNG_COLORSPACE_INVALID; - png_error(png_ptr, "internal error checking chromaticities"); + impossible("error checking chromaticities"); break; } @@ -1627,7 +1698,7 @@ png_icc_tag_char(png_uint_32 byte) { byte &= 0xff; if (byte >= 32 && byte <= 126) - return (char)byte; + return (char)/*SAFE*/byte; else return '?'; } @@ -1650,7 +1721,8 @@ is_ICC_signature_char(png_alloc_size_t it) (it >= 97 && it <= 122); } -static int is_ICC_signature(png_alloc_size_t it) +static int +is_ICC_signature(png_alloc_size_t it) { return is_ICC_signature_char(it >> 24) /* checks all the top bits */ && is_ICC_signature_char((it >> 16) & 0xff) && @@ -1660,7 +1732,7 @@ static int is_ICC_signature(png_alloc_size_t it) static int png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace, - png_const_charp name, png_alloc_size_t value, png_const_charp reason) + png_const_charp name, png_alloc_size_t value, png_const_charp reason) { size_t pos; char message[196]; /* see below for calculation */ @@ -1671,7 +1743,7 @@ png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace, pos = png_safecat(message, (sizeof message), 0, "profile '"); /* 9 chars */ pos = png_safecat(message, pos+79, pos, name); /* Truncate to 79 chars */ pos = png_safecat(message, (sizeof message), pos, "': "); /* +2 = 90 */ - if (is_ICC_signature(value)) + if (is_ICC_signature(value) != 0) { /* So 'value' is at most 4 bytes and the following cast is safe */ png_icc_tag_name(message+pos, (png_uint_32)value); @@ -1685,8 +1757,8 @@ png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace, char number[PNG_NUMBER_BUFFER_SIZE]; /* +24 = 114*/ pos = png_safecat(message, (sizeof message), pos, - png_format_number(number, number+(sizeof number), - PNG_NUMBER_FORMAT_x, value)); + png_format_number(number, number+(sizeof number), + PNG_NUMBER_FORMAT_x, value)); pos = png_safecat(message, (sizeof message), pos, "h: "); /*+2 = 116*/ } # endif @@ -1695,12 +1767,12 @@ png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace, PNG_UNUSED(pos) /* This is recoverable, but make it unconditionally an app_error on write to - * avoid writing invalid ICC profiles into PNG files. (I.e. we handle them + * avoid writing invalid ICC profiles into PNG files (i.e., we handle them * on read, with a warning, but on write unless the app turns off * application errors the PNG won't be written.) */ png_chunk_report(png_ptr, message, - (colorspace != NULL) ? PNG_CHUNK_ERROR : PNG_CHUNK_WRITE_ERROR); + (colorspace != NULL) ? PNG_CHUNK_ERROR : PNG_CHUNK_WRITE_ERROR); return 0; } @@ -1709,12 +1781,12 @@ png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace, #ifdef PNG_sRGB_SUPPORTED int /* PRIVATE */ png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace, - int intent) + int intent) { /* sRGB sets known gamma, end points and (from the chunk) intent. */ /* IMPORTANT: these are not necessarily the values found in an ICC profile * because ICC profiles store values adapted to a D50 environment; it is - * expected that the ICC profile mediaWhitePointTag will be D50, see the + * expected that the ICC profile mediaWhitePointTag will be D50; see the * checks and code elsewhere to understand this better. * * These XYZ values, which are accurate to 5dp, produce rgb to gray @@ -1732,7 +1804,7 @@ png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace, }; /* Do nothing if the colorspace is already invalidated. */ - if (colorspace->flags & PNG_COLORSPACE_INVALID) + if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0) return 0; /* Check the intent, then check for existing settings. It is valid for the @@ -1746,10 +1818,10 @@ png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace, */ if (intent < 0 || intent >= PNG_sRGB_INTENT_LAST) return png_icc_profile_error(png_ptr, colorspace, "sRGB", - (unsigned)intent, "invalid sRGB rendering intent"); + (unsigned)intent, "invalid sRGB rendering intent"); if ((colorspace->flags & PNG_COLORSPACE_HAVE_INTENT) != 0 && - colorspace->rendering_intent != intent) + colorspace->rendering_intent != intent) return png_icc_profile_error(png_ptr, colorspace, "sRGB", (unsigned)intent, "inconsistent rendering intents"); @@ -1763,8 +1835,8 @@ png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace, * warn but overwrite the value with the correct one. */ if ((colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0 && - !png_colorspace_endpoints_match(&sRGB_xy, &colorspace->end_points_xy, - 100)) + !png_colorspace_endpoints_match(&sRGB_xy, &colorspace->end_points_xy, + 100)) png_chunk_report(png_ptr, "cHRM chunk does not match sRGB", PNG_CHUNK_ERROR); @@ -1772,10 +1844,10 @@ png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace, * returns true when the 'from' argument corresponds to sRGB (2). */ (void)png_colorspace_check_gamma(png_ptr, colorspace, PNG_GAMMA_sRGB_INVERSE, - 2/*from sRGB*/); + 2/*from sRGB*/); /* intent: bugs in GCC force 'int' to be used as the parameter type. */ - colorspace->rendering_intent = (png_uint_16)intent; + colorspace->rendering_intent = png_check_u16(png_ptr, intent); colorspace->flags |= PNG_COLORSPACE_HAVE_INTENT; /* endpoints */ @@ -1807,23 +1879,23 @@ static const png_byte D50_nCIEXYZ[12] = int /* PRIVATE */ png_icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace, - png_const_charp name, png_uint_32 profile_length) + png_const_charp name, png_uint_32 profile_length) { if (profile_length < 132) return png_icc_profile_error(png_ptr, colorspace, name, profile_length, - "too short"); + "too short"); if (profile_length & 3) return png_icc_profile_error(png_ptr, colorspace, name, profile_length, - "invalid length"); + "invalid length"); return 1; } int /* PRIVATE */ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, - png_const_charp name, png_uint_32 profile_length, - png_const_bytep profile/* first 132 bytes only */, int color_type) + png_const_charp name, png_uint_32 profile_length, + png_const_bytep profile/* first 132 bytes only */, int is_color) { png_uint_32 temp; @@ -1835,13 +1907,13 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, temp = png_get_uint_32(profile); if (temp != profile_length) return png_icc_profile_error(png_ptr, colorspace, name, temp, - "length does not match profile"); + "length does not match profile"); temp = png_get_uint_32(profile+128); /* tag count: 12 bytes/tag */ if (temp > 357913930 || /* (2^32-4-132)/12: maximum possible tag count */ profile_length < 132+12*temp) /* truncated tag table */ return png_icc_profile_error(png_ptr, colorspace, name, temp, - "tag count too large"); + "tag count too large"); /* The 'intent' must be valid or we can't store it, ICC limits the intent to * 16 bits. @@ -1849,14 +1921,14 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, temp = png_get_uint_32(profile+64); if (temp >= 0xffff) /* The ICC limit */ return png_icc_profile_error(png_ptr, colorspace, name, temp, - "invalid rendering intent"); + "invalid rendering intent"); /* This is just a warning because the profile may be valid in future * versions. */ if (temp >= PNG_sRGB_INTENT_LAST) (void)png_icc_profile_error(png_ptr, NULL, name, temp, - "intent outside defined range"); + "intent outside defined range"); /* At this point the tag table can't be checked because it hasn't necessarily * been loaded; however, various header fields can be checked. These checks @@ -1873,7 +1945,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, temp = png_get_uint_32(profile+36); /* signature 'ascp' */ if (temp != 0x61637370) return png_icc_profile_error(png_ptr, colorspace, name, temp, - "invalid signature"); + "invalid signature"); /* Currently the PCS illuminant/adopted white point (the computational * white point) are required to be D50, @@ -1884,7 +1956,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, */ if (memcmp(profile+68, D50_nCIEXYZ, 12) != 0) (void)png_icc_profile_error(png_ptr, NULL, name, 0/*no tag value*/, - "PCS illuminant is not D50"); + "PCS illuminant is not D50"); /* The PNG spec requires this: * "If the iCCP chunk is present, the image samples conform to the colour @@ -1910,20 +1982,20 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, switch (temp) { case 0x52474220: /* 'RGB ' */ - if (!(color_type & PNG_COLOR_MASK_COLOR)) + if (!is_color) return png_icc_profile_error(png_ptr, colorspace, name, temp, - "RGB color space not permitted on grayscale PNG"); + "RGB color space not permitted on grayscale PNG"); break; case 0x47524159: /* 'GRAY' */ - if (color_type & PNG_COLOR_MASK_COLOR) + if (is_color) return png_icc_profile_error(png_ptr, colorspace, name, temp, - "Gray color space not permitted on RGB PNG"); + "Gray color space not permitted on RGB PNG"); break; default: return png_icc_profile_error(png_ptr, colorspace, name, temp, - "invalid ICC profile color space"); + "invalid ICC profile color space"); } /* It is up to the application to check that the profile class matches the @@ -1948,25 +2020,25 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, case 0x61627374: /* 'abst' */ /* May not be embedded in an image */ return png_icc_profile_error(png_ptr, colorspace, name, temp, - "invalid embedded Abstract ICC profile"); + "invalid embedded Abstract ICC profile"); case 0x6C696E6B: /* 'link' */ - /* DeviceLink profiles cannnot be interpreted in a non-device specific + /* DeviceLink profiles cannot be interpreted in a non-device specific * fashion, if an app uses the AToB0Tag in the profile the results are * undefined unless the result is sent to the intended device, * therefore a DeviceLink profile should not be found embedded in a * PNG. */ return png_icc_profile_error(png_ptr, colorspace, name, temp, - "unexpected DeviceLink ICC profile class"); + "unexpected DeviceLink ICC profile class"); case 0x6E6D636C: /* 'nmcl' */ /* A NamedColor profile is also device specific, however it doesn't - * contain an AToB0 tag that is open to misintrepretation. Almost + * contain an AToB0 tag that is open to misinterpretation. Almost * certainly it will fail the tests below. */ (void)png_icc_profile_error(png_ptr, NULL, name, temp, - "unexpected NamedColor ICC profile class"); + "unexpected NamedColor ICC profile class"); break; default: @@ -1976,7 +2048,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, * understood profiles. */ (void)png_icc_profile_error(png_ptr, NULL, name, temp, - "unrecognized ICC profile class"); + "unrecognized ICC profile class"); break; } @@ -1992,7 +2064,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, default: return png_icc_profile_error(png_ptr, colorspace, name, temp, - "unexpected ICC PCS encoding"); + "unexpected ICC PCS encoding"); } return 1; @@ -2000,8 +2072,8 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, int /* PRIVATE */ png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace, - png_const_charp name, png_uint_32 profile_length, - png_const_bytep profile /* header plus whole tag table */) + png_const_charp name, png_uint_32 profile_length, + png_const_bytep profile /* header plus whole tag table */) { png_uint_32 tag_count = png_get_uint_32(profile+128); png_uint_32 itag; @@ -2029,7 +2101,7 @@ png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace, * alignment. */ (void)png_icc_profile_error(png_ptr, NULL, name, tag_id, - "ICC profile tag start not a multiple of 4"); + "ICC profile tag start not a multiple of 4"); } /* This is a hard error; potentially it can cause read outside the @@ -2037,13 +2109,14 @@ png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace, */ if (tag_start > profile_length || tag_length > profile_length - tag_start) return png_icc_profile_error(png_ptr, colorspace, name, tag_id, - "ICC profile tag outside profile"); + "ICC profile tag outside profile"); } return 1; /* success, maybe with warnings */ } #ifdef PNG_sRGB_SUPPORTED +#if PNG_sRGB_PROFILE_CHECKS >= 0 /* Information about the known ICC sRGB profiles */ static const struct { @@ -2064,22 +2137,22 @@ static const struct */ /* adler32, crc32, MD5[4], intent, date, length, file-name */ PNG_ICC_CHECKSUM(0x0a3fd9f6, 0x3b8772b9, - PNG_MD5(0x29f83dde, 0xaff255ae, 0x7842fae4, 0xca83390d), 0, 0, - "2009/03/27 21:36:31", 3048, "sRGB_IEC61966-2-1_black_scaled.icc") + PNG_MD5(0x29f83dde, 0xaff255ae, 0x7842fae4, 0xca83390d), 0, 0, + "2009/03/27 21:36:31", 3048, "sRGB_IEC61966-2-1_black_scaled.icc") /* ICC sRGB v2 perceptual no black-compensation: */ PNG_ICC_CHECKSUM(0x4909e5e1, 0x427ebb21, - PNG_MD5(0xc95bd637, 0xe95d8a3b, 0x0df38f99, 0xc1320389), 1, 0, - "2009/03/27 21:37:45", 3052, "sRGB_IEC61966-2-1_no_black_scaling.icc") + PNG_MD5(0xc95bd637, 0xe95d8a3b, 0x0df38f99, 0xc1320389), 1, 0, + "2009/03/27 21:37:45", 3052, "sRGB_IEC61966-2-1_no_black_scaling.icc") PNG_ICC_CHECKSUM(0xfd2144a1, 0x306fd8ae, - PNG_MD5(0xfc663378, 0x37e2886b, 0xfd72e983, 0x8228f1b8), 0, 0, - "2009/08/10 17:28:01", 60988, "sRGB_v4_ICC_preference_displayclass.icc") + PNG_MD5(0xfc663378, 0x37e2886b, 0xfd72e983, 0x8228f1b8), 0, 0, + "2009/08/10 17:28:01", 60988, "sRGB_v4_ICC_preference_displayclass.icc") /* ICC sRGB v4 perceptual */ PNG_ICC_CHECKSUM(0x209c35d2, 0xbbef7812, - PNG_MD5(0x34562abf, 0x994ccd06, 0x6d2c5721, 0xd0d68c5d), 0, 0, - "2007/07/25 00:05:37", 60960, "sRGB_v4_ICC_preference.icc") + PNG_MD5(0x34562abf, 0x994ccd06, 0x6d2c5721, 0xd0d68c5d), 0, 0, + "2007/07/25 00:05:37", 60960, "sRGB_v4_ICC_preference.icc") /* The following profiles have no known MD5 checksum. If there is a match * on the (empty) MD5 the other fields are used to attempt a match and @@ -2087,8 +2160,8 @@ static const struct * which suggests that they were also made by Hewlett Packard. */ PNG_ICC_CHECKSUM(0xa054d762, 0x5d5129ce, - PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 0, - "2004/07/21 18:57:42", 3024, "sRGB_IEC61966-2-1_noBPC.icc") + PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 0, + "2004/07/21 18:57:42", 3024, "sRGB_IEC61966-2-1_noBPC.icc") /* This is a 'mntr' (display) profile with a mediaWhitePointTag that does not * match the D50 PCS illuminant in the header (it is in fact the D65 values, @@ -2098,17 +2171,17 @@ static const struct * chromaticAdaptationTag. */ PNG_ICC_CHECKSUM(0xf784f3fb, 0x182ea552, - PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 0, 1/*broken*/, - "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 perceptual") + PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 0, 1/*broken*/, + "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 perceptual") PNG_ICC_CHECKSUM(0x0398f3fc, 0xf29e526d, - PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 1/*broken*/, - "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 media-relative") + PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 1/*broken*/, + "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 media-relative") }; static int png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr, - png_const_bytep profile, uLong adler) + png_const_bytep profile, uLong adler) { /* The quick check is to verify just the MD5 signature and trust the * rest of the data. Because the profile has already been verified for @@ -2124,6 +2197,12 @@ png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr, #endif unsigned int i; +# ifdef PNG_SET_OPTION_SUPPORTED +# ifdef PNG_SKIP_sRGB_CHECK_PROFILE + /* First see if PNG_SKIP_sRGB_CHECK_PROFILE has been set to "on" */ + if (!png_ptr->skip_sRGB_profile_check) +# endif /* SKIP_sRGB_CHECK_PROFILE */ +# endif /* SET_OPTION */ for (i=0; i < (sizeof png_sRGB_checks) / (sizeof png_sRGB_checks[0]); ++i) { if (png_get_uint_32(profile+84) == png_sRGB_checks[i].md5[0] && @@ -2136,7 +2215,7 @@ png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr, * are not used by default if there is an MD5!) */ # if PNG_sRGB_PROFILE_CHECKS == 0 - if (png_sRGB_checks[i].have_md5) + if (png_sRGB_checks[i].have_md5 != 0) return 1+png_sRGB_checks[i].is_broken; # endif @@ -2149,7 +2228,7 @@ png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr, /* Length *and* intent must match */ if (length == png_sRGB_checks[i].length && - intent == png_sRGB_checks[i].intent) + intent == (png_uint_32) png_sRGB_checks[i].intent) { /* Now calculate the adler32 if not done already. */ if (adler == 0) @@ -2176,7 +2255,7 @@ png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr, if (crc == png_sRGB_checks[i].crc) # endif { - if (png_sRGB_checks[i].is_broken) + if (png_sRGB_checks[i].is_broken != 0) { /* These profiles are known to have bad data that may cause * problems if they are used, therefore attempt to @@ -2184,24 +2263,23 @@ png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr, * which is made irrelevant by this error. */ png_chunk_report(png_ptr, "known incorrect sRGB profile", - PNG_CHUNK_ERROR); + PNG_CHUNK_ERROR); } /* Warn that this being done; this isn't even an error since * the profile is perfectly valid, but it would be nice if * people used the up-to-date ones. */ - else if (!png_sRGB_checks[i].have_md5) + else if (png_sRGB_checks[i].have_md5 == 0) { png_chunk_report(png_ptr, - "out-of-date sRGB profile with no signature", - PNG_CHUNK_WARNING); + "out-of-date sRGB profile with no signature", + PNG_CHUNK_WARNING); } return 1+png_sRGB_checks[i].is_broken; } } - } # if PNG_sRGB_PROFILE_CHECKS > 0 /* The signature matched, but the profile had been changed in some @@ -2209,45 +2287,46 @@ png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr, * Fall through to "no match". */ png_chunk_report(png_ptr, - "Not recognizing known sRGB profile that has been edited", + "Not recognizing known sRGB profile that has been edited", PNG_CHUNK_WARNING); + break; # endif + } } } return 0; /* no match */ } -#endif -#ifdef PNG_sRGB_SUPPORTED void /* PRIVATE */ png_icc_set_sRGB(png_const_structrp png_ptr, - png_colorspacerp colorspace, png_const_bytep profile, uLong adler) + png_colorspacerp colorspace, png_const_bytep profile, uLong adler) { /* Is this profile one of the known ICC sRGB profiles? If it is, just set * the sRGB information. */ - if (png_compare_ICC_profile_with_sRGB(png_ptr, profile, adler)) + if (png_compare_ICC_profile_with_sRGB(png_ptr, profile, adler) != 0) (void)png_colorspace_set_sRGB(png_ptr, colorspace, (int)/*already checked*/png_get_uint_32(profile+64)); } -#endif /* PNG_READ_sRGB_SUPPORTED */ +#endif /* PNG_sRGB_PROFILE_CHECKS >= 0 */ +#endif /* sRGB */ int /* PRIVATE */ png_colorspace_set_ICC(png_const_structrp png_ptr, png_colorspacerp colorspace, - png_const_charp name, png_uint_32 profile_length, png_const_bytep profile, - int color_type) + png_const_charp name, png_uint_32 profile_length, png_const_bytep profile, + int is_color) { - if (colorspace->flags & PNG_COLORSPACE_INVALID) + if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0) return 0; - if (png_icc_check_length(png_ptr, colorspace, name, profile_length) && - png_icc_check_header(png_ptr, colorspace, name, profile_length, profile, - color_type) && - png_icc_check_tag_table(png_ptr, colorspace, name, profile_length, - profile)) + if (png_icc_check_length(png_ptr, colorspace, name, profile_length) != 0 && + png_icc_check_header(png_ptr, colorspace, name, profile_length, profile, + is_color) != 0 && + png_icc_check_tag_table(png_ptr, colorspace, name, profile_length, + profile) != 0) { -# ifdef PNG_sRGB_SUPPORTED +# if defined(PNG_sRGB_SUPPORTED) && PNG_sRGB_PROFILE_CHECKS >= 0 /* If no sRGB support, don't try storing sRGB information */ png_icc_set_sRGB(png_ptr, colorspace, profile, 0); # endif @@ -2258,80 +2337,149 @@ png_colorspace_set_ICC(png_const_structrp png_ptr, png_colorspacerp colorspace, return 0; } #endif /* iCCP */ +#endif /* COLORSPACE */ -#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED -void /* PRIVATE */ -png_colorspace_set_rgb_coefficients(png_structrp png_ptr) +png_alloc_size_t /* PRIVATE */ +png_calc_rowbytes(png_const_structrp png_ptr, unsigned int pixel_depth, + png_uint_32 row_width) { - /* Set the rgb_to_gray coefficients from the colorspace. */ - if ((png_ptr->colorspace.flags & PNG_COLORSPACE_RGB_TO_GRAY_SET) == 0 && - (png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) + png_alloc_size_t rowbytes = row_width; + + /* Carefully calculate the row buffer size. */ + if (pixel_depth > 8) { - /* png_set_background has not been called, get the coefficients from the Y - * values of the colorspace colorants. + if ((pixel_depth & 7) != 0) + png_error(png_ptr, "unsupported pixel byte size"); + + pixel_depth >>= 3; /* Now in bytes */ + + if (rowbytes > PNG_SIZE_MAX/pixel_depth) + png_error(png_ptr, "image row exceeds system limits"); + + rowbytes *= pixel_depth; + } + + else /* Less than 1 byte per pixel */ switch (pixel_depth) + { + case 1: rowbytes += 7; rowbytes >>= 3; break; + case 2: rowbytes += 3; rowbytes >>= 2; break; + case 4: rowbytes += 1; rowbytes >>= 1; break; + case 8: break; + default: + png_error(png_ptr, "unsupported pixel bit size"); + } + + return rowbytes; +} + +unsigned int /*PRIVATE*/ +png_max_pixel_block(png_const_structrp png_ptr) +{ + /* Need the *smallest* pixel size that must occur in alignment units. On + * read this is the PNG pixel depth because the read transforms cannot reduce + * the pixel size below the input size or 8-bits, whichever is smaller. + * + * On write the 'pack' transform can pack 8-bit pixels back to a lower bit + * depth, but the lowest bit depth is still given by the PNG pixel size. + */ + const unsigned int pixel_depth = PNG_PIXEL_DEPTH(*png_ptr); + const unsigned int pixel_block = /* count of pixels in a block */ + pixel_depth < 8U ? + PNG_ROW_BUFFER_BYTE_ALIGN * (8U/pixel_depth) : + PNG_ROW_BUFFER_BYTE_ALIGN; /* pixels may be any whole byte size */ + /* The maximum block size in bits is MAX_PIXEL_DEPTH*pixel_block so work out + * the minimum number of pixel blocks that can fit in PNG_ROW_BUFFER_SIZE + * bytes and use this to calculate the maximum number of pixels: + */ + return pixel_block * + ((8U*PNG_ROW_BUFFER_SIZE) / (png_ptr->row_max_pixel_depth*pixel_block)); +} + +void /* PRIVATE */ +png_copy_row(png_const_structrp png_ptr, png_bytep dp, png_const_bytep sp, + png_uint_32 x/*in INPUT*/, png_uint_32 width/*of INPUT*/, + unsigned int pixel_depth, int clear/*clear the final byte*/, int x_in_dest) + /* Copy the row in row_buffer; this is the non-interlaced copy used in both + * the read and write code. 'x_in_dest' specifies whether the 'x' applies to + * the destination (sp->dp[x], x_in_dest tru) or the source (sp[x]->dp, + * x_in_dest false). + */ +{ + png_alloc_size_t cb, offset; + unsigned int remaining; /* remaining bits in a partial byte */ + + /* Copy 'cb' pixels, but take care with the last byte because it may + * be partially written. 'x' must correspond to the start of a byte, check + * that too: + */ + switch (pixel_depth) + { + case 1U: remaining = width & 7U; + debug((x & 7U) == 0U); + cb = width >> 3; + offset = x >> 3; + break; + case 2U: remaining = (width << 1) & 6U; + debug((x & 3U) == 0U); + cb = width >> 2; + offset = x >> 2; + break; + case 4U: remaining = (width << 2) & 4U; + debug((x & 1U) == 0U); + cb = width >> 1; + offset = x >> 1; + break; + case 8U: remaining = 0U; + cb = width; + offset = x; + break; + default: remaining = 0U; + cb = png_calc_rowbytes(png_ptr, pixel_depth, width); + offset = png_calc_rowbytes(png_ptr, pixel_depth, x); + break; + } + + if (x_in_dest) + dp += offset; + + else + sp += offset; + + memcpy(dp, sp, cb); + + if (remaining > 0U) + { + /* 'remaining' is the number of bits still to be copied. Format may be + * little endian; bits to copy in the bottom of 's'. Make 'remaining' + * into a mask of the bits to *preserve* in dp. */ - png_fixed_point r = png_ptr->colorspace.end_points_XYZ.red_Y; - png_fixed_point g = png_ptr->colorspace.end_points_XYZ.green_Y; - png_fixed_point b = png_ptr->colorspace.end_points_XYZ.blue_Y; - png_fixed_point total = r+g+b; - - if (total > 0 && - r >= 0 && png_muldiv(&r, r, 32768, total) && r >= 0 && r <= 32768 && - g >= 0 && png_muldiv(&g, g, 32768, total) && g >= 0 && g <= 32768 && - b >= 0 && png_muldiv(&b, b, 32768, total) && b >= 0 && b <= 32768 && - r+g+b <= 32769) - { - /* We allow 0 coefficients here. r+g+b may be 32769 if two or - * all of the coefficients were rounded up. Handle this by - * reducing the *largest* coefficient by 1; this matches the - * approach used for the default coefficients in pngrtran.c - */ - int add = 0; - - if (r+g+b > 32768) - add = -1; - else if (r+g+b < 32768) - add = 1; - - if (add != 0) - { - if (g >= r && g >= b) - g += add; - else if (r >= g && r >= b) - r += add; - else - b += add; - } - - /* Check for an internal error. */ - if (r+g+b != 32768) - png_error(png_ptr, - "internal error handling cHRM coefficients"); +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + if ((png_ptr->row_format & PNG_FORMAT_FLAG_SWAPPED) == 0U) + remaining = 0xffU >> remaining; else - { - png_ptr->rgb_to_gray_red_coeff = (png_uint_16)r; - png_ptr->rgb_to_gray_green_coeff = (png_uint_16)g; - } - } +# endif /* TRANSFORM_MECH */ + remaining = 0xffU << remaining; - /* This is a png_error at present even though it could be ignored - - * it should never happen, but it is important that if it does, the - * bug is fixed. + /* remaining is now the bits to *keep* from the destination byte, if + * 'clear' is true the source bytes aren't copied - this is for security + * reasons to avoid copying undefined bits at the end of a row. If + * 'clear' is set the destination bits are not preserved, they are just + * set to 0. */ + if (clear) + dp[cb] = PNG_BYTE(sp[cb] & ~remaining); + else - png_error(png_ptr, "internal error handling cHRM->XYZ"); + dp[cb] = PNG_BYTE((sp[cb] & ~remaining) | (dp[cb] & remaining)); } } -#endif - -#endif /* COLORSPACE */ void /* PRIVATE */ png_check_IHDR(png_const_structrp png_ptr, - png_uint_32 width, png_uint_32 height, int bit_depth, - int color_type, int interlace_type, int compression_type, - int filter_type) + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type) { int error = 0; @@ -2341,46 +2489,52 @@ png_check_IHDR(png_const_structrp png_ptr, png_warning(png_ptr, "Image width is zero in IHDR"); error = 1; } - else if (width > PNG_UINT_31_MAX) + + if (width > PNG_UINT_31_MAX) { png_warning(png_ptr, "Invalid image width in IHDR"); error = 1; } - else + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (width > png_ptr->user_width_max) { -# ifdef PNG_SET_USER_LIMITS_SUPPORTED - if (width > png_ptr->user_width_max) -# else - if (width > PNG_USER_WIDTH_MAX) -# endif - { - png_warning(png_ptr, "Image width exceeds user limit in IHDR"); - error = 1; - } + png_warning(png_ptr, "Image width exceeds user limit in IHDR"); + error = 1; } +#else + if (width > PNG_USER_WIDTH_MAX) + { + png_warning(png_ptr, "Image width exceeds WIDTH_MAX in IHDR"); + error = 1; + } +#endif if (height == 0) { png_warning(png_ptr, "Image height is zero in IHDR"); error = 1; } - else if (height > PNG_UINT_31_MAX) + + if (height > PNG_UINT_31_MAX) { png_warning(png_ptr, "Invalid image height in IHDR"); error = 1; } - else + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (height > png_ptr->user_height_max) { -# ifdef PNG_SET_USER_LIMITS_SUPPORTED - if (height > png_ptr->user_height_max) -# else - if (height > PNG_USER_HEIGHT_MAX) -# endif - { - png_warning(png_ptr, "Image height exceeds user limit in IHDR"); - error = 1; - } + png_warning(png_ptr, "Image height exceeds user limit in IHDR"); + error = 1; } +#else + if (height > PNG_USER_HEIGHT_MAX) + { + png_warning(png_ptr, "Image height exceeds HEIGHT_MAX in IHDR"); + error = 1; + } +#endif /* Check other values */ if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 && @@ -2418,7 +2572,7 @@ png_check_IHDR(png_const_structrp png_ptr, error = 1; } -# ifdef PNG_MNG_FEATURES_SUPPORTED +#ifdef PNG_MNG_FEATURES_SUPPORTED /* Accept filter_method 64 (intrapixel differencing) only if * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and * 2. Libpng did not read a PNG signature (this filter_method is only @@ -2428,39 +2582,46 @@ png_check_IHDR(png_const_structrp png_ptr, * 4. The filter_method is 64 and * 5. The color_type is RGB or RGBA */ - if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) && - png_ptr->mng_features_permitted) + if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) != 0 && + png_ptr->mng_features_permitted != 0) png_warning(png_ptr, "MNG features are not allowed in a PNG datastream"); if (filter_type != PNG_FILTER_TYPE_BASE) { - if (!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + if (!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 && (filter_type == PNG_INTRAPIXEL_DIFFERENCING) && ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) == 0) && (color_type == PNG_COLOR_TYPE_RGB || - color_type == PNG_COLOR_TYPE_RGB_ALPHA))) + color_type == PNG_COLOR_TYPE_RGB_ALPHA))) { - png_warning(png_ptr, "Unknown filter method in IHDR"); + png_warning(png_ptr, "Invalid filter method in IHDR"); error = 1; } - if (png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) + if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) != 0) { png_warning(png_ptr, "Invalid filter method in IHDR"); error = 1; } } -# else +#else /* !MNG_FEATURES */ if (filter_type != PNG_FILTER_TYPE_BASE) { png_warning(png_ptr, "Unknown filter method in IHDR"); error = 1; } -# endif +#endif /* !MNG_FEATURES */ if (error == 1) png_error(png_ptr, "Invalid IHDR data"); + + /* Finally, if the IHDR data is correct, check it against the system + * limits (NOTE: this need not be done; the IDAT handling code repeats the + * check in both read and write.) + */ + (void)png_calc_rowbytes(png_ptr, + PNG_COLOR_TYPE_CHANNELS(color_type) * bit_depth, width); } #if defined(PNG_sCAL_SUPPORTED) || defined(PNG_pCAL_SUPPORTED) @@ -2474,7 +2635,7 @@ png_check_IHDR(png_const_structrp png_ptr, int /* PRIVATE */ png_check_fp_number(png_const_charp string, png_size_t size, int *statep, - png_size_tp whereami) + png_size_tp whereami) { int state = *statep; png_size_t i = *whereami; @@ -2504,7 +2665,7 @@ png_check_fp_number(png_const_charp string, png_size_t size, int *statep, switch ((state & PNG_FP_STATE) + (type & PNG_FP_SAW_ANY)) { case PNG_FP_INTEGER + PNG_FP_SAW_SIGN: - if (state & PNG_FP_SAW_ANY) + if ((state & PNG_FP_SAW_ANY) != 0) goto PNG_FP_End; /* not a part of the number */ png_fp_add(state, type); @@ -2512,10 +2673,10 @@ png_check_fp_number(png_const_charp string, png_size_t size, int *statep, case PNG_FP_INTEGER + PNG_FP_SAW_DOT: /* Ok as trailer, ok as lead of fraction. */ - if (state & PNG_FP_SAW_DOT) /* two dots */ + if ((state & PNG_FP_SAW_DOT) != 0) /* two dots */ goto PNG_FP_End; - else if (state & PNG_FP_SAW_DIGIT) /* trailing dot? */ + else if ((state & PNG_FP_SAW_DIGIT) != 0) /* trailing dot? */ png_fp_add(state, type); else @@ -2524,7 +2685,7 @@ png_check_fp_number(png_const_charp string, png_size_t size, int *statep, break; case PNG_FP_INTEGER + PNG_FP_SAW_DIGIT: - if (state & PNG_FP_SAW_DOT) /* delayed fraction */ + if ((state & PNG_FP_SAW_DOT) != 0) /* delayed fraction */ png_fp_set(state, PNG_FP_FRACTION | PNG_FP_SAW_DOT); png_fp_add(state, type | PNG_FP_WAS_VALID); @@ -2562,7 +2723,7 @@ png_check_fp_number(png_const_charp string, png_size_t size, int *statep, break; case PNG_FP_EXPONENT + PNG_FP_SAW_SIGN: - if (state & PNG_FP_SAW_ANY) + if ((state & PNG_FP_SAW_ANY) != 0) goto PNG_FP_End; /* not a part of the number */ png_fp_add(state, PNG_FP_SAW_SIGN); @@ -2605,13 +2766,13 @@ png_check_fp_string(png_const_charp string, png_size_t size) int state=0; png_size_t char_index=0; - if (png_check_fp_number(string, size, &state, &char_index) && + if (png_check_fp_number(string, size, &state, &char_index) != 0 && (char_index == size || string[char_index] == 0)) return state /* must be non-zero - see above */; return 0; /* i.e. fail */ } -#endif /* pCAL or sCAL */ +#endif /* pCAL || sCAL */ #ifdef PNG_sCAL_SUPPORTED # ifdef PNG_FLOATING_POINT_SUPPORTED @@ -2630,7 +2791,7 @@ png_pow10(int power) if (power < 0) { if (power < DBL_MIN_10_EXP) return 0; - recip = 1, power = -power; + recip = 1; power = -power; } if (power > 0) @@ -2683,7 +2844,7 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size, if (fp >= DBL_MIN && fp <= DBL_MAX) { - int exp_b10; /* A base 10 exponent */ + int exp_b10; /* A base 10 exponent */ double base; /* 10^exp_b10 */ /* First extract a base 10 exponent of the number, @@ -2708,7 +2869,9 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size, double test = png_pow10(exp_b10+1); if (test <= DBL_MAX) - ++exp_b10, base = test; + { + ++exp_b10; base = test; + } else break; @@ -2722,7 +2885,10 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size, * test on DBL_MAX above. */ fp /= base; - while (fp >= 1) fp /= 10, ++exp_b10; + while (fp >= 1) + { + fp /= 10; ++exp_b10; + } /* Because of the code above fp may, at this point, be * less than .1, this is ok because the code below can @@ -2731,7 +2897,7 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size, */ { - int czero, clead, cdigits; + unsigned int czero, clead, cdigits; char exponent[10]; /* Allow up to two leading zeros - this will not lengthen @@ -2761,7 +2927,7 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size, * of the loop don't break the number into parts so * that the final digit is rounded. */ - if (cdigits+czero-clead+1 < (int)precision) + if (cdigits+czero+1 < precision+clead) fp = modf(fp, &d); else @@ -2773,7 +2939,7 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size, /* Rounding up to 10, handle that here. */ if (czero > 0) { - --czero, d = 1; + --czero; d = 1; if (cdigits == 0) --clead; } else @@ -2787,7 +2953,7 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size, else if (ch == 46) { - ch = *--ascii, ++size; + ch = *--ascii; ++size; /* Advance exp_b10 to '1', so that the * decimal point happens after the * previous digit. @@ -2814,7 +2980,9 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size, int ch = *--ascii; if (ch == 46) - ++size, exp_b10 = 1; + { + ++size; exp_b10 = 1; + } /* Else lost a leading zero, so 'exp_b10' is * still ok at (-1) @@ -2859,21 +3027,22 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size, if (exp_b10 != (-1)) { - if (exp_b10 == 0) *ascii++ = 46, --size; /* counted - above */ + if (exp_b10 == 0) + *ascii++ = 46, --size; /* counted above */ + --exp_b10; } - *ascii++ = (char)(48 + (int)d), ++cdigits; + *ascii++ = png_check_char(png_ptr, 48 + (int)d), ++cdigits; } } - while (cdigits+czero-clead < (int)precision && fp > DBL_MIN); + while (cdigits+czero < precision+clead && fp > DBL_MIN); /* The total output count (max) is now 4+precision */ /* Check for an exponent, if we don't need one we are * done and just need to terminate the string. At * this point exp_b10==(-1) is effectively if flag - it got - * to '-1' because of the decrement after outputing + * to '-1' because of the decrement after outputting * the decimal point above (the exponent required is * *not* -1!) */ @@ -2881,7 +3050,7 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size, { /* The following only happens if we didn't output the * leading zeros above for negative exponent, so this - * doest add to the digit requirement. Note that the + * doesn't add to the digit requirement. Note that the * two zeros here can only be output if the two leading * zeros were *not* output, so this doesn't increase * the output count. @@ -2904,7 +3073,7 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size, */ size -= cdigits; - *ascii++ = 69, --size; /* 'E': PLUS 1 TOTAL 2+precision */ + *ascii++ = 69; --size; /* 'E': PLUS 1 TOTAL 2+precision */ /* The following use of an unsigned temporary avoids ambiguities in * the signed arithmetic on exp_b10 and permits GCC at least to do @@ -2926,7 +3095,7 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size, while (uexp_b10 > 0) { - exponent[cdigits++] = (char)(48 + uexp_b10 % 10); + exponent[cdigits++] = (char)/*SAFE*/(48 + uexp_b10 % 10); uexp_b10 /= 10; } } @@ -2934,7 +3103,7 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size, /* Need another size check here for the exponent digits, so * this need not be considered above. */ - if ((int)size > cdigits) + if (size > cdigits) { while (cdigits > 0) *ascii++ = exponent[--cdigits]; @@ -2996,7 +3165,7 @@ png_ascii_from_fixed(png_const_structrp png_ptr, png_charp ascii, /* Split the low digit off num: */ unsigned int tmp = num/10; num -= tmp*10; - digits[ndigits++] = (char)(48 + num); + digits[ndigits++] = png_check_char(png_ptr, 48 + num); /* Record the first non-zero digit, note that this is a number * starting at 1, it's not actually the array index. */ @@ -3020,7 +3189,10 @@ png_ascii_from_fixed(png_const_structrp png_ptr, png_charp ascii, * then ndigits digits to first: */ i = 5; - while (ndigits < i) *ascii++ = 48, --i; + while (ndigits < i) + { + *ascii++ = 48; --i; + } while (ndigits >= first) *ascii++ = digits[--ndigits]; /* Don't output the trailing zeros! */ } @@ -3180,7 +3352,8 @@ png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times, result = -result; /* Check for overflow. */ - if ((negative && result <= 0) || (!negative && result >= 0)) + if ((negative != 0 && result <= 0) || + (negative == 0 && result >= 0)) { *res = result; return 1; @@ -3192,969 +3365,140 @@ png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times, return 0; } -#endif /* READ_GAMMA || INCH_CONVERSIONS */ +#endif /* GAMMA || INCH_CONVERSIONS */ -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED) -/* The following is for when the caller doesn't much care about the - * result. - */ -png_fixed_point -png_muldiv_warn(png_const_structrp png_ptr, png_fixed_point a, png_int_32 times, - png_int_32 divisor) +/* SOFTWARE SETTING SUPPORT */ +png_int_32 PNGAPI +png_setting(png_structrp png_ptr, png_uint_32 setting, png_uint_32 parameter, + png_int_32 value) { - png_fixed_point result; + png_int_32 result; - if (png_muldiv(&result, a, times, divisor)) - return result; - - png_warning(png_ptr, "fixed point overflow ignored"); - return 0; -} -#endif - -#ifdef PNG_GAMMA_SUPPORTED /* more fixed point functions for gamma */ -/* Calculate a reciprocal, return 0 on div-by-zero or overflow. */ -png_fixed_point -png_reciprocal(png_fixed_point a) -{ -#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED - double r = floor(1E10/a+.5); - - if (r <= 2147483647. && r >= -2147483648.) - return (png_fixed_point)r; -#else - png_fixed_point res; - - if (png_muldiv(&res, 100000, 100000, a)) - return res; -#endif - - return 0; /* error/overflow */ -} - -/* This is the shared test on whether a gamma value is 'significant' - whether - * it is worth doing gamma correction. - */ -int /* PRIVATE */ -png_gamma_significant(png_fixed_point gamma_val) -{ - return gamma_val < PNG_FP_1 - PNG_GAMMA_THRESHOLD_FIXED || - gamma_val > PNG_FP_1 + PNG_GAMMA_THRESHOLD_FIXED; -} -#endif - -#ifdef PNG_READ_GAMMA_SUPPORTED -/* A local convenience routine. */ -#ifndef PNG_FLOATING_ARITHMETIC_SUPPORTED -static png_fixed_point -png_product2(png_fixed_point a, png_fixed_point b) -{ - /* The required result is 1/a * 1/b; the following preserves accuracy. */ -#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED - double r = a * 1E-5; - r *= b; - r = floor(r+.5); - - if (r <= 2147483647. && r >= -2147483648.) - return (png_fixed_point)r; -#else - png_fixed_point res; - - if (png_muldiv(&res, a, b, 100000)) - return res; -#endif - - return 0; /* overflow */ -} -#endif - -/* The inverse of the above. */ -png_fixed_point -png_reciprocal2(png_fixed_point a, png_fixed_point b) -{ - /* The required result is 1/a * 1/b; the following preserves accuracy. */ -#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED - double r = 1E15/a; - r /= b; - r = floor(r+.5); - - if (r <= 2147483647. && r >= -2147483648.) - return (png_fixed_point)r; -#else - /* This may overflow because the range of png_fixed_point isn't symmetric, - * but this API is only used for the product of file and screen gamma so it - * doesn't matter that the smallest number it can produce is 1/21474, not - * 1/100000 - */ - png_fixed_point res = png_product2(a, b); - - if (res != 0) - return png_reciprocal(res); -#endif - - return 0; /* overflow */ -} -#endif /* READ_GAMMA */ - -#ifdef PNG_READ_GAMMA_SUPPORTED /* gamma table code */ -#ifndef PNG_FLOATING_ARITHMETIC_SUPPORTED -/* Fixed point gamma. - * - * The code to calculate the tables used below can be found in the shell script - * contrib/tools/intgamma.sh - * - * To calculate gamma this code implements fast log() and exp() calls using only - * fixed point arithmetic. This code has sufficient precision for either 8-bit - * or 16-bit sample values. - * - * The tables used here were calculated using simple 'bc' programs, but C double - * precision floating point arithmetic would work fine. - * - * 8-bit log table - * This is a table of -log(value/255)/log(2) for 'value' in the range 128 to - * 255, so it's the base 2 logarithm of a normalized 8-bit floating point - * mantissa. The numbers are 32-bit fractions. - */ -static const png_uint_32 -png_8bit_l2[128] = -{ - 4270715492U, 4222494797U, 4174646467U, 4127164793U, 4080044201U, 4033279239U, - 3986864580U, 3940795015U, 3895065449U, 3849670902U, 3804606499U, 3759867474U, - 3715449162U, 3671346997U, 3627556511U, 3584073329U, 3540893168U, 3498011834U, - 3455425220U, 3413129301U, 3371120137U, 3329393864U, 3287946700U, 3246774933U, - 3205874930U, 3165243125U, 3124876025U, 3084770202U, 3044922296U, 3005329011U, - 2965987113U, 2926893432U, 2888044853U, 2849438323U, 2811070844U, 2772939474U, - 2735041326U, 2697373562U, 2659933400U, 2622718104U, 2585724991U, 2548951424U, - 2512394810U, 2476052606U, 2439922311U, 2404001468U, 2368287663U, 2332778523U, - 2297471715U, 2262364947U, 2227455964U, 2192742551U, 2158222529U, 2123893754U, - 2089754119U, 2055801552U, 2022034013U, 1988449497U, 1955046031U, 1921821672U, - 1888774511U, 1855902668U, 1823204291U, 1790677560U, 1758320682U, 1726131893U, - 1694109454U, 1662251657U, 1630556815U, 1599023271U, 1567649391U, 1536433567U, - 1505374214U, 1474469770U, 1443718700U, 1413119487U, 1382670639U, 1352370686U, - 1322218179U, 1292211689U, 1262349810U, 1232631153U, 1203054352U, 1173618059U, - 1144320946U, 1115161701U, 1086139034U, 1057251672U, 1028498358U, 999877854U, - 971388940U, 943030410U, 914801076U, 886699767U, 858725327U, 830876614U, - 803152505U, 775551890U, 748073672U, 720716771U, 693480120U, 666362667U, - 639363374U, 612481215U, 585715177U, 559064263U, 532527486U, 506103872U, - 479792461U, 453592303U, 427502463U, 401522014U, 375650043U, 349885648U, - 324227938U, 298676034U, 273229066U, 247886176U, 222646516U, 197509248U, - 172473545U, 147538590U, 122703574U, 97967701U, 73330182U, 48790236U, - 24347096U, 0U - -#if 0 - /* The following are the values for 16-bit tables - these work fine for the - * 8-bit conversions but produce very slightly larger errors in the 16-bit - * log (about 1.2 as opposed to 0.7 absolute error in the final value). To - * use these all the shifts below must be adjusted appropriately. - */ - 65166, 64430, 63700, 62976, 62257, 61543, 60835, 60132, 59434, 58741, 58054, - 57371, 56693, 56020, 55352, 54689, 54030, 53375, 52726, 52080, 51439, 50803, - 50170, 49542, 48918, 48298, 47682, 47070, 46462, 45858, 45257, 44661, 44068, - 43479, 42894, 42312, 41733, 41159, 40587, 40020, 39455, 38894, 38336, 37782, - 37230, 36682, 36137, 35595, 35057, 34521, 33988, 33459, 32932, 32408, 31887, - 31369, 30854, 30341, 29832, 29325, 28820, 28319, 27820, 27324, 26830, 26339, - 25850, 25364, 24880, 24399, 23920, 23444, 22970, 22499, 22029, 21562, 21098, - 20636, 20175, 19718, 19262, 18808, 18357, 17908, 17461, 17016, 16573, 16132, - 15694, 15257, 14822, 14390, 13959, 13530, 13103, 12678, 12255, 11834, 11415, - 10997, 10582, 10168, 9756, 9346, 8937, 8531, 8126, 7723, 7321, 6921, 6523, - 6127, 5732, 5339, 4947, 4557, 4169, 3782, 3397, 3014, 2632, 2251, 1872, 1495, - 1119, 744, 372 -#endif -}; - -static png_int_32 -png_log8bit(unsigned int x) -{ - unsigned int lg2 = 0; - /* Each time 'x' is multiplied by 2, 1 must be subtracted off the final log, - * because the log is actually negate that means adding 1. The final - * returned value thus has the range 0 (for 255 input) to 7.994 (for 1 - * input), return -1 for the overflow (log 0) case, - so the result is - * always at most 19 bits. - */ - if ((x &= 0xff) == 0) - return -1; - - if ((x & 0xf0) == 0) - lg2 = 4, x <<= 4; - - if ((x & 0xc0) == 0) - lg2 += 2, x <<= 2; - - if ((x & 0x80) == 0) - lg2 += 1, x <<= 1; - - /* result is at most 19 bits, so this cast is safe: */ - return (png_int_32)((lg2 << 16) + ((png_8bit_l2[x-128]+32768)>>16)); -} - -/* The above gives exact (to 16 binary places) log2 values for 8-bit images, - * for 16-bit images we use the most significant 8 bits of the 16-bit value to - * get an approximation then multiply the approximation by a correction factor - * determined by the remaining up to 8 bits. This requires an additional step - * in the 16-bit case. - * - * We want log2(value/65535), we have log2(v'/255), where: - * - * value = v' * 256 + v'' - * = v' * f - * - * So f is value/v', which is equal to (256+v''/v') since v' is in the range 128 - * to 255 and v'' is in the range 0 to 255 f will be in the range 256 to less - * than 258. The final factor also needs to correct for the fact that our 8-bit - * value is scaled by 255, whereas the 16-bit values must be scaled by 65535. - * - * This gives a final formula using a calculated value 'x' which is value/v' and - * scaling by 65536 to match the above table: - * - * log2(x/257) * 65536 - * - * Since these numbers are so close to '1' we can use simple linear - * interpolation between the two end values 256/257 (result -368.61) and 258/257 - * (result 367.179). The values used below are scaled by a further 64 to give - * 16-bit precision in the interpolation: - * - * Start (256): -23591 - * Zero (257): 0 - * End (258): 23499 - */ -static png_int_32 -png_log16bit(png_uint_32 x) -{ - unsigned int lg2 = 0; - - /* As above, but now the input has 16 bits. */ - if ((x &= 0xffff) == 0) - return -1; - - if ((x & 0xff00) == 0) - lg2 = 8, x <<= 8; - - if ((x & 0xf000) == 0) - lg2 += 4, x <<= 4; - - if ((x & 0xc000) == 0) - lg2 += 2, x <<= 2; - - if ((x & 0x8000) == 0) - lg2 += 1, x <<= 1; - - /* Calculate the base logarithm from the top 8 bits as a 28-bit fractional - * value. - */ - lg2 <<= 28; - lg2 += (png_8bit_l2[(x>>8)-128]+8) >> 4; - - /* Now we need to interpolate the factor, this requires a division by the top - * 8 bits. Do this with maximum precision. - */ - x = ((x << 16) + (x >> 9)) / (x >> 8); - - /* Since we divided by the top 8 bits of 'x' there will be a '1' at 1<<24, - * the value at 1<<16 (ignoring this) will be 0 or 1; this gives us exactly - * 16 bits to interpolate to get the low bits of the result. Round the - * answer. Note that the end point values are scaled by 64 to retain overall - * precision and that 'lg2' is current scaled by an extra 12 bits, so adjust - * the overall scaling by 6-12. Round at every step. - */ - x -= 1U << 24; - - if (x <= 65536U) /* <= '257' */ - lg2 += ((23591U * (65536U-x)) + (1U << (16+6-12-1))) >> (16+6-12); - - else - lg2 -= ((23499U * (x-65536U)) + (1U << (16+6-12-1))) >> (16+6-12); - - /* Safe, because the result can't have more than 20 bits: */ - return (png_int_32)((lg2 + 2048) >> 12); -} - -/* The 'exp()' case must invert the above, taking a 20-bit fixed point - * logarithmic value and returning a 16 or 8-bit number as appropriate. In - * each case only the low 16 bits are relevant - the fraction - since the - * integer bits (the top 4) simply determine a shift. - * - * The worst case is the 16-bit distinction between 65535 and 65534, this - * requires perhaps spurious accuracty in the decoding of the logarithm to - * distinguish log2(65535/65534.5) - 10^-5 or 17 bits. There is little chance - * of getting this accuracy in practice. - * - * To deal with this the following exp() function works out the exponent of the - * frational part of the logarithm by using an accurate 32-bit value from the - * top four fractional bits then multiplying in the remaining bits. - */ -static const png_uint_32 -png_32bit_exp[16] = -{ - /* NOTE: the first entry is deliberately set to the maximum 32-bit value. */ - 4294967295U, 4112874773U, 3938502376U, 3771522796U, 3611622603U, 3458501653U, - 3311872529U, 3171459999U, 3037000500U, 2908241642U, 2784941738U, 2666869345U, - 2553802834U, 2445529972U, 2341847524U, 2242560872U -}; - -/* Adjustment table; provided to explain the numbers in the code below. */ -#if 0 -for (i=11;i>=0;--i){ print i, " ", (1 - e(-(2^i)/65536*l(2))) * 2^(32-i), "\n"} - 11 44937.64284865548751208448 - 10 45180.98734845585101160448 - 9 45303.31936980687359311872 - 8 45364.65110595323018870784 - 7 45395.35850361789624614912 - 6 45410.72259715102037508096 - 5 45418.40724413220722311168 - 4 45422.25021786898173001728 - 3 45424.17186732298419044352 - 2 45425.13273269940811464704 - 1 45425.61317555035558641664 - 0 45425.85339951654943850496 -#endif - -static png_uint_32 -png_exp(png_fixed_point x) -{ - if (x > 0 && x <= 0xfffff) /* Else overflow or zero (underflow) */ + if (png_ptr != NULL) { - /* Obtain a 4-bit approximation */ - png_uint_32 e = png_32bit_exp[(x >> 12) & 0xf]; + int handle_error = (setting & PNG_SF_ERROR) != 0U; - /* Incorporate the low 12 bits - these decrease the returned value by - * multiplying by a number less than 1 if the bit is set. The multiplier - * is determined by the above table and the shift. Notice that the values - * converge on 45426 and this is used to allow linear interpolation of the - * low bits. + setting &= ~PNG_SF_ERROR; /* because it is handled below. */ + + switch (setting & (PNG_SF_READ|PNG_SF_WRITE)) + { +# ifdef PNG_READ_SUPPORTED + case PNG_SF_READ: + if (png_ptr->read_struct) + result = png_read_setting(png_ptr, setting, parameter, value); + + else + result = PNG_EINVAL; /* read setting on write struct */ + break; +# endif /* READ */ + +# ifdef PNG_WRITE_SUPPORTED + case PNG_SF_WRITE: + if (!png_ptr->read_struct) + result = png_write_setting(png_ptr, setting, parameter, + value); + + else + result = PNG_EINVAL; /* write setting on read struct */ + break; +# endif /* WRITE */ + + default: + /* Handle everything else here. This includes the error of not + * having either read or write set; that error will cause a + * PNG_ENOSYS return code. + */ + switch (setting) + { +# ifdef PNG_SET_OPTION_SUPPORTED + case PNG_SRW_OPTION: + if (parameter >= PNG_OPTION_NEXT) + return PNG_OPTION_INVALID; + + if (parameter == PNG_SKIP_sRGB_CHECK_PROFILE) + { + if (png_ptr->skip_sRGB_profile_check) + { + if (!value) + png_ptr->skip_sRGB_profile_check = 0U; + result = PNG_OPTION_ON; + } + + else + { + if (value) + png_ptr->skip_sRGB_profile_check = 1U; + result = PNG_OPTION_OFF; + } + + break; + } + +# ifdef PNG_READ_SUPPORTED + if (png_ptr->read_struct) + { + result = png_read_setting(png_ptr, setting, + parameter, value); + break; + } +# endif /* READ */ + + /* No write options at present */ + result = PNG_OPTION_UNSET; /* i.e. ignore it */ + break; +# endif /* SET_OPTION */ + + default: + /* Any other option; handle in the appropriate setting: */ +# ifdef PNG_READ_SUPPORTED + if (png_ptr->read_struct) + { + result = png_read_setting(png_ptr, setting, + parameter, value); + break; + } +# endif /* READ */ + +# ifdef PNG_WRITE_SUPPORTED + if (!png_ptr->read_struct) + { + result = png_write_setting(png_ptr, setting, + parameter, value); + break; + } +# endif /* WRITE */ + + NOT_REACHED; + result= PNG_ENOSYS; + break; + } + break; + } /* switch */ + + /* Handle error returns here. + * TODO: this is crude, should use a formatted warning style message and + * output result/setting/parameter/value. */ - if (x & 0x800) - e -= (((e >> 16) * 44938U) + 16U) >> 5; - - if (x & 0x400) - e -= (((e >> 16) * 45181U) + 32U) >> 6; - - if (x & 0x200) - e -= (((e >> 16) * 45303U) + 64U) >> 7; - - if (x & 0x100) - e -= (((e >> 16) * 45365U) + 128U) >> 8; - - if (x & 0x080) - e -= (((e >> 16) * 45395U) + 256U) >> 9; - - if (x & 0x040) - e -= (((e >> 16) * 45410U) + 512U) >> 10; - - /* And handle the low 6 bits in a single block. */ - e -= (((e >> 16) * 355U * (x & 0x3fU)) + 256U) >> 9; - - /* Handle the upper bits of x. */ - e >>= x >> 16; - return e; + if (handle_error && PNG_FAILED(result)) + png_error(png_ptr, "png_setting"); } - /* Check for overflow */ - if (x <= 0) - return png_32bit_exp[0]; + else /* png_ptr is NULL */ + result = PNG_EINVAL; - /* Else underflow */ - return 0; + return result; + PNG_UNUSED(parameter) + PNG_UNUSED(value) } -static png_byte -png_exp8bit(png_fixed_point lg2) -{ - /* Get a 32-bit value: */ - png_uint_32 x = png_exp(lg2); - - /* Convert the 32-bit value to 0..255 by multiplying by 256-1, note that the - * second, rounding, step can't overflow because of the first, subtraction, - * step. - */ - x -= x >> 8; - return (png_byte)((x + 0x7fffffU) >> 24); -} - -static png_uint_16 -png_exp16bit(png_fixed_point lg2) -{ - /* Get a 32-bit value: */ - png_uint_32 x = png_exp(lg2); - - /* Convert the 32-bit value to 0..65535 by multiplying by 65536-1: */ - x -= x >> 16; - return (png_uint_16)((x + 32767U) >> 16); -} -#endif /* FLOATING_ARITHMETIC */ - -png_byte -png_gamma_8bit_correct(unsigned int value, png_fixed_point gamma_val) -{ - if (value > 0 && value < 255) - { -# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED - double r = floor(255*pow(value/255.,gamma_val*.00001)+.5); - return (png_byte)r; -# else - png_int_32 lg2 = png_log8bit(value); - png_fixed_point res; - - if (png_muldiv(&res, gamma_val, lg2, PNG_FP_1)) - return png_exp8bit(res); - - /* Overflow. */ - value = 0; -# endif - } - - return (png_byte)value; -} - -png_uint_16 -png_gamma_16bit_correct(unsigned int value, png_fixed_point gamma_val) -{ - if (value > 0 && value < 65535) - { -# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED - double r = floor(65535*pow(value/65535.,gamma_val*.00001)+.5); - return (png_uint_16)r; -# else - png_int_32 lg2 = png_log16bit(value); - png_fixed_point res; - - if (png_muldiv(&res, gamma_val, lg2, PNG_FP_1)) - return png_exp16bit(res); - - /* Overflow. */ - value = 0; -# endif - } - - return (png_uint_16)value; -} - -/* This does the right thing based on the bit_depth field of the - * png_struct, interpreting values as 8-bit or 16-bit. While the result - * is nominally a 16-bit value if bit depth is 8 then the result is - * 8-bit (as are the arguments.) - */ -png_uint_16 /* PRIVATE */ -png_gamma_correct(png_structrp png_ptr, unsigned int value, - png_fixed_point gamma_val) -{ - if (png_ptr->bit_depth == 8) - return png_gamma_8bit_correct(value, gamma_val); - - else - return png_gamma_16bit_correct(value, gamma_val); -} - -#define PNG_GAMMA_TABLE_8 0 /* 8-bit entries in png_byte */ -#define PNG_GAMMA_TABLE_8_IN_16 1 /* 8-bit entries * 257 in png_uint_16 */ -#define PNG_GAMMA_TABLE_16 2 /* 16-bit entries in png_uint_16 */ - -typedef struct -{ - png_fixed_point gamma; - png_uint_32 mult; - unsigned int add; - unsigned int shift; /* input value is (i * mult + add) >> shift */ - int output; /* One of the above values */ - int adjust; /* Divide or multiple output by 257 */ - png_voidp table; /* Lookup table */ -} gamma_table_data; - -static unsigned int -write_gamma_table_entry(const gamma_table_data *data, png_uint_32 i) - /* Calculate and write a single entry into table[i], the value of the entry - * written is returned. - */ -{ - png_uint_32 in = (i * data->mult + data->add) >> data->shift; - unsigned int out; - - /* If the output is TABLE_8 with no adjust, or the output is not with an - * adjust, use 8-bit correction. - */ - if ((data->output == PNG_GAMMA_TABLE_8) != (data->adjust != 0)) - { - out = png_gamma_8bit_correct((unsigned int)in, data->gamma); - - if (data->adjust != 0) - out *= 257U; - } - - else /* 16-bit correction */ - { - out = png_gamma_16bit_correct((unsigned int)in, data->gamma); - - if (data->adjust != 0) - out = PNG_DIV257(out); - } - - if (data->output == PNG_GAMMA_TABLE_8) - ((png_bytep)data->table)[i] = (png_byte)out; - - else - ((png_uint_16p)data->table)[i] = (png_uint_16)out; - - return out; -} - -static void -write_gamma_table(const gamma_table_data *data, png_uint_32 lo, - unsigned int loval, png_uint_32 hi, unsigned int hival) - /* Fill in gamma table entries between lo and hi, exclusive. The entries at - * table[lo] and table[hi] have already been written, the intervening entries - * are written. - */ -{ - if (hi > lo+1) /* Else nothing to fill in */ - { - if (hival == loval) - { - /* All intervening entries must be the same. */ - if (data->output == PNG_GAMMA_TABLE_8) - { - png_bytep table8 = ((png_bytep)data->table); - - while (++lo < hi) - table8[lo] = (png_byte)loval; - } - - else - { - png_uint_16p table16 = ((png_uint_16p)data->table); - - while (++lo < hi) - table16[lo] = (png_uint_16)loval; - } - } - - else - { - png_uint_32 mid = (lo+hi) >> 1; - unsigned int midval = write_gamma_table_entry(data, mid); - - /* The algorithm used is to divide the entries to be written in half - * and fill in the middle. For all practical tables with significant - * gamma this will result in a performance gain because the expensive - * gamma correction arithmetic is avoided for some entries. - */ - write_gamma_table(data, lo, loval, mid, midval); - write_gamma_table(data, mid, midval, hi, hival); - } - } -} - -static void * -png_build_gamma_table(png_structrp png_ptr, png_fixed_point gamma_val, - int output/*as above*/, int input_depth, int use_shift) - /* Build a gamma lookup table to encode input_depth bit input values. - * The table will have 2^input_depth entries plus an extra one if use_shift - * is specified. With shift the table is accessed: - * - * table[(original-value + rounding) >> shift] - * - * And an extra entry exists to accomodate overflow of original-value on - * rounding. If use_shift is not specified the table is accessed with an - * input_depth bit value and the original values must have been correctly - * scaled to this range (not using a shift!) - * - * Each table entry contains input-value^gamma_val rounded to the output - * precision. This is 8 bit precision unless output is specified as - * PNG_GAMMA_TABLE_16, in which case it is 16-bit precision. For - * PNG_GAMMA_TABLE_8_IN_16 the 8-bit value is scaled to 16-bits by - * multiplying by 257. - */ -{ - png_uint_32 size; - unsigned int hival; - gamma_table_data data; - - /* If use_shift is true or if the input or output is not 8-bit the gamma - * correction will use the 16-bit correction code. This requires a value in - * the range 0..65535. For use_shift the value is simply: - * - * input << shift - * - * For the scaling case the value is: - * - * round(input * 65535 / ((1<> shift; - * - * With 'mult' and 'add' chosen to minimize the error for all input values - * in the range 0..((1< PNG_GAMMA_TABLE_8; - - if (use_shift != 0) - { - /* The multiplier does the shift: */ - data.mult = 1U << (8-input_depth); - data.add = 0; - data.shift = 0; - if (input_depth < 8) ++size; - } - - else - { - data.mult = multadd255[input_depth-1].mult; - data.add = multadd255[input_depth-1].add; - data.shift = multadd255[input_depth-1].shift; - } - } - - else - { - /* 16-bit correction is used for cases where input or output require more - * than 8 bits. - */ - data.adjust = output == PNG_GAMMA_TABLE_8; - - if (use_shift != 0) - { - data.mult = 1U << (16-input_depth); - data.add = 0; - data.shift = 0; - if (input_depth < 16) ++size; - } - - else - { - data.mult = multadd65535[input_depth-1].mult; - data.add = multadd65535[input_depth-1].add; - data.shift = multadd65535[input_depth-1].shift; - } - } - - if (output == PNG_GAMMA_TABLE_8) - { - data.table = png_malloc(png_ptr, size * sizeof (png_byte)); - ((png_bytep)data.table)[0] = 0; - hival = ((png_bytep)data.table)[size-1] = 255; - } - - else - { - /* Output is 16 bits, although it may only have 8 bits of precision */ - data.table = png_malloc(png_ptr, size * sizeof (png_uint_16)); - ((png_uint_16p)data.table)[0] = 0; - hival = ((png_uint_16p)data.table)[size-1] = 65535; - } - - if (png_gamma_significant(gamma_val)) - write_gamma_table(&data, 0, 0, size-1, hival); - - else /* gamma_val not significant */ - { - if (output == PNG_GAMMA_TABLE_8) - { - png_uint_32 i; - png_bytep table8 = ((png_bytep)data.table); - - if (data.adjust) - for (i=1; i> - data.shift); - - else - for (i=1; i> data.shift); - } - - else - { - png_uint_32 i; - png_uint_16p table16 = ((png_uint_16p)data.table); - - if (data.adjust) - for (i=1; i> - data.shift) * 257U); - - else - for (i=1; i> - data.shift); - } - } - - return data.table; -} - -/* Used from png_read_destroy and below to release the memory used by the gamma - * tables. - */ -void /* PRIVATE */ -png_destroy_gamma_table(png_structrp png_ptr) -{ - png_free(png_ptr, png_ptr->gamma_table); - png_ptr->gamma_table = NULL; - - png_free(png_ptr, png_ptr->gamma_16_table); - png_ptr->gamma_16_table = NULL; - -#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ - defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) - png_free(png_ptr, png_ptr->gamma_from_1); - png_ptr->gamma_from_1 = NULL; - png_free(png_ptr, png_ptr->gamma_to_1); - png_ptr->gamma_to_1 = NULL; - - png_free(png_ptr, png_ptr->gamma_16_from_1); - png_ptr->gamma_16_from_1 = NULL; - png_free(png_ptr, png_ptr->gamma_16_to_1); - png_ptr->gamma_16_to_1 = NULL; -#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ -} - -/* We build the 8- or 16-bit gamma tables here. Note that for 16-bit - * tables, we don't make a full table if we are reducing to 8-bit in - * the future. Note also how the gamma_16 tables are segmented so that - * we don't need to allocate > 64K chunks for a full 16-bit table. - */ -void /* PRIVATE */ -png_build_gamma_tables(png_structrp png_ptr, int bit_depth) -{ - png_debug(1, "in png_build_gamma_table"); - - /* Remove any existing table; this copes with multiple calls to - * png_read_update_info. The warning is because building the gamma tables - * multiple times is a performance hit - it's harmless but the ability to call - * png_read_update_info() multiple times is new in 1.5.6 so it seems sensible - * to warn if the app introduces such a hit. - */ - if (png_ptr->gamma_table != NULL || png_ptr->gamma_16_table != NULL) - { - png_warning(png_ptr, "gamma table being rebuilt"); - png_destroy_gamma_table(png_ptr); - } - - if (bit_depth <= 8) - { - png_ptr->gamma_table = png_voidcast(png_bytep, png_build_gamma_table( - png_ptr, png_ptr->screen_gamma > 0 ? - png_reciprocal2(png_ptr->colorspace.gamma, png_ptr->screen_gamma) : - PNG_FP_1, PNG_GAMMA_TABLE_8, 8/*input depth*/, 0/*scale*/)); - -#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ - defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) - if (png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) - { - /* This sets the accuracy of 8-bit composition and the 8-bit RGB to gray - * conversion - PNG_MAX_GAMMA_8 (the number of bits in the sixteen bit - * value that are considered significant.) - */ - png_ptr->gamma_to_1 = png_voidcast(png_uint_16p, png_build_gamma_table( - png_ptr, png_reciprocal(png_ptr->colorspace.gamma), - PNG_GAMMA_TABLE_16, 8/*input depth*/, 0/*scale*/)); - - png_ptr->gamma_from_1 = png_voidcast(png_bytep, png_build_gamma_table( - png_ptr, png_ptr->screen_gamma > 0 ? - png_reciprocal(png_ptr->screen_gamma) : - png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */, - PNG_GAMMA_TABLE_8, PNG_MAX_GAMMA_8/*input depth*/, 1/*shift*/)); - - png_ptr->gamma_shift = 16-PNG_MAX_GAMMA_8; - } -#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ - } - else - { - png_byte shift, sig_bit; - int table_type; - -# ifdef PNG_16BIT_SUPPORTED - table_type = PNG_GAMMA_TABLE_16; -# else - table_type = PNG_GAMMA_TABLE_8_IN_16; -# endif - - if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) - { - sig_bit = png_ptr->sig_bit.red; - - if (png_ptr->sig_bit.green > sig_bit) - sig_bit = png_ptr->sig_bit.green; - - if (png_ptr->sig_bit.blue > sig_bit) - sig_bit = png_ptr->sig_bit.blue; - } - else - sig_bit = png_ptr->sig_bit.gray; - - if (sig_bit > 0 && sig_bit < 16U) - shift = (png_byte)(16U - sig_bit); /* shift == insignificant bits */ - - else - shift = 0; /* keep all 16 bits */ - - if (png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8)) - { - /* PNG_MAX_GAMMA_8 is the number of bits to keep - effectively - * the significant bits in the *input* when the output will - * eventually be 8 bits. - */ - if (shift < (16U - PNG_MAX_GAMMA_8)) - shift = (16U - PNG_MAX_GAMMA_8); - - table_type = PNG_GAMMA_TABLE_8_IN_16; - } - - png_ptr->gamma_shift = shift; - - png_ptr->gamma_16_table = png_voidcast(png_uint_16p, png_build_gamma_table( - png_ptr, png_ptr->screen_gamma > 0 ? png_reciprocal2( - png_ptr->colorspace.gamma, png_ptr->screen_gamma) : PNG_FP_1, - table_type, (16-shift)/*input depth*/, 1/*shift*/)); - -#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ - defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) - if (png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) - { - png_ptr->gamma_16_to_1 = png_voidcast(png_uint_16p, - png_build_gamma_table(png_ptr, - png_reciprocal(png_ptr->colorspace.gamma), PNG_GAMMA_TABLE_16, - (16-shift)/*input depth*/, 1/*shift*/)); - - /* Notice that the '16 from 1' table should be full precision, however - * the lookup on this table still uses gamma_shift, so it can't be. - * TODO: fix this. - */ - png_ptr->gamma_16_from_1 = png_voidcast(png_uint_16p, - png_build_gamma_table(png_ptr, png_ptr->screen_gamma > 0 ? - png_reciprocal(png_ptr->screen_gamma) : - png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */, - PNG_GAMMA_TABLE_16, (16-shift)/*input depth*/, 1/*shift*/)); - } -#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ - } -} -#endif /* READ_GAMMA */ - -/* HARDWARE OPTION SUPPORT */ -#ifdef PNG_SET_OPTION_SUPPORTED -int PNGAPI -png_set_option(png_structrp png_ptr, int option, int onoff) -{ - if (png_ptr != NULL && option >= 0 && option < PNG_OPTION_NEXT && - (option & 1) == 0) - { - int mask = 3 << option; - int setting = (2 + (onoff != 0)) << option; - int current = png_ptr->options; - - png_ptr->options = (png_byte)((current & ~mask) | setting); - - return (current & mask) >> option; - } - - return PNG_OPTION_INVALID; -} -#endif - /* sRGB support */ #if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\ defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) /* sRGB conversion tables; these are machine generated with the code in * contrib/tools/makesRGB.c. The actual sRGB transfer curve defined in the - * specification (see the article at http://en.wikipedia.org/wiki/SRGB) + * specification (see the article at https://en.wikipedia.org/wiki/SRGB) * is used, not the gamma=1/2.2 approximation use elsewhere in libpng. - * The sRGB to linear table is exact (to the nearest 16 bit linear fraction). + * The sRGB to linear table is exact (to the nearest 16-bit linear fraction). * The inverse (linear to sRGB) table has accuracies as follows: * * For all possible (255*65535+1) input values: @@ -4165,7 +3509,7 @@ png_set_option(png_structrp png_ptr, int option, int onoff) * * error: -0.513727 - 0.607759, 308 (0.469978%) of readings inexact * - * In all cases the inexact readings are off by one. + * In all cases the inexact readings are only off by one. */ #ifdef PNG_SIMPLIFIED_READ_SUPPORTED @@ -4206,7 +3550,7 @@ const png_uint_16 png_sRGB_table[256] = 61517,62082,62650,63221,63795,64372,64952,65535 }; -#endif /* simplified read only */ +#endif /* SIMPLIFIED_READ */ /* The base/delta tables are required for both read and write (but currently * only the simplified versions.) @@ -4334,15 +3678,15 @@ png_image_free_function(png_voidp argument) /* First free any data held in the control structure. */ # ifdef PNG_STDIO_SUPPORTED - if (cp->owned_file) + if (cp->owned_file != 0) { - FILE *fp = png_voidcast(FILE*, cp->png_ptr->io_ptr); + FILE *fp = png_voidcast(FILE*, png_get_io_ptr(cp->png_ptr)); cp->owned_file = 0; /* Ignore errors here. */ if (fp != NULL) { - cp->png_ptr->io_ptr = NULL; + png_init_io(cp->png_ptr, NULL); (void)fclose(fp); } } @@ -4358,7 +3702,7 @@ png_image_free_function(png_voidp argument) png_free(c.png_ptr, cp); /* Then the structures, calling the correct API. */ - if (c.for_write) + if (c.for_write != 0) { # ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED png_destroy_write_struct(&c.png_ptr, &c.info_ptr); @@ -4405,5 +3749,34 @@ png_image_error(png_imagep image, png_const_charp error_message) return 0; } +#ifdef PNG_STDIO_SUPPORTED +typedef struct +{ + png_structrp png_ptr; + png_FILE_p fp; +} +png_image_init_io_struct; + +static int +image_init_io(png_voidp display) +{ + png_image_init_io_struct *p = + png_voidcast(png_image_init_io_struct*, display); + + png_init_io(p->png_ptr, p->fp); + return 1; +} + +int /* PRIVATE */ +png_image_init_io(png_imagep image, png_FILE_p fp) +{ + png_image_init_io_struct s; + + s.png_ptr = image->opaque->png_ptr; + s.fp = fp; + + return png_safe_execute(image, image_init_io, &s); +} +#endif /* STDIO */ #endif /* SIMPLIFIED READ/WRITE */ -#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ +#endif /* READ || WRITE */ diff --git a/ext/libpng17/png.h b/ext/libpng17/png.h index 775fe83a62..3feecd4c41 100644 --- a/ext/libpng17/png.h +++ b/ext/libpng17/png.h @@ -1,8 +1,9 @@ /* png.h - header file for PNG reference library * - * libpng version 1.7.0beta35 - March 17, 2014 - * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * libpng version 1.7.0beta90, August 28, 2017 + * + * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -10,17 +11,161 @@ * * Authors and maintainers: * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat - * libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger - * libpng versions 0.97, January 1998, through 1.7.0beta35 - March 17, 2014: Glenn + * libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.7.0beta90, August 28, 2017: + * Glenn Randers-Pehrson. * See also "Contributing Authors", below. + */ + +/* + * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE: * - * Note about libpng version numbers: + * If you modify libpng you may insert additional notices immediately following + * this sentence. * - * Due to various miscommunications, unforeseen code incompatibilities - * and occasional factors outside the authors' control, version numbering - * on the library has not always been consistent and straightforward. - * The following table summarizes matters since version 0.89c, which was - * the first widely used release: + * This code is released under the libpng license. + * + * libpng versions 1.0.7, July 1, 2000 through 1.7.0beta90, August 28, 2017 are + * Copyright (c) 2000-2002, 2004, 2006-2017 Glenn Randers-Pehrson, are + * derived from libpng-1.0.6, and are distributed according to the same + * disclaimer and license as libpng-1.0.6 with the following individuals + * added to the list of Contributing Authors: + * + * Simon-Pierre Cadieux + * Eric S. Raymond + * Mans Rullgard + * Cosmin Truta + * Gilles Vollant + * James Yu + * + * and with the following additions to the disclaimer: + * + * There is no warranty against interference with your enjoyment of the + * library or against infringement. There is no warranty that our + * efforts or the library will fulfill any of your particular purposes + * or needs. This library is provided with all faults, and the entire + * risk of satisfactory quality, performance, accuracy, and effort is with + * the user. + * + * Some files in the "contrib" directory and some configure-generated + * files that are distributed with libpng have other copyright owners and + * are released under other open source licenses. + * + * libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are + * Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from + * libpng-0.96, and are distributed according to the same disclaimer and + * license as libpng-0.96, with the following individuals added to the list + * of Contributing Authors: + * + * Tom Lane + * Glenn Randers-Pehrson + * Willem van Schaik + * + * libpng versions 0.89, June 1996, through 0.96, May 1997, are + * Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88, + * and are distributed according to the same disclaimer and license as + * libpng-0.88, with the following individuals added to the list of + * Contributing Authors: + * + * John Bowler + * Kevin Bracey + * Sam Bushell + * Magnus Holmgren + * Greg Roelofs + * Tom Tanner + * + * Some files in the "scripts" directory have other copyright owners + * but are released under this license. + * + * libpng versions 0.5, May 1995, through 0.88, January 1996, are + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * For the purposes of this copyright and license, "Contributing Authors" + * is defined as the following set of individuals: + * + * Andreas Dilger + * Dave Martindale + * Guy Eric Schalnat + * Paul Schmidt + * Tim Wegner + * + * The PNG Reference Library is supplied "AS IS". The Contributing Authors + * and Group 42, Inc. disclaim all warranties, expressed or implied, + * including, without limitation, the warranties of merchantability and of + * fitness for any purpose. The Contributing Authors and Group 42, Inc. + * assume no liability for direct, indirect, incidental, special, exemplary, + * or consequential damages, which may result from the use of the PNG + * Reference Library, even if advised of the possibility of such damage. + * + * Permission is hereby granted to use, copy, modify, and distribute this + * source code, or portions hereof, for any purpose, without fee, subject + * to the following restrictions: + * + * 1. The origin of this source code must not be misrepresented. + * + * 2. Altered versions must be plainly marked as such and must not + * be misrepresented as being the original source. + * + * 3. This Copyright notice may not be removed or altered from any + * source or altered source distribution. + * + * The Contributing Authors and Group 42, Inc. specifically permit, without + * fee, and encourage the use of this source code as a component to + * supporting the PNG file format in commercial products. If you use this + * source code in a product, acknowledgment is not required but would be + * appreciated. + * + * END OF COPYRIGHT NOTICE, DISCLAIMER, and LICENSE. + * + * TRADEMARK: + * + * The name "libpng" has not been registered by the Copyright owner + * as a trademark in any jurisdiction. However, because libpng has + * been distributed and maintained world-wide, continually since 1995, + * the Copyright owner claims "common-law trademark protection" in any + * jurisdiction where common-law trademark is recognized. + * + * OSI CERTIFICATION: + * + * Libpng is OSI Certified Open Source Software. OSI Certified Open Source is + * a certification mark of the Open Source Initiative. OSI has not addressed + * the additional disclaimers inserted at version 1.0.7. + * + * EXPORT CONTROL: + * + * The Copyright owner believes that the Export Control Classification + * Number (ECCN) for libpng is EAR99, which means not subject to export + * controls or International Traffic in Arms Regulations (ITAR) because + * it is open source, publicly available software, that does not contain + * any encryption software. See the EAR, paragraphs 734.3(b)(3) and + * 734.7(b). + */ + +/* + * A "png_get_copyright" function is available, for convenient use in "about" + * boxes and the like: + * + * printf("%s", png_get_copyright(NULL)); + * + * Also, the PNG logo (in PNG format, of course) is supplied in the + * files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). + */ + +/* + * The contributing authors would like to thank all those who helped + * with testing, bug fixes, and patience. This wouldn't have been + * possible without all of you. + * + * Thanks to Frank J. T. Wojcik for helping with the documentation. + */ + +/* Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: * * source png.h png.h shared-lib * version string int version @@ -58,267 +203,51 @@ * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) * 1.0.7 1 10007 (still compatible) - * 1.0.8beta1-4 1 10008 2.1.0.8beta1-4 - * 1.0.8rc1 1 10008 2.1.0.8rc1 - * 1.0.8 1 10008 2.1.0.8 - * 1.0.9beta1-6 1 10009 2.1.0.9beta1-6 - * 1.0.9rc1 1 10009 2.1.0.9rc1 - * 1.0.9beta7-10 1 10009 2.1.0.9beta7-10 - * 1.0.9rc2 1 10009 2.1.0.9rc2 - * 1.0.9 1 10009 2.1.0.9 - * 1.0.10beta1 1 10010 2.1.0.10beta1 - * 1.0.10rc1 1 10010 2.1.0.10rc1 - * 1.0.10 1 10010 2.1.0.10 - * 1.0.11beta1-3 1 10011 2.1.0.11beta1-3 - * 1.0.11rc1 1 10011 2.1.0.11rc1 - * 1.0.11 1 10011 2.1.0.11 - * 1.0.12beta1-2 2 10012 2.1.0.12beta1-2 - * 1.0.12rc1 2 10012 2.1.0.12rc1 - * 1.0.12 2 10012 2.1.0.12 - * 1.1.0a-f - 10100 2.1.1.0a-f (branch abandoned) - * 1.2.0beta1-2 2 10200 2.1.2.0beta1-2 - * 1.2.0beta3-5 3 10200 3.1.2.0beta3-5 - * 1.2.0rc1 3 10200 3.1.2.0rc1 - * 1.2.0 3 10200 3.1.2.0 - * 1.2.1beta1-4 3 10201 3.1.2.1beta1-4 - * 1.2.1rc1-2 3 10201 3.1.2.1rc1-2 - * 1.2.1 3 10201 3.1.2.1 - * 1.2.2beta1-6 12 10202 12.so.0.1.2.2beta1-6 - * 1.0.13beta1 10 10013 10.so.0.1.0.13beta1 - * 1.0.13rc1 10 10013 10.so.0.1.0.13rc1 - * 1.2.2rc1 12 10202 12.so.0.1.2.2rc1 - * 1.0.13 10 10013 10.so.0.1.0.13 - * 1.2.2 12 10202 12.so.0.1.2.2 - * 1.2.3rc1-6 12 10203 12.so.0.1.2.3rc1-6 - * 1.2.3 12 10203 12.so.0.1.2.3 - * 1.2.4beta1-3 13 10204 12.so.0.1.2.4beta1-3 - * 1.0.14rc1 13 10014 10.so.0.1.0.14rc1 - * 1.2.4rc1 13 10204 12.so.0.1.2.4rc1 - * 1.0.14 10 10014 10.so.0.1.0.14 - * 1.2.4 13 10204 12.so.0.1.2.4 - * 1.2.5beta1-2 13 10205 12.so.0.1.2.5beta1-2 - * 1.0.15rc1-3 10 10015 10.so.0.1.0.15rc1-3 - * 1.2.5rc1-3 13 10205 12.so.0.1.2.5rc1-3 - * 1.0.15 10 10015 10.so.0.1.0.15 - * 1.2.5 13 10205 12.so.0.1.2.5 - * 1.2.6beta1-4 13 10206 12.so.0.1.2.6beta1-4 - * 1.0.16 10 10016 10.so.0.1.0.16 - * 1.2.6 13 10206 12.so.0.1.2.6 - * 1.2.7beta1-2 13 10207 12.so.0.1.2.7beta1-2 - * 1.0.17rc1 10 10017 12.so.0.1.0.17rc1 - * 1.2.7rc1 13 10207 12.so.0.1.2.7rc1 - * 1.0.17 10 10017 12.so.0.1.0.17 - * 1.2.7 13 10207 12.so.0.1.2.7 - * 1.2.8beta1-5 13 10208 12.so.0.1.2.8beta1-5 - * 1.0.18rc1-5 10 10018 12.so.0.1.0.18rc1-5 - * 1.2.8rc1-5 13 10208 12.so.0.1.2.8rc1-5 - * 1.0.18 10 10018 12.so.0.1.0.18 - * 1.2.8 13 10208 12.so.0.1.2.8 - * 1.2.9beta1-3 13 10209 12.so.0.1.2.9beta1-3 - * 1.2.9beta4-11 13 10209 12.so.0.9[.0] - * 1.2.9rc1 13 10209 12.so.0.9[.0] - * 1.2.9 13 10209 12.so.0.9[.0] - * 1.2.10beta1-7 13 10210 12.so.0.10[.0] - * 1.2.10rc1-2 13 10210 12.so.0.10[.0] - * 1.2.10 13 10210 12.so.0.10[.0] - * 1.4.0beta1-5 14 10400 14.so.0.0[.0] - * 1.2.11beta1-4 13 10211 12.so.0.11[.0] - * 1.4.0beta7-8 14 10400 14.so.0.0[.0] - * 1.2.11 13 10211 12.so.0.11[.0] - * 1.2.12 13 10212 12.so.0.12[.0] - * 1.4.0beta9-14 14 10400 14.so.0.0[.0] - * 1.2.13 13 10213 12.so.0.13[.0] - * 1.4.0beta15-36 14 10400 14.so.0.0[.0] - * 1.4.0beta37-87 14 10400 14.so.14.0[.0] - * 1.4.0rc01 14 10400 14.so.14.0[.0] - * 1.4.0beta88-109 14 10400 14.so.14.0[.0] - * 1.4.0rc02-08 14 10400 14.so.14.0[.0] - * 1.4.0 14 10400 14.so.14.0[.0] - * 1.4.1beta01-03 14 10401 14.so.14.1[.0] - * 1.4.1rc01 14 10401 14.so.14.1[.0] - * 1.4.1beta04-12 14 10401 14.so.14.1[.0] - * 1.4.1 14 10401 14.so.14.1[.0] - * 1.4.2 14 10402 14.so.14.2[.0] - * 1.4.3 14 10403 14.so.14.3[.0] - * 1.4.4 14 10404 14.so.14.4[.0] - * 1.5.0beta01-58 15 10500 15.so.15.0[.0] - * 1.5.0rc01-07 15 10500 15.so.15.0[.0] - * 1.5.0 15 10500 15.so.15.0[.0] - * 1.5.1beta01-11 15 10501 15.so.15.1[.0] - * 1.5.1rc01-02 15 10501 15.so.15.1[.0] - * 1.5.1 15 10501 15.so.15.1[.0] - * 1.5.2beta01-03 15 10502 15.so.15.2[.0] - * 1.5.2rc01-03 15 10502 15.so.15.2[.0] - * 1.5.2 15 10502 15.so.15.2[.0] - * 1.5.3beta01-10 15 10503 15.so.15.3[.0] - * 1.5.3rc01-02 15 10503 15.so.15.3[.0] - * 1.5.3beta11 15 10503 15.so.15.3[.0] - * 1.5.3 [omitted] - * 1.5.4beta01-08 15 10504 15.so.15.4[.0] - * 1.5.4rc01 15 10504 15.so.15.4[.0] - * 1.5.4 15 10504 15.so.15.4[.0] - * 1.5.5beta01-08 15 10505 15.so.15.5[.0] - * 1.5.5rc01 15 10505 15.so.15.5[.0] - * 1.5.5 15 10505 15.so.15.5[.0] - * 1.5.6beta01-07 15 10506 15.so.15.6[.0] - * 1.5.6rc01-03 15 10506 15.so.15.6[.0] - * 1.5.6 15 10506 15.so.15.6[.0] - * 1.5.7beta01-05 15 10507 15.so.15.7[.0] - * 1.5.7rc01-03 15 10507 15.so.15.7[.0] - * 1.5.7 15 10507 15.so.15.7[.0] - * 1.6.0beta01-37 16 10600 16.so.16.0[.0] + * ... + * 1.0.19 10 10019 10.so.0.19[.0] + * ... + * 1.2.56 13 10253 12.so.0.53[.0] + * ... + * 1.5.27 15 10523 15.so.15.23[.0] + * ... + * 1.6.22 16 10622 16.so.16.22[.0] + * ... * 1.7.0alpha01-10 17 10700 17.so.17.0[.0] - * 1.7.0beta01-35 17 10700 17.so.17.0[.0] + * 1.7.0beta01-84 17 10700 17.so.17.0[.0] * - * Henceforth the source version will match the shared-library major - * and minor numbers; the shared-library major version number will be - * used for changes in backward compatibility, as it is intended. The - * PNG_LIBPNG_VER macro, which is not used within libpng but is available - * for applications, is an unsigned integer of the form xyyzz corresponding - * to the source version x.y.z (leading zeros in y and z). Beta versions - * were given the previous public release number plus a letter, until - * version 1.0.6j; from then on they were given the upcoming public - * release number plus "betaNN" or "rcNN". + * Henceforth the source version will match the shared-library major + * and minor numbers; the shared-library major version number will be + * used for changes in backward compatibility, as it is intended. The + * PNG_LIBPNG_VER macro, which is not used within libpng but is available + * for applications, is an unsigned integer of the form xyyzz corresponding + * to the source version x.y.z (leading zeros in y and z). Beta versions + * were given the previous public release number plus a letter, until + * version 1.0.6j; from then on they were given the upcoming public + * release number plus "betaNN" or "rcNN". * - * Binary incompatibility exists only when applications make direct access - * to the info_ptr or png_ptr members through png.h, and the compiled - * application is loaded with a different version of the library. + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. * - * DLLNUM will change each time there are forward or backward changes - * in binary compatibility (e.g., when a new feature is added). + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). * - * See libpng-manual.txt or libpng.3 for more information. The PNG - * specification is available as a W3C Recommendation and as an ISO - * Specification, * * If you just need to read a PNG file and don't want to read the documentation * skip to the end of this file and read the section entitled 'simplified API'. */ /* Version information for png.h - this should match the version in png.c */ -#define PNG_LIBPNG_VER_STRING "1.7.0beta35" -#define PNG_HEADER_VERSION_STRING \ - " libpng version 1.7.0beta35 - March 17, 2014\n" +#define PNG_LIBPNG_VER_STRING "1.7.0beta90" +#define PNG_HEADER_VERSION_STRING " libpng version 1.7.0beta90 - August 28, 2017\n" #define PNG_LIBPNG_VER_SONUM 17 #define PNG_LIBPNG_VER_DLLNUM 17 @@ -394,7 +324,7 @@ * PNG_LIBPNG_VER_STRING, omitting any leading zero: */ -#define PNG_LIBPNG_VER_BUILD 35 +#define PNG_LIBPNG_VER_BUILD 90 /* Release Status */ #define PNG_LIBPNG_BUILD_ALPHA 1 @@ -425,14 +355,14 @@ * the library has been built. */ #ifndef PNGLCONF_H - /* If pnglibconf.h is missing, you can - * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h - */ +/* If pnglibconf.h is missing, you can + * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h + */ # include "pnglibconf.h" #endif #ifndef PNG_VERSION_INFO_ONLY - /* Machine specific configuration. */ +/* Machine specific configuration. */ # include "pngconf.h" #endif @@ -479,6 +409,7 @@ * definitions. * 4. Exported library functions. * 5. Simplified API. + * 6. Implementation options * * The library source code has additional files (principally pngpriv.h) that * allow configuration of the library. @@ -507,18 +438,24 @@ */ #define PNG_u2(b1, b2) (((unsigned int)(b1) << 8) + (b2)) -#define PNG_U16(b1, b2) ((png_uint_16)PNG_u2(b1, b2)) +#define PNG_U16(b1, b2) ((png_uint_16)/*SAFE*/PNG_u2(b1, b2)) #define PNG_U32(b1, b2, b3, b4)\ - (((png_uint_32)PNG_u2(b1, b2) << 16) + PNG_u2(b3, b4)) + (((png_uint_32)/*SAFE*/PNG_u2(b1, b2) << 16) + PNG_u2(b3, b4)) /* ISO-PNG states that signed 32-bit values are stored in two's complement * format. There is no guarantee that (png_int_32) is exactly 32 bits, so the * following macro tests for a negative number and generates the machine format * directly by portable arithmetic operations. The cost is that the argument * 'b1' is evaluated twice. + * + * NOTE: the 0x7fffffffU BIC is there to ensure that potential overflow in the + * cast does not occur. This fixes the case where 1's complement machines could + * be forced into an overflow by an invalid value in the stream and, therefore, + * potentially raise an arithmetic exception; the invalid value is converted to + * 0 and any resultant problems will be caught later in the libpng checking. */ #define PNG_S32(b1, b2, b3, b4) ((b1) & 0x80\ - ? -(png_int_32)((PNG_U32(b1, b2, b3, b4) ^ 0xffffffff) + 1)\ + ? -(png_int_32)(((PNG_U32(b1, b2, b3, b4)^0xffffffffU)+1U)&0x7fffffffU)\ : (png_int_32)PNG_U32(b1, b2, b3, b4)) /* Constants for known chunk types. @@ -588,8 +525,10 @@ * systems where (char) is more than 8 bits. */ #define PNG_STRING_FROM_CHUNK(s,c)\ - (void)(((char*)(s))[0]=(char)((c)>>24), ((char*)(s))[1]=(char)((c)>>16),\ - ((char*)(s))[2]=(char)((c)>>8), ((char*)(s))[3]=(char)((c))) + (void)(((char*)(s))[0]=(char)(((c)>>24) & 0xff), \ + ((char*)(s))[1]=(char)(((c)>>16) & 0xff),\ + ((char*)(s))[2]=(char)(((c)>>8) & 0xff), \ + ((char*)(s))[3]=(char)((c & 0xff))) /* Do the same but terminate with a null character. */ #define PNG_CSTRING_FROM_CHUNK(s,c)\ @@ -645,7 +584,7 @@ extern "C" { /* This triggers a compiler error in png.c, if png.c and png.h * do not agree upon the version number. */ -typedef char* png_libpng_version_1_7_0beta35; +typedef char* png_libpng_version_1_7_0beta90; /* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. * @@ -775,6 +714,16 @@ typedef png_sPLT_t * * png_sPLT_tpp; * PNG_TEXT_COMPRESSION_zTXt. Note that the "compression value" is not the * same as what appears in the PNG tEXt/zTXt/iTXt chunk's "compression flag" * which is always 0 or 1, or its "compression method" which is always 0. + * + * The location field (added in libpng 1.7.0) records where the text chunk was + * found when png_get_text is used. When png_set_text is used the field in the + * structure passed in is ignored and, instead, the field is set to the current + * write position. + * + * Prior to 1.7.0 the write behavior was the same; the text fields were written + * (once) at the next write_info call, however the read mechanism did not record + * the chunk location so if an info_struct from read was passed to the write + * APIs the text chunks would all be written at the start (before PLTE). */ typedef struct png_text_struct { @@ -783,6 +732,9 @@ typedef struct png_text_struct 0: zTXt, deflate 1: iTXt, none 2: iTXt, deflate */ + png_byte location; /* 1: PNG_HAVE_IHDR + 2: PNG_HAVE_PLTE + 8: PNG_AFTER_IDAT */ png_charp key; /* keyword, 1-79 character description of "text" */ png_charp text; /* comment, may be an empty string (ie "") or a NULL pointer */ @@ -800,8 +752,14 @@ typedef png_text * * png_textpp; /* Supported compression types for text in PNG files (tEXt, and zTXt). * The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */ +#ifdef PNG_OLD_COMPRESSION_CODES_SUPPORTED + /* These values were used to prevent double write of text chunks in versions + * prior to 1.7.0. They are never set now; if you need them #define the + * _SUPPORTED macro. + */ #define PNG_TEXT_COMPRESSION_NONE_WR -3 #define PNG_TEXT_COMPRESSION_zTXt_WR -2 +#endif /* OLD_COMPRESSION_CODES */ #define PNG_TEXT_COMPRESSION_NONE -1 #define PNG_TEXT_COMPRESSION_zTXt 0 #define PNG_ITXT_COMPRESSION_NONE 1 @@ -838,9 +796,11 @@ typedef png_time * * png_timepp; */ typedef struct png_unknown_chunk_t { - png_byte name[5]; /* Textual chunk name with '\0' terminator */ - png_byte *data; /* Data, should not be modified on read! */ - size_t size; + png_byte *data; /* Data, should not be modified on read! */ + png_uint_32 size; /* Size of data, must not exceed 0x7fffffff. + * API CHANGE 1.7.0: changed from 'size_t' + */ + png_byte name[5]; /* Textual chunk name with '\0' terminator */ /* On write 'location' must be set using the flag values listed below. * Notice that on read it is set by libpng however the values stored have @@ -848,7 +808,7 @@ typedef struct png_unknown_chunk_t * bitmask. On write set only one bit - setting multiple bits may cause the * chunk to be written in multiple places. */ - png_byte location; /* mode of operation at read time */ + png_byte location; /* mode of operation at read time */ } png_unknown_chunk; @@ -857,10 +817,10 @@ typedef const png_unknown_chunk * png_const_unknown_chunkp; typedef png_unknown_chunk * * png_unknown_chunkpp; #endif -/* Flag values for the unknown chunk location byte. */ -#define PNG_HAVE_IHDR 0x01 -#define PNG_HAVE_PLTE 0x02 -#define PNG_AFTER_IDAT 0x08 +/* Flag values for the chunk location byte. */ +#define PNG_HAVE_IHDR 0x01U +#define PNG_HAVE_PLTE 0x02U +#define PNG_AFTER_IDAT 0x08U /* Maximum positive integer used in PNG is (2^31)-1 */ #define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL) @@ -877,12 +837,12 @@ typedef png_unknown_chunk * * png_unknown_chunkpp; /* These describe the color_type field in png_info. */ /* color type masks */ -#define PNG_COLOR_MASK_PALETTE 1 -#define PNG_COLOR_MASK_COLOR 2 -#define PNG_COLOR_MASK_ALPHA 4 +#define PNG_COLOR_MASK_PALETTE 1U +#define PNG_COLOR_MASK_COLOR 2U +#define PNG_COLOR_MASK_ALPHA 4U /* color types. Note that not all combinations are legal */ -#define PNG_COLOR_TYPE_GRAY 0 +#define PNG_COLOR_TYPE_GRAY 0U #define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE) #define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR) #define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA) @@ -895,11 +855,26 @@ typedef png_unknown_chunk * * png_unknown_chunkpp; #define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */ #define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE -/* This is for filter type. PNG 1.0-1.2 only define the single type. */ +/* This is for filter method. PNG 1.0-1.2 only defines a single method. + * + * NOTE: CONFUSING NAME. The specification refers to a 'method', one of the + * defines below, and a 'type', one of the FILTER_VALUE defines. + * Historically libpng uses TYPE for 'method' and VALUE for 'type'. + */ #define PNG_FILTER_TYPE_BASE 0 /* Single row per-byte filtering */ #define PNG_INTRAPIXEL_DIFFERENCING 64 /* Used only in MNG datastreams */ #define PNG_FILTER_TYPE_DEFAULT PNG_FILTER_TYPE_BASE +/* Filter values defined for method '0' (PNG_FILTER_TYPE_BASE) in the PNG + * specification. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 /* Not a valid value */ + /* These are for the interlacing type. These values should NOT be changed. */ #define PNG_INTERLACE_NONE 0 /* Non-interlaced image */ #define PNG_INTERLACE_ADAM7 1 /* Adam7 interlacing */ @@ -946,22 +921,22 @@ typedef png_unknown_chunk * * png_unknown_chunkpp; * data in the info_struct to be written into the output file. The values * of the PNG_INFO_ defines should NOT be changed. */ -#define PNG_INFO_gAMA 0x0001 -#define PNG_INFO_sBIT 0x0002 -#define PNG_INFO_cHRM 0x0004 -#define PNG_INFO_PLTE 0x0008 -#define PNG_INFO_tRNS 0x0010 -#define PNG_INFO_bKGD 0x0020 -#define PNG_INFO_hIST 0x0040 -#define PNG_INFO_pHYs 0x0080 -#define PNG_INFO_oFFs 0x0100 -#define PNG_INFO_tIME 0x0200 -#define PNG_INFO_pCAL 0x0400 -#define PNG_INFO_sRGB 0x0800 /* GR-P, 0.96a */ -#define PNG_INFO_iCCP 0x1000 /* ESR, 1.0.6 */ -#define PNG_INFO_sPLT 0x2000 /* ESR, 1.0.6 */ -#define PNG_INFO_sCAL 0x4000 /* ESR, 1.0.6 */ -#define PNG_INFO_IDAT 0x8000 /* ESR, 1.0.6 */ +#define PNG_INFO_gAMA 0x0001U +#define PNG_INFO_sBIT 0x0002U +#define PNG_INFO_cHRM 0x0004U +#define PNG_INFO_PLTE 0x0008U +#define PNG_INFO_tRNS 0x0010U +#define PNG_INFO_bKGD 0x0020U +#define PNG_INFO_hIST 0x0040U +#define PNG_INFO_pHYs 0x0080U +#define PNG_INFO_oFFs 0x0100U +#define PNG_INFO_tIME 0x0200U +#define PNG_INFO_pCAL 0x0400U +#define PNG_INFO_sRGB 0x0800U /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000U /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000U /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000U /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000U /* ESR, 1.0.6 */ /* This is used for the transformation routines, as some of them * change these values for the row. It also should enable using @@ -1000,22 +975,53 @@ typedef PNG_CALLBACK(void, *png_write_status_ptr, (png_structp, png_uint_32, typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop)); typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop)); -/* The following callback receives png_uint_32 row_number, int pass for the - * png_bytep data of the row. When transforming an interlaced image the - * row number is the row number within the sub-image of the interlace pass, so - * the value will increase to the height of the sub-image (not the full image) - * then reset to 0 for the next pass. +/* WARNING: the API for this callback is poorly documented and produces + * unexpected results when dealing with interlaced images. For non-interlaced + * images the parameters are straightforward: * - * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to - * find the output pixel (x,y) given an interlaced sub-image pixel - * (row,col,pass). (See below for these macros.) + * next_row: a pointer to the transformed row read from the PNG input + * stream, it has png_get_image_width() pixels. + * row_y: the y ordinate of the image; 0..png_get_image_height()-1 + * pass: 0 + * + * For interlaced images if png_set_interlace_handling has been called (libpng + * does *not* call this itself) the parameters are the same except that the + * pass will be the pass in the range 0..6 (NOTE: one less than the PNG spec) + * and 'next_row' will be NULL if (and only if) the row does not contribute + * to the output in 'blocky' display mode. + * + * pass: 0..6 + * + * If 'next_row' is not NULL it is necessary for the application to combine the + * pixels with the output. This can most easily be done by calling + * png_progressive_combine_row(). Note that the 'next_row' data cannot be + * changed; even though the value is passed to png_progressive_combine_row the + * pointer is not used, it is just a flag , if it is NULL nothing will happen. + * + * If png_set_interlace_handling has not been called the callback only gets + * called for original PNG interlaced row: + * + * row_y: the y ordinate in the pass; 0..PNG_PASS_ROWS()-1 + * + * What is more if PNG_PASS_COLS() is 0 the entire pass will be skipped. The + * row data is not full width and there is no guarantee that the buffer passed + * in 'next_row' is able to accomodate the full width of output pixels, however + * 'next_row' will never be NULL. + * + * Use PNG_ROW_FROM_PASS_ROW(row_y, pass) and PNG_COL_FROM_PASS_COL(col, pass) + * to find the output pixel (x,y) given an interlaced sub-image pixel + * (row_y,col,pass). (See below for these macros.) + * + * Note that in this latter case if you want to do the 'blocky' display update + * method you have to work out all the details yourself with regard to which + * pixels to set for each row and whether to replicate it to the following + * rows of the image. */ -typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, png_bytep, - png_uint_32, int)); +typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, + png_bytep next_row, png_uint_32 row_y, int pass)); #endif -#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED typedef PNG_CALLBACK(void, *png_user_transform_ptr, (png_structp, png_row_infop, png_bytep)); #endif @@ -1144,11 +1150,35 @@ PNG_EXPORTA(5, png_structp, png_create_write_struct, png_error_ptr warn_fn), PNG_ALLOCATED); -PNG_EXPORT(6, size_t, png_get_compression_buffer_size, - (png_const_structrp png_ptr)); - -PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr, - size_t size)); +/* These APIs control the size of the buffer used for reading IDAT chunks in the + * sequential read code and the size of the IDAT chunks produced when writing. + * They have no effect on the progressive read code. In both read and write + * cases it will be necessary to allocate at least this amount of buffer space. + * The default value is PNG_IDAT_READ_SIZE on read and PNG_ZBUF_SIZE on write. + * + * The valid range is 1..0x7FFFFFFF on write and 1..max(uInt) on read, where + * uInt is the type declared by zlib.h. On write setting the largest value will + * typically cause the PNG image data to be written in one chunk; this gives the + * smallest PNG and has little or no effect on applications that read the PNG. + * + * DEPRECATED: use png_set_IDAT_size on write and png_set_read_buffer_size on + * read. + */ +PNG_EXPORTA(6, png_alloc_size_t, png_get_compression_buffer_size, + (png_const_structrp png_ptr), PNG_DEPRECATED); +PNG_EXPORTA(7, void, png_set_compression_buffer_size, (png_structrp png_ptr, + png_alloc_size_t size), PNG_DEPRECATED); +#define png_set_read_buffer_size(p,size) (png_setting((p),\ + PNG_SR_COMPRESS_buffer_size, (size), 0)) + /* The size of the buffer used while processing compressed data, both single + * chunk data (zTXt, iTXt, iCCP) and IDAT data. With IDAT data in libpng 1.7 + * IDATs are read until the end or until the buffer is full; this means that + * you can optimize the buffer size for the particular memory behavior of + * your system and, possibly, your application. + * + * NOTE: the result (on success) is 0, which is actually an invalid value. + * Retrieving the current value is not possible. + */ /* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp * match up. @@ -1200,8 +1230,8 @@ PNG_EXPORTA(12, png_structp, png_create_write_struct_2, PNG_EXPORT(13, void, png_write_sig, (png_structrp png_ptr)); /* Write a PNG chunk - size, type, (optional) data, CRC. */ -PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, png_const_bytep - chunk_name, png_const_bytep data, size_t length)); +PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, + png_const_bytep chunk_name, png_const_voidp data, size_t length)); /* Write the start of a PNG chunk - length and chunk name. */ PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr, @@ -1209,7 +1239,7 @@ PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr, /* Write the data of a PNG chunk started with png_write_chunk_start(). */ PNG_EXPORT(16, void, png_write_chunk_data, (png_structrp png_ptr, - png_const_bytep data, size_t length)); + png_const_voidp data, size_t length)); /* Finish a chunk started with png_write_chunk_start() (includes CRC). */ PNG_EXPORT(17, void, png_write_chunk_end, (png_structrp png_ptr)); @@ -1247,12 +1277,13 @@ PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer, (char out[29], #ifdef PNG_CONVERT_tIME_SUPPORTED /* Convert from a struct tm to png_time */ -PNG_EXPORT(24, void, png_convert_from_struct_tm, (png_timep ptime, - const struct tm * ttime)); +PNG_EXPORT(24, PNG_DEPRECATED void, png_convert_from_struct_tm, + (png_timep ptime, const struct tm * ttime)); /* Convert from time_t to png_time. Uses gmtime() */ -PNG_EXPORT(25, void, png_convert_from_time_t, (png_timep ptime, time_t ttime)); -#endif /* PNG_CONVERT_tIME_SUPPORTED */ +PNG_EXPORT(25, PNG_DEPRECATED void, png_convert_from_time_t, (png_timep ptime, + time_t ttime)); +#endif /* CONVERT_tIME */ #ifdef PNG_READ_EXPAND_SUPPORTED /* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ @@ -1281,15 +1312,45 @@ PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr)); #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED /* Reduce RGB to grayscale. */ -#define PNG_ERROR_ACTION_NONE 1 -#define PNG_ERROR_ACTION_WARN 2 -#define PNG_ERROR_ACTION_ERROR 3 +#define PNG_ERROR_ACTION_NO_CHECK 0 +#define PNG_ERROR_ACTION_NONE 1 +#define PNG_ERROR_ACTION_WARN 2 +#define PNG_ERROR_ACTION_ERROR 3 #define PNG_RGB_TO_GRAY_DEFAULT (-1)/*for red/green coefficients*/ PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structrp png_ptr, int error_action, double red, double green)) PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structrp png_ptr, int error_action, png_fixed_point red, png_fixed_point green)) + /* Convert RGB pixels to gray (CIE Y) values, the red and green value must be + * less than or equal to 1, if either is negative a set of defaults + * corresponding to the sRGB standard are used. + * + * The error action specifies whether to check for r==g==b in each pixel, if + * it is 0 (PNG_ERROR_ACTION_NO_CHECK; added in libpng 1.7.0) no check will + * be performed, otherwise a check is performed and the result can be + * retrieved using png_get_rgb_to_gray_status (which just returns a + * true if a non-gray pixel was encountered). + * + * Pass PNG_ERROR_ACTION_ERROR if you are confident that the image only + * contains gray pixels (you have already checked); the check is still + * performed but a very optimized code path is used for RGB to gray + * convertion. + * + * If you want to extract a single channel pass PNG_FP_1 for the coefficient + * for that channel and 0 for the rest (0 for both red and green to extract + * blue). + * + * NOTE: the default coefficients used if negative values are passed for red + * or green are based on the cHRM chunk if available, otherwise sRGB. The + * calculation returns the Y (luminance value) corresponding to the white + * point of the PNG. UNLESS THE WHITE POINT IS D50 THIS IS NOT A CIEXYZ Y + * VALUE. It is the luminance of the pixel perceived by a viewer completely + * adapted to the white point of the PNG, this may not be what you want + * because to interpret it you have to also record the white point of the + * PNG. To obtain CIEXYZ Y values read the cHRM chunk XYZ values and + * chromatically adapt them to D50 + */ PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structrp png_ptr)); @@ -1458,7 +1519,7 @@ PNG_EXPORT(39, void, png_set_filler, (png_structrp png_ptr, png_uint_32 filler, /* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */ PNG_EXPORT(40, void, png_set_add_alpha, (png_structrp png_ptr, png_uint_32 filler, int flags)); -#endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */ +#endif /* READ_FILLER || WRITE_FILLER */ #if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) /* Swap bytes in 16-bit depth files. */ @@ -1524,7 +1585,7 @@ PNG_EXPORT(229, void, png_set_scale_16, (png_structrp png_ptr)); #endif #ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED -#define PNG_READ_16_TO_8 SUPPORTED /* Name prior to 1.5.4 */ +#define PNG_READ_16_TO_8_SUPPORTED /* Name prior to 1.5.4 */ /* Strip the second byte of information from a 16-bit depth file. */ PNG_EXPORT(48, void, png_set_strip_16, (png_structrp png_ptr)); #endif @@ -1562,11 +1623,16 @@ PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed, (png_structrp png_ptr, #endif #ifdef PNG_WRITE_FLUSH_SUPPORTED -/* Set how many lines between output flushes - 0 for no flushing */ -PNG_EXPORT(51, void, png_set_flush, (png_structrp png_ptr, int nrows)); +PNG_REMOVED(51, void, png_set_flush, (png_structrp png_ptr, int nrows), + PNG_EMPTY) +#define png_set_flush(p,v) (png_setting((p), PNG_SW_FLUSH, 0, (v))) + /* Set how many lines between output flushes - 0 for no flushing. The result + * on success is always 0. + */ + /* Flush the current PNG output buffer */ PNG_EXPORT(52, void, png_write_flush, (png_structrp png_ptr)); -#endif +#endif /* WRITE_FLUSH */ /* Optional update palette with requested transformations */ PNG_EXPORT(53, void, png_start_read_image, (png_structrp png_ptr)); @@ -1587,7 +1653,7 @@ PNG_EXPORT(56, void, png_read_row, (png_structrp png_ptr, png_bytep row, png_bytep display_row)); #endif -#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +#ifdef PNG_READ_IMAGE_SUPPORTED /* Read the whole image into memory at once. */ PNG_EXPORT(57, void, png_read_image, (png_structrp png_ptr, png_bytepp image)); #endif @@ -1628,9 +1694,11 @@ PNG_EXPORT(64, void, png_destroy_read_struct, (png_structpp png_ptr_ptr, PNG_EXPORT(65, void, png_destroy_write_struct, (png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)); -/* Set the libpng method of handling chunk CRC errors */ -PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action, - int ancil_action)); +/* Set the libpng method of handling chunk CRC errors on read */ +PNG_REMOVED(66, void, png_set_crc_action, (png_structrp png_ptr, + int crit_action, int ancil_action), PNG_EMPTY) +#define png_set_crc_action(png_ptr, crit, ancil)\ + (png_setting((png_ptr), PNG_SR_CRC_ACTION, (crit), (ancil))) /* Values for png_set_crc_action() say how to handle CRC errors in * ancillary and critical chunks, and whether to use the data contained @@ -1648,6 +1716,169 @@ PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action, #define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ #define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ +/* Write image filtering and compression options. + * + * These settings just change the very low level encoding of a PNG. The changes + * make no difference to the image or the meta-data stored in the PNG. The API + * used to make these changes can be disabled in a very minimal configuration, + * if it is your compiler will report undefined values when the APIs below are + * used. + * + * Write settings defined here, in order of ease of use: + * + * 1) Write compression settings: whether to optimize the write and the PNG + * that results for read speed, final PNG size, write speed or memory + * usage. + * 2) IDAT size: What size to make the IDAT chunks in the PNG. + * 3) PNG row filters to consider when writing the PNG. + * 4) Very low level control over the deflate compression (useful mainly for + * programs that want to try every option to find which gives the smallest + * PNG.) + */ +#ifdef PNG_WRITE_SUPPORTED +/* (1) Write compression settings: */ +#define png_set_compression(p, v) (png_setting((p), PNG_SW_COMPRESS_png_level,\ + 0, (v))) + /* Control the write compression of all chunks. This affects five basic + * pieces of behavior: + * + * 1) The size of the PNG produced. + * 2) The amount of memory the write code takes to produce the PNG. + * 3) The amount of time the write code takes to produce the PNG. + * 4) The amount of memory required to read the resultant PNG. + * 5) The amount of time required to read the resultant PNG. + * + * There is considerable interdependence between these variables. As a + * result there are a limited number of options: + */ +# define PNG_COMPRESSION_LOW_MEMORY (1) + /* Minimize the memory required both when reading (4) and writing (2) the + * PNG. This results in a significantly larger PNG (which may itself have + * the opposite effect of slowing down either read or write) however the + * memory overhead is reduced and, apart from the extra time to read the + * data, the read time is likely to be reduced too. + * + * Use this when both read and write will happen on a memory starved + * (really, very low memory) system. Note that this sets a high deflate + * compression setting because that does not affect zlib memory usage. + */ +# define PNG_COMPRESSION_HIGH_SPEED (2) + /* Minimize the time to both read (5) and write (3) the PNG. This uses + * slightly more memory on read and potentially significantly more on + * write but is optimized for maximum speed in both cases. + * + * Use this when both read and write need to be fast and PNG size is not + * likely to be an issue. An example would be if you are using PNG to + * pass intermediate data between applications on the same machine. + */ +# define PNG_COMPRESSION_HIGH_READ_SPEED (3) + /* Minimize the time to read (5) the PNG. This also reduces the amount + * of memory on read, however some options which require more memory but + * are likely to decrease PNG size, therefore improve read spead, are + * used. + * + * This is one of the 'normal' options; options that are used when a + * reasonably capable write machine is producing PNG files that will be + * read many times. In this case the option is optimizing for speed on + * read even if it increases the size of the PNG. + */ +# define PNG_COMPRESSION_LOW (4) + /* This switches on options which do affect speed of both compression and + * decompression, but biases the choice towards higher performance in both + * cases. (So it is something of a compromise between all-out speed and + * PNG compression). + * + * This is a good default to use in typical usages where PNG file size is + * less of an issue than the overheads on reading a PNG file. + * + * Use this option when producing PNG files that are not expected to be + * distributed widely or where read speed is more important than size. + * This is also a good default for small images where the slight increase + * in size of the compressed data doesn't change the file size much. + */ +# define PNG_COMPRESSION_MEDIUM (5) + /* This is a compromise which switches on the options found most helpful + * across a wide range of files without switching on the full range of + * options which would decrease file size only a little while taking a lot + * more time. PNG read memory (4) or time (5) is not a factor in the + * choice of options; only write time (3). + * + * This is closest to the default used in prior versions of libpng. There + * seems no logic to using it if the actual requirements are known and, + * even if they aren't, it is probably better to guess 'LOW' or 'HIGH'. + * + * This is the normal libpng default. + */ +# define PNG_COMPRESSION_HIGH (6) + /* This turns on everything which reduces file size on aggregate across a + * large test set of files. It optimizes solely for the size of the + * resultant PNG (1). + * + * This is a good default to use if file size is all important; it was the + * stated original default in the PNG design, but the implementation of + * libpng never used it. + * + * Use this setting in image authoring applications when writing the + * finished image in PNG format. + * + * NOTE: several PNG file size optimizers exist (see the web-site + * libpng.org). libpng does not perform the same functions as these + * optimizers; libpng does not search for the best compression settings. + * For this reason if you really want to minimize the size of the PNG files + * produced use PNG_COMPRESSION_HIGH_SPEED then post-process the result + * with one of the many PNG optimization programs. + */ +# define PNG_COMPRESSION_COMPAT (0) + /* DEPRECATED: this is provided as a setting to aid transition of test + * suites between major library versions (1.5 or 1.6 moving to 1.7). The + * default settings change in 1.7 so, while the PNG files produced do not + * change, their encoding does. Test systems that rely on constant + * encoding can use this to verify that this is all that has changed. + * + * NOTE: the option will be removed at some point. It is difficult to + * maintain and adds to libpng code size. + * + * NOTE: there are other changes in major and minor releases, such as + * better ancillary chunk error handling, that also cause binary changes + * to the PNG files libpng generates. Furthermore versions of libpng + * prior to 1.7 included random data from uninitialized memory in the + * image data under certain circumstances; this meant that earlier + * versions were often not even consistent across two writes of the same + * PNG file! + */ + +/* png_set_compression sets the default for all libpng compression operations. + * While the setting is the same for all chunks it results in different + * compression options for different chunks. The setting can be applied + * separately to each class of chunks as follows: + */ +#define png_set_image_compression(p, v) (png_setting((p),\ + PNG_SW_COMPRESS_png_level, png_IDAT, (v))) + /* Control the compression of the image data (IDAT) chunks. */ + +#define png_set_ICC_profile_compression(p, v) (png_setting((p),\ + PNG_SW_COMPRESS_png_level, png_iCCP, (v))) + /* Control the compression of ICC profiles (iCCP chunks.) */ + +#define png_set_text_compression(p, v) (png_setting((p),\ + PNG_SW_COMPRESS_png_level, png_zTXt, (v))) + /* Control the compression of text (png_zTXt and png_iTXt) chunks. */ + +/* (2) IDAT size: */ +#define png_set_IDAT_size(p, v) (png_setting((p), PNG_SW_IDAT_size, (v), 0)) + /* Set the maximum size of the IDAT chunks libpng writes. Valid values are + * in the range 1U..0x7fffffffU, the default is 'PNG_ZBUF_SIZE' (a + * historically confusing name) and this default *also* controls the size of + * the buffer the read code uses when reading IDAT chunks. + * + * libpng has to buffer the data in the IDAT chunk before it writes any of + * it, therefore this number directly controls that part of the memory + * overhead while writing a PNG. There is a 12 byte per chunk overhead, so + * the number also directly affects the size of the PNG. The number has no + * significant effect (beyond the latter size effect) on the read code. + */ +#endif /* WRITE */ + /* These functions give the user control over the scan-line filtering in * libpng and the compression methods used by zlib. These functions are * mainly useful for testing, as the defaults should work with most users. @@ -1656,135 +1887,221 @@ PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action, * header file (zlib.h) for an explination of the compression functions. */ -/* Set the filtering method(s) used by libpng. Currently, the only valid - * value for "method" is 0. - */ -PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method, - int filters)); - -/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. - * These defines match the values in the PNG specification. - */ -#define PNG_FILTER_VALUE_NONE 0 -#define PNG_FILTER_VALUE_SUB 1 -#define PNG_FILTER_VALUE_UP 2 -#define PNG_FILTER_VALUE_AVG 3 -#define PNG_FILTER_VALUE_PAETH 4 -#define PNG_FILTER_VALUE_LAST 5 - -/* The above values are valid arguments to png_set_filter() if only a single - * filter is to be used. If multiple filters are to be allowed (the default is - * to allow any of them) then a combination of the following masks must be used - * and the low three bits of the argument to png_set_filter must be 0. +#ifdef PNG_WRITE_FILTER_SUPPORTED +/* (3) PNG row filters to consider when writing the PNG. * - * The resultant argument fits in a single byte. + * Control the filtering method(s) used by libpng for the write of subsequent + * rows of the image. The argument is either a single filter value (one of the + * PNG_FILTER_VALUE_ defines above), in which case that filter will be used on + * following rows, or a mask of filter values (logical or of the PNG_FILTER_ + * bit masks that follow PNG_FILTER_VALUE_*). Support for selection of a filter + * from a mask with more than one bit set is dependent on + * PNG_SELECT_FILTER_SUPPORTED, however support is the default configuration of + * libpng. If support is not available the lowest bit set in the mask (the + * lowest numbered filter) is used. + * + * The set of filters may be changed at any time, the new values will affect the + * next row written. + * + * The 'method' must match that passed to png_set_IHDR; it cannot be changed and + * is ignored in 1.7 and later. + * + * If multiple filters are enabled libpng will select one according to the + * following rules: + * + * 1) On the first row of a pass UP is ignored if NONE is set and PAETH is + * ignored if SUB is set; this is because these filter pairs are equivalent + * when there is no previous row. + * + * 2) PNG_SELECT_FILTER_SUPPORTED: + * If the PNG rows are long enough (have enough bytes) libpng will process a + * row at a time; it will buffer the row if necessary. It uses a heuristic + * based on the closeness of the filtered values to 0 to determine which + * filter to use. + * + * 3) !PNG_SELECT_FILTER_SUPPORTED: + * libpng selects the first filter in the list (there is no warning that this + * will happen - check the #defines if you need to know.) + * + * The 'up', 'avg' and 'Paeth' filters require the previous image row to work. + * If it is not available they are removed from the set of filters to try. The + * first time the filter mask includes one of these filters libpng turns on + * saving of the row. The filters do work on the first row of a pass, where + * there is no previous row from the image. The PNG standard defines the + * previous row as consisting of all 0 bytes in this case. That definition + * causes the filters to have the following properties on the first row of a + * pass: + * + * UP: The same as NONE (i.e. no filtering). + * AVG: Uses the arithmetic (not modular arithmetc!) half of the preceding + * pixel as the predictor. This is unique and not typically very + * useful. + * PAETH: The same as SUB. + * + * As a result with all versions of libpng if you want to use any of these + * filters anywhere in the image you need only turn on one of them on the first + * row of the image, or of a pass for interlaced images. For example if you + * want to use 'sub' on the first row simply set 'sub'+'Paeth' in the mask; + * libpng will automatically eliminate the Paeth algorithm from consideration + * because it knows that 'sub' will rank equal or (if the filter byte is taken + * into account) better. + * + * This approach is portable to earlier versions of libpng, however it may be + * difficult to program. 1.7 allows you to directly specify whether or not to + * retain the previous row. This is simpler and allows you to turn off previous + * row retention if you want to. */ -#define PNG_FILTER_NONE (0x08 << PNG_FILTER_VALUE_NONE) -#define PNG_FILTER_SUB (0x08 << PNG_FILTER_VALUE_SUB) -#define PNG_FILTER_UP (0x08 << PNG_FILTER_VALUE_UP) -#define PNG_FILTER_AVG (0x08 << PNG_FILTER_VALUE_AVG) -#define PNG_FILTER_PAETH (0x08 << PNG_FILTER_VALUE_PAETH) +PNG_REMOVED(67, void, png_set_filter, (png_structrp png_ptr, int method, + int filters), PNG_EMPTY) -/* Then two convenience values. PNG_NO_FILTERS is the same as +#define png_set_filter(p, m, f) (png_setting((p), PNG_SW_COMPRESS_filters,\ + (m), (f))) + /* 'm' is the method and must be 0 (PNG_FILTER_TYPE_BASE) unless MNG + * processing is supported (very unusual). 'f' is either a single value, + * PNG_FILTER_VALUE_* below, or a combination of one or more PNG_FILTER_MASK + * values. + * + * This sets the filter mask (or value) for the *next* row that is written. + * It may be called at any time but does not have any effect until the next + * row starts to be written. + * + * The return value is the mask that is set (or, with PNG_SF_GET, the + * currently set mask). When PNG_SELECT_FILTER_SUPPORTED is not defined this + * mask will have only one bit. + * + * NOTE: with PNG_SF_GET the result will be PNG_UNSET if png_set_filter has + * not been called before and row writing has not started. + */ + +#define png_set_row_buffers(p, onoff) (png_setting((p),\ + PNG_SW_COMPRESS_row_buffers, (onoff), 0)) + /* If you intend to change the filter list after the first row using the + * previous API call png_set_row_buffers(png_ptr, 1) if you intend to use UP, + * AVG or Paeth filters. + * + * You can turn the buffering on and off dynamically, just as with + * png_set_filter. + * + * The second argument should be 0 (off) or 1 (on). In the future it may be + * used to control the maximum number of rows buffered. + */ +#endif /* WRITE_FILTER */ + +/* The PNG_FILTER_VALUE_ definitions (the filter values from the base PNG spec) + * are valid arguments to png_set_filter() if only a single filter is to be + * used. If multiple filters are to be allowed (the default is to allow any of + * them) then a combination of the following masks must be used and the low + * three bits of the argument to png_set_filter must be 0. + * + * The resultant argument fits in a single byte in either case. + */ +#define PNG_FILTER_MASK(value) (0x08 << (value)) +#define PNG_FILTER_NONE PNG_FILTER_MASK(PNG_FILTER_VALUE_NONE) +#define PNG_FILTER_SUB PNG_FILTER_MASK(PNG_FILTER_VALUE_SUB) +#define PNG_FILTER_UP PNG_FILTER_MASK(PNG_FILTER_VALUE_UP) +#define PNG_FILTER_AVG PNG_FILTER_MASK(PNG_FILTER_VALUE_AVG) +#define PNG_FILTER_PAETH PNG_FILTER_MASK(PNG_FILTER_VALUE_PAETH) + +/* Then three convenience values. PNG_NO_FILTERS is the same as * PNG_FILTER_VALUE_NONE, but this is harmless because they mean the same thing. */ -#define PNG_NO_FILTERS 0x00 -#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \ - PNG_FILTER_AVG | PNG_FILTER_PAETH) - -#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* EXPERIMENTAL */ -/* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_ - * defines, either the default (minimum-sum-of-absolute-differences), or - * the experimental method (weighted-minimum-sum-of-absolute-differences). - * - * Weights are factors >= 1.0, indicating how important it is to keep the - * filter type consistent between rows. Larger numbers mean the current - * filter is that many times as likely to be the same as the "num_weights" - * previous filters. This is cumulative for each previous row with a weight. - * There needs to be "num_weights" values in "filter_weights", or it can be - * NULL if the weights aren't being specified. Weights have no influence on - * the selection of the first row filter. Well chosen weights can (in theory) - * improve the compression for a given image. - * - * Costs are factors >= 1.0 indicating the relative decoding costs of a - * filter type. Higher costs indicate more decoding expense, and are - * therefore less likely to be selected over a filter with lower computational - * costs. There needs to be a value in "filter_costs" for each valid filter - * type (given by PNG_FILTER_VALUE_LAST), or it can be NULL if you aren't - * setting the costs. Costs try to improve the speed of decompression without - * unduly increasing the compressed image size. - * - * A negative weight or cost indicates the default value is to be used, and - * values in the range [0.0, 1.0) indicate the value is to remain unchanged. - * The default values for both weights and costs are currently 1.0, but may - * change if good general weighting/cost heuristics can be found. If both - * the weights and costs are set to 1.0, this degenerates the WEIGHTED method - * to the UNWEIGHTED method, but with added encoding time/computation. - */ -PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structrp png_ptr, - int heuristic_method, int num_weights, png_const_doublep filter_weights, - png_const_doublep filter_costs)) -PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed, - (png_structrp png_ptr, int heuristic_method, int num_weights, - png_const_fixed_point_p filter_weights, - png_const_fixed_point_p filter_costs)) -#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ - -/* Heuristic used for row filter selection. These defines should NOT be - * changed. - */ -#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ -#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ -#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ -#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FAST_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP) +#define PNG_ALL_FILTERS (PNG_FAST_FILTERS | PNG_FILTER_AVG | PNG_FILTER_PAETH) #ifdef PNG_WRITE_SUPPORTED -/* Set the library compression level. Currently, valid values range from - * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 - * (0 - no compression, 9 - "maximal" compression). Note that tests have - * shown that zlib compression levels 3-6 usually perform as well as level 9 - * for PNG images, and do considerably fewer caclulations. In the future, - * these values may not correspond directly to the zlib compression levels. - */ -PNG_EXPORT(69, void, png_set_compression_level, (png_structrp png_ptr, - int level)); +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */ +PNG_REMOVED(68, void, png_set_filter_heuristics, + (png_structrp png_ptr, int heuristic_method, int num_weights, + png_const_doublep filter_weights, png_const_doublep filter_costs), + PNG_DEPRECATED) +PNG_REMOVED(209, void, png_set_filter_heuristics_fixed, + (png_structrp png_ptr, int heuristic_method, int num_weights, + png_const_fixed_point_p filter_weights, + png_const_fixed_point_p filter_costs), + PNG_DEPRECATED) + /* Neither of these API calls did anything in libpng 1.6, however they were + * not marked PNG_DEPRECATED, so they are converted to no-op function-like + * macros here. (NOTE: the macro arguments are evaluated once each, this + * will probably cause warnings with some compiler options: simply remove the + * function call after ensuring that the arguments had no side effects.) + */ +#define png_set_filter_heuristics(p,m,w,fw,fc) ((void)(p,m,w,fw,fc)) +#define png_set_filter_heuristics_fixed(p,m,w,fw,fc) ((void)(p,m,w,fw,fc)) +#endif /* WRITE_WEIGHTED_FILTER */ -PNG_EXPORT(70, void, png_set_compression_mem_level, (png_structrp png_ptr, - int mem_level)); +#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +PNG_REMOVED(69, void, png_set_compression_level, (png_structrp png_ptr, + int level), PNG_EMPTY) +#define png_set_compression_level(p, v) (png_setting((p),\ + PNG_SW_COMPRESS_zlib_level, png_IDAT, (v))) -PNG_EXPORT(71, void, png_set_compression_strategy, (png_structrp png_ptr, - int strategy)); +PNG_REMOVED(70, void, png_set_compression_mem_level, (png_structrp png_ptr, + int mem_level), PNG_EMPTY) +#define png_set_compression_mem_level(p, v) (png_setting((p),\ + PNG_SW_COMPRESS_memLevel, png_IDAT, (v))) -/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a - * smaller value of window_bits if it can do so safely. - */ -PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structrp png_ptr, - int window_bits)); +PNG_REMOVED(71, void, png_set_compression_strategy, (png_structrp png_ptr, + int strategy), PNG_EMPTY) +#define png_set_compression_strategy(p, v) (png_setting((p),\ + PNG_SW_COMPRESS_strategy, png_IDAT, (v))) -PNG_EXPORT(73, void, png_set_compression_method, (png_structrp png_ptr, - int method)); -#endif +PNG_REMOVED(72, void, png_set_compression_window_bits, (png_structrp png_ptr, + int window_bits), PNG_EMPTY) +#define png_set_compression_window_bits(p, v) (png_setting((p),\ + PNG_SW_COMPRESS_windowBits, png_IDAT, (v))) + +PNG_REMOVED(73, void, png_set_compression_method, (png_structrp png_ptr, + int method), PNG_EMPTY) +#define png_set_compression_method(p, v) (png_setting((p),\ + PNG_SW_COMPRESS_method, png_IDAT, (v))) +#endif /* WRITE_CUSTOMIZE_COMPRESSION */ #ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED /* Also set zlib parameters for compressing non-IDAT chunks */ -PNG_EXPORT(222, void, png_set_text_compression_level, (png_structrp png_ptr, - int level)); +PNG_REMOVED(222, void, png_set_text_compression_level, (png_structrp png_ptr, + int level), PNG_EMPTY) +#define png_set_text_compression_level(p, v) (png_setting((p),\ + PNG_SW_COMPRESS_zlib_level, png_zTXt, (v))) -PNG_EXPORT(223, void, png_set_text_compression_mem_level, (png_structrp png_ptr, - int mem_level)); +PNG_REMOVED(223, void, png_set_text_compression_mem_level, + (png_structrp png_ptr, int mem_level), PNG_EMPTY) +#define png_set_text_compression_mem_level(p, v) (png_setting((p),\ + PNG_SW_COMPRESS_memLevel, png_zTXt, (v))) -PNG_EXPORT(224, void, png_set_text_compression_strategy, (png_structrp png_ptr, - int strategy)); +PNG_REMOVED(224, void, png_set_text_compression_strategy, (png_structrp png_ptr, + int strategy), PNG_EMPTY) +#define png_set_text_compression_strategy(p, v) (png_setting((p),\ + PNG_SW_COMPRESS_strategy, png_zTXt, (v))) -/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a - * smaller value of window_bits if it can do so safely. +PNG_REMOVED(225, void, png_set_text_compression_window_bits, + (png_structrp png_ptr, int window_bits), PNG_EMPTY) +#define png_set_text_compression_window_bits(p, v) (png_setting((p),\ + PNG_SW_COMPRESS_windowBits, png_zTXt, (v))) + +PNG_REMOVED(226, void, png_set_text_compression_method, (png_structrp png_ptr, + int method), PNG_EMPTY) +#define png_set_text_compression_method(p, v) (png_setting((p),\ + PNG_SW_COMPRESS_method, png_zTXt, (v))) + +/* NOTE: in versions of libpng prior to 1.7 iCCP compression was controlled by + * the text settings, hence the controls were only available if + * PNG_WRITE_CUSTOMIZIZE_ZTXT_COMPRESSION_SUPPORTED. In 1.7 the text settings + * no longer affect iCCP compression, the following macros must be used (if + * necessary): */ -PNG_EXPORT(225, void, png_set_text_compression_window_bits, - (png_structrp png_ptr, int window_bits)); - -PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr, - int method)); -#endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */ +#define png_set_ICC_profile_compression_level(p, v) (png_setting((p),\ + PNG_SW_COMPRESS_zlib_level, png_iCCP, (v))) +#define png_set_ICC_profile_compression_mem_level(p, v) (png_setting((p),\ + PNG_SW_COMPRESS_memLevel, png_iCCP, (v))) +#define png_set_ICC_profile_compression_strategy(p, v) (png_setting((p),\ + PNG_SW_COMPRESS_strategy, png_iCCP, (v))) +#define png_set_ICC_profile_compression_window_bits(p, v) (png_setting((p),\ + PNG_SW_COMPRESS_windowBits, png_iCCP, (v))) +#define png_set_ICC_profile_compression_method(p, v) (png_setting((p),\ + PNG_SW_COMPRESS_method, png_iCCP, (v))) +#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ +#endif /* WRITE */ /* These next functions are called for input/output, memory, and error * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, @@ -1887,15 +2204,15 @@ PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp)); /* This callback is called only for *unknown* chunks. If * PNG_HANDLE_AS_UNKNOWN_SUPPORTED is set then it is possible to set known * chunks to be treated as unknown, however in this case the callback must do - * any processing required by the chunk (e.g. by calling the appropriate - * png_set_ APIs.) + * any processing required by the chunk and this is not possible for any chunk + * that affects the image reading (e.g. PLTE, tRNS). * * There is no write support - on write, by default, all the chunks in the * 'unknown' list are written in the specified position. * * The integer return from the callback function is interpreted thus: * - * negative: An error occured, png_chunk_error will be called. + * negative: An error occurred; png_chunk_error will be called. * zero: The chunk was not handled, the default unknown handling is used * (even if this was a chunk that would otherwise be known.) * NOTE: prior to libpng 1.7 handling values of @@ -1904,6 +2221,11 @@ PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp)); * happens) so it was not possible to discard unknown chunk data if a * user callback was installed. * positive: The chunk was handled, libpng will ignore/discard it. + * + * WARNING: if this callback is set every chunk in the stream is temporarily + * read into a memory buffer. This has potential performance implications, + * particularly for small PNG images with large amounts of ancilliary + * information. */ PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr, png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); @@ -1954,8 +2276,8 @@ PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp)); */ PNG_EXPORT(93, void, png_progressive_combine_row, (png_const_structrp png_ptr, png_bytep old_row, png_const_bytep new_row)); -#endif /* PNG_READ_INTERLACING_SUPPORTED */ -#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ +#endif /* READ_INTERLACING */ +#endif /* PROGRESSIVE_READ */ PNG_EXPORTA(94, png_voidp, png_malloc, (png_const_structrp png_ptr, png_alloc_size_t size), PNG_ALLOCATED); @@ -1986,21 +2308,21 @@ PNG_REMOVED(99, void, png_data_freer, (png_const_structrp png_ptr, png_inforp info_ptr, int freer, png_uint_32 mask), PNG_DEPRECATED) /* Flags for png_free_data */ -#define PNG_FREE_HIST 0x0008 -#define PNG_FREE_ICCP 0x0010 -#define PNG_FREE_SPLT 0x0020 -#define PNG_FREE_ROWS 0x0040 -#define PNG_FREE_PCAL 0x0080 -#define PNG_FREE_SCAL 0x0100 +#define PNG_FREE_HIST 0x0008U +#define PNG_FREE_ICCP 0x0010U +#define PNG_FREE_SPLT 0x0020U +#define PNG_FREE_ROWS 0x0040U +#define PNG_FREE_PCAL 0x0080U +#define PNG_FREE_SCAL 0x0100U #ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED -# define PNG_FREE_UNKN 0x0200 +# define PNG_FREE_UNKN 0x0200U #endif -/* PNG_FREE_LIST 0x0400 removed in 1.6.0 because it is ignored */ -#define PNG_FREE_PLTE 0x1000 -#define PNG_FREE_TRNS 0x2000 -#define PNG_FREE_TEXT 0x4000 -#define PNG_FREE_ALL 0x7fff -#define PNG_FREE_MUL 0x4220 /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ +/* PNG_FREE_LIST 0x0400U removed in 1.6.0 because it is ignored */ +#define PNG_FREE_PLTE 0x1000U +#define PNG_FREE_TRNS 0x2000U +#define PNG_FREE_TEXT 0x4000U +#define PNG_FREE_ALL 0x7fffU +#define PNG_FREE_MUL 0x4220U /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ #ifdef PNG_USER_MEM_SUPPORTED /* These were deprecated in libpng 1.6.0 and have been removed from libpng @@ -2017,16 +2339,18 @@ PNG_REMOVED(101, void, png_free_default, (png_const_structrp png_ptr, /* Fatal error in PNG image of libpng - can't continue */ PNG_EXPORTA(102, void, png_error, (png_const_structrp png_ptr, png_const_charp error_message), PNG_NORETURN); - -/* The same, but the chunk name is prepended to the error string. */ -PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr, - png_const_charp error_message), PNG_NORETURN); - #else /* Fatal error in PNG image of libpng - can't continue */ PNG_EXPORTA(104, void, png_err, (png_const_structrp png_ptr), PNG_NORETURN); # define png_error(s1,s2) png_err(s1) -# define png_chunk_error(s1,s2) png_err(s1) +#endif + +#if defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED) +/* The same, but the chunk name is prepended to the error string. */ +PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); +#else +# define png_chunk_error(s1,s2) png_error(s1,s2) #endif #ifdef PNG_WARNINGS_SUPPORTED @@ -2054,15 +2378,38 @@ PNG_EXPORT(108, void, png_chunk_benign_error, (png_const_structrp png_ptr, png_const_charp warning_message)); #endif -PNG_EXPORT(109, void, png_set_benign_errors, - (png_structrp png_ptr, int allowed)); +#define png_set_error_action(png_ptr, what, action)\ + (png_setting((png_ptr), PNG_SRW_ERROR_HANDLING, (what), (action))) + /* Control the handling of 'benign' errors; errors that can be handled in + * some way. The action is one of the following values: + */ +#define PNG_IGNORE 0 /* ignore the error; no warning or error message */ +#define PNG_WARN 1 /* call png_warning with an appropriate error message */ +#define PNG_ERROR 2 /* call png_error with the error message */ + /* 'what' is a list (bit mask) of the errors to set: */ +#define PNG_BENIGN_ERRORS (1U) +#define PNG_APP_WARNINGS (2U) +#define PNG_APP_ERRORS (4U) +#define PNG_IDAT_ERRORS (8U) +#define PNG_SAFE_ERRORS (PNG_BENIGN_ERRORS+PNG_APP_WARNINGS+PNG_APP_ERRORS) +#define PNG_ALL_ERRORS (PNG_SAFE_ERRORS+PNG_IDAT_ERRORS) + +PNG_REMOVED(109, void, png_set_benign_errors, + (png_structrp png_ptr, int allowed), PNG_EMPTY) +#define png_set_benign_errors(png_ptr, allowed) (png_setting((png_ptr),\ + PNG_SRW_ERROR_HANDLING, PNG_SAFE_ERRORS,\ + (allowed) ? PNG_WARN : PNG_ERROR)) + /* Turn all errors that can be handled into warnings, or turn them back into + * errors if 'allowed' is false. + */ + #else # ifdef PNG_ALLOW_BENIGN_ERRORS -# define png_benign_error png_warning -# define png_chunk_benign_error png_chunk_warning +# define png_benign_error(pp,e) png_warning(pp,e) +# define png_chunk_benign_error(pp,e) png_chunk_warning(pp,e) # else -# define png_benign_error png_error -# define png_chunk_benign_error png_chunk_error +# define png_benign_error(pp,e) png_error(pp,e) +# define png_chunk_benign_error(pp,e) png_chunk_error(pp,e) # endif #endif @@ -2083,16 +2430,18 @@ PNG_EXPORT(110, png_uint_32, png_get_valid, (png_const_structrp png_ptr, png_const_inforp info_ptr, png_uint_32 flag)); /* Returns number of bytes needed to hold a transformed row. */ -PNG_EXPORT(111, size_t, png_get_rowbytes, (png_const_structrp png_ptr, +PNG_EXPORT(111, png_alloc_size_t, png_get_rowbytes, (png_const_structrp png_ptr, png_const_inforp info_ptr)); -#ifdef PNG_INFO_IMAGE_SUPPORTED +#ifdef PNG_READ_PNG_SUPPORTED /* Returns row_pointers, which is an array of pointers to scanlines that was * returned from png_read_png(). */ PNG_EXPORT(112, png_bytepp, png_get_rows, (png_const_structrp png_ptr, png_const_inforp info_ptr)); +#endif +#ifdef PNG_WRITE_PNG_SUPPORTED /* Set row_pointers, which is an array of pointers to scanlines for use * by png_write_png(). */ @@ -2157,7 +2506,7 @@ PNG_EXPORT(128, png_int_32, png_get_x_offset_microns, PNG_EXPORT(129, png_int_32, png_get_y_offset_microns, (png_const_structrp png_ptr, png_const_inforp info_ptr)); -#endif /* PNG_EASY_ACCESS_SUPPORTED */ +#endif /* EASY_ACCESS */ #ifdef PNG_READ_SUPPORTED /* Returns pointer to signature string read from PNG header */ @@ -2413,7 +2762,7 @@ PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed, (png_const_structrp png_ptr, PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr, png_inforp info_ptr, int unit, png_const_charp swidth, png_const_charp sheight)); -#endif /* PNG_sCAL_SUPPORTED */ +#endif /* sCAL */ #ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED /* Provide the default handling for all unknown chunks or, optionally, for @@ -2510,8 +2859,10 @@ PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr, * except for the IHDR, PLTE, tRNS, IDAT, and IEND chunks (which continue to * be processed by libpng. */ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr, int keep, png_const_bytep chunk_list, int num_chunks)); +#endif /* HANDLE_AS_UNKNOWN */ /* The "keep" PNG_HANDLE_CHUNK_ parameter for the specified chunk is returned; * the result is therefore true (non-zero) if special handling is required, @@ -2519,7 +2870,7 @@ PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr, */ PNG_EXPORT(173, int, png_handle_as_unknown, (png_const_structrp png_ptr, png_const_bytep chunk_name)); -#endif +#endif /* SET_UNKNOWN_CHUNKS */ #ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED PNG_EXPORT(174, void, png_set_unknown_chunks, (png_structrp png_ptr, @@ -2532,6 +2883,12 @@ PNG_EXPORT(174, void, png_set_unknown_chunks, (png_structrp png_ptr, * code won't be compiled on earlier versions you can rely on * png_set_unknown_chunks(write-ptr, png_get_unknown_chunks(read-ptr)) doing * the correct thing. + * + * API CHANGE: in 1.7.0 this API will not work on read unless + * PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED is set; it may be necessary to change + * code to check the latter SUPPORTED flag. png_set_keep_unknown_chunks + * will issue a warning if it is asked to save a chunk and there is no read + * support. */ PNG_EXPORT(175, void, png_set_unknown_chunk_location, @@ -2548,17 +2905,15 @@ PNG_EXPORT(176, int, png_get_unknown_chunks, (png_const_structrp png_ptr, PNG_EXPORT(177, void, png_set_invalid, (png_const_structrp png_ptr, png_inforp info_ptr, int mask)); -#ifdef PNG_INFO_IMAGE_SUPPORTED /* The "params" pointer is currently not used and is for future expansion. */ -#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +#ifdef PNG_READ_PNG_SUPPORTED PNG_EXPORT(178, void, png_read_png, (png_structrp png_ptr, png_inforp info_ptr, int transforms, png_voidp params)); #endif -#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_PNG_SUPPORTED PNG_EXPORT(179, void, png_write_png, (png_structrp png_ptr, png_inforp info_ptr, int transforms, png_voidp params)); #endif -#endif PNG_EXPORT(180, png_const_charp, png_get_copyright, (png_const_structrp png_ptr)); @@ -2581,12 +2936,10 @@ PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structrp png_ptr, #define PNG_HANDLE_CHUNK_ALWAYS 3 #define PNG_HANDLE_CHUNK_LAST 4 -/* Strip the prepended error numbers ("#nnn ") from error and warning - * messages before passing them to the error or warning handler. - */ #ifdef PNG_ERROR_NUMBERS_SUPPORTED -PNG_EXPORT(185, void, png_set_strip_error_numbers, (png_structrp png_ptr, - png_uint_32 strip_mode)); +/* This was never implemented: */ +PNG_REMOVED(185, void, png_set_strip_error_numbers, (png_structrp png_ptr, + png_uint_32 strip_mode), PNG_EMPTY) #endif /* Added in libpng-1.2.6 */ @@ -2637,8 +2990,8 @@ PNG_FIXED_EXPORT(212, png_fixed_point, png_get_y_offset_inches_fixed, PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi, (png_const_structrp png_ptr, png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); -# endif /* PNG_pHYs_SUPPORTED */ -#endif /* PNG_INCH_CONVERSIONS_SUPPORTED */ +# endif /* pHYs */ +#endif /* INCH_CONVERSIONS */ /* Added in libpng-1.4.0 */ #ifdef PNG_IO_STATE_SUPPORTED @@ -2652,16 +3005,16 @@ PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, (png_const_structrp png_ptr)); /* The flags returned by png_get_io_state() are the following: */ -# define PNG_IO_NONE 0x0000 /* no I/O at this moment */ -# define PNG_IO_READING 0x0001 /* currently reading */ -# define PNG_IO_WRITING 0x0002 /* currently writing */ -# define PNG_IO_SIGNATURE 0x0010 /* currently at the file signature */ -# define PNG_IO_CHUNK_HDR 0x0020 /* currently at the chunk header */ -# define PNG_IO_CHUNK_DATA 0x0040 /* currently at the chunk data */ -# define PNG_IO_CHUNK_CRC 0x0080 /* currently at the chunk crc */ -# define PNG_IO_MASK_OP 0x000f /* current operation: reading/writing */ -# define PNG_IO_MASK_LOC 0x00f0 /* current location: sig/hdr/data/crc */ -#endif /* ?PNG_IO_STATE_SUPPORTED */ +# define PNG_IO_NONE 0x0000U /* no I/O at this moment */ +# define PNG_IO_READING 0x0001U /* currently reading */ +# define PNG_IO_WRITING 0x0002U /* currently writing */ +# define PNG_IO_SIGNATURE 0x0010U /* currently at the file signature */ +# define PNG_IO_CHUNK_HDR 0x0020U /* currently at the chunk header */ +# define PNG_IO_CHUNK_DATA 0x0040U /* currently at the chunk data */ +# define PNG_IO_CHUNK_CRC 0x0080U /* currently at the chunk crc */ +# define PNG_IO_MASK_OP 0x000fU /* current operation: reading/writing */ +# define PNG_IO_MASK_LOC 0x00f0U /* current location: sig/hdr/data/crc */ +#endif /* IO_STATE */ /* Interlace support. The following macros are always defined so that if * libpng interlace handling is turned off the macros may be used to handle @@ -2685,7 +3038,7 @@ PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, #define PNG_PASS_COL_OFFSET(pass) (1<<((7-(pass))>>1)) /* Two macros to help evaluate the number of rows or columns in each - * pass. This is expressed as a shift - effectively log2 of the number or + * pass. This is expressed as a shift - effectively 3-log2 of the number or * rows or columns in each 8x8 tile of the original image. */ #define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3) @@ -2725,6 +3078,25 @@ PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, #define PNG_COL_IN_INTERLACE_PASS(x, pass) \ ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1) +/* Whether the pass is in the image at all, taking into account the full image + * width and height, evaluates 'pass' lots of times, but width and height at + * most once each. + */ +#define PNG_PASS_IN_IMAGE(width, height, pass)\ + ((height) > PNG_PASS_START_ROW(pass) && (width) > PNG_PASS_START_COL(pass)) + +/* A macro to find the last pass (in the range 0 to 6) given an image width and + * height. Then two macros two find whether a given image row or column which + * is present in the pass is the last row or column in the pass. Note that + * these macros return 'true' for earlier rows or columns of the image that are + * *not* in the pass. + */ +#define PNG_LAST_PASS(width, height) ((height) > 1 ? 6 : ((width) > 1 ? 5 : 0)) +#define PNG_LAST_PASS_ROW(y, pass, height)\ + ((y) + PNG_PASS_ROW_OFFSET(pass) >= (height)) +#define PNG_LAST_PASS_COL(x, pass, width)\ + ((x) + PNG_PASS_COL_OFFSET(pass) >= (width)) + #ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED /* With these routines we avoid an integer divide, which will be slower on * most machines. However, it does take more operations than the corresponding @@ -2765,7 +3137,7 @@ PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \ (png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) + \ 32767) / 65535) -#endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */ +#endif /* READ_COMPOSITE_NODIV */ #ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED PNG_EXPORT(201, png_uint_32, png_get_uint_32, (png_const_bytep buf)); @@ -2780,10 +3152,17 @@ PNG_EXPORT(204, png_uint_32, png_get_uint_31, (png_const_structrp png_ptr, /* Place a 32-bit number into a buffer in PNG byte order (big-endian). */ #ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED PNG_EXPORT(205, void, png_save_uint_32, (png_bytep buf, png_uint_32 i)); -#endif -#ifdef PNG_SAVE_INT_32_SUPPORTED -PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i)); -#endif + +/* This becomes a macro in 1.7 because the old implementation was wrong; it + * failed to do the cast. ANSI C requires the cast to convert a negative number + * to the 2's complement form, so this just works: + */ +#define png_save_int_32(b, i) png_save_uint_32(b, i); +#endif /* WRITE_INT_FUNCTIONS */ + +/* Apps that used this will use the macro in 1.7. */ +PNG_REMOVED(206, void, png_save_int_32, (png_bytep buf, png_int_32 i), + PNG_DEPRECATED) /* Place a 16-bit number into a buffer in PNG byte order. * The parameter is declared unsigned int, not png_uint_16, @@ -2827,59 +3206,92 @@ PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i)); #endif #ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED -PNG_EXPORT(242, void, png_set_check_for_invalid_index, - (png_structrp png_ptr, int allowed)); -# ifdef PNG_GET_PALETTE_MAX_SUPPORTED +PNG_REMOVED(242, void, png_set_check_for_invalid_index, + (png_structrp png_ptr, int enabled_if_greater_than_0), PNG_EMPTY) +#define png_set_check_for_invalid_index(png_ptr, value)\ + (png_setting((png_ptr), PNG_SRW_CHECK_FOR_INVALID_INDEX, 0, (value))) + /* By default the check is enabled on both read and write when the number of + * entries in the palette is less than the maximum required by the bit depth + * of a palette image. + * + * Passing 1 to 'enabled' turns the check on in all cases. + * Passing -1 turns it off and the PNG may have invalid palette index values. + * Passing 0 restores the default. + * + * On read chunk (benign) error messages are only produced with the default + * setting; it is assumed that when the check is turned on explicitly the + * caller will call png_get_palette_max to check the result. + * + * The png_setting call returns 0. + */ +#endif /* CHECK_FOR_INVALID_INDEX */ +#ifdef PNG_GET_PALETTE_MAX_SUPPORTED PNG_EXPORT(243, int, png_get_palette_max, (png_const_structrp png_ptr, png_const_inforp info_ptr)); -# endif -#endif /* CHECK_FOR_INVALID_INDEX */ + /* The info_ptr is not used, it may be NULL in 1.7.0 (not in earlier + * versions). If the information is not available because + * png_set_check_for_invalid_index was not used to turn it on -1 is returned. + * Valid results can only be obtained after the complete image has been read, + * though it may be called at any time to get the result so far. + */ +#endif /* GET_PALETTE_MAX */ -/******************************************************************************* - * IMPLEMENTATION OPTIONS - ******************************************************************************* +/* Memory format options; these return information about the layout of the + * transformed row using the Simplified API PNG_FORMAT_ values (see below for + * the #defines). * - * Support for arbitrary implementation-specific optimizations. The API allows - * particular options to be turned on or off. 'Option' is the number of the - * option and 'onoff' is 0 (off) or non-0 (on). The value returned is given - * by the PNG_OPTION_ defines below. + * These are only relevant if read or write transforms are supported; these + * may cause the memory format of pixel data to differ from that used in the + * PNG file itself. Nevertheless the APIs are supported regardless of whether + * transforms are applied; use these to consistently and safely determine the + * layout of the image in memory. * - * HARDWARE: normally hardware capabilites, such as the Intel SSE instructions, - * are detected at run time, however sometimes it may be impossible - * to do this in user mode, in which case it is necessary to discover - * the capabilities in an OS specific way. Such capabilities are - * listed here when libpng has support for them and must be turned - * ON by the application if present. - * - * SOFTWARE: sometimes software optimizations actually result in performance - * decrease on some architectures or systems, or with some sets of - * PNG images. 'Software' options allow such optimizations to be - * selected at run time. + * Some of the same information can be obtained from png_info, however this + * does not record whether the byte or bit formats have been changed. */ -#ifdef PNG_SET_OPTION_SUPPORTED -#ifdef PNG_ARM_NEON_API_SUPPORTED -# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */ -#endif -#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */ -#define PNG_OPTION_NEXT 4 /* Next option - numbers must be even */ +PNG_EXPORT(246, unsigned int, png_memory_format, (png_structrp png_ptr)); + /* The in-memory format as a bitmask of PNG_FORMAT_FLAG_ values. All the + * flags listed below are used. If PNG_FORMAT_FLAG_INVALID is set the + * following caveats apply to the interpretation of PNG_FORMAT_FLAG_LINEAR: + * + * The gamma may differ from the sRGB (!LINEAR) or 1.0 (LINEAR). Call + * png_memory_gamma to find the correct value. + * + * The channel depth may differ from 8 (!LINEAR) or 16 (LINEAR). Call + * png_memory_channel_depth to find the correct value. + * + * It is only valid to call these APIS *after* either png_read_update_info + * or png_start_read_image on read or after the first row of an image has + * been written on write. + * + * To find the number of channels in each pixel from the returned value, + * 'fmt' use: + * + * PNG_FORMAT_CHANNELS(fmt) + */ -/* Return values: NOTE: there are four values and 'off' is *not* zero */ -#define PNG_OPTION_UNSET 0 /* Unset - defaults to off */ -#define PNG_OPTION_INVALID 1 /* Option number out of range */ -#define PNG_OPTION_OFF 2 -#define PNG_OPTION_ON 3 +PNG_EXPORT(247, unsigned int, png_memory_channel_depth, (png_structrp png_ptr)); + /* The actual depth of each channel in the image, to determine the full pixel + * depth (in bits) use: + * + * png_memory_channel_depth(pp) * PNG_FORMAT_CHANNELS(fmt) + */ -PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option, - int onoff)); -#endif - -/******************************************************************************* - * END OF HARDWARE OPTIONS - ******************************************************************************/ - -/* Maintainer: Put new public prototypes here ^, in libpng.3, and project - * defs - */ +#ifdef PNG_GAMMA_SUPPORTED +PNG_EXPORT(248, png_fixed_point, png_memory_gamma, (png_structrp png_ptr)); + /* The actual gamma of the image data, scaled by 100,000. This is the + * encoding gamma, e.g. 1/2.2 for sRGB. If the gamma is unknown this will + * return 0. + * + * On write this invariably returns 0; libpng does not change the gamma of + * the data on write. + * + * Note that this is not always the exact inverse of the 'screen gamma' + * passed to png_set_gamma; internal optimizations remove attempts to make + * small changes to the gamma value. This function returns the actual + * output value. + */ +#endif /* GAMMA */ /******************************************************************************* * Section 5: SIMPLIFIED API @@ -2952,7 +3364,7 @@ typedef struct # define PNG_IMAGE_WARNING 1 # define PNG_IMAGE_ERROR 2 /* - * The result is a two bit code such that a value more than 1 indicates + * The result is a two-bit code such that a value more than 1 indicates * a failure in the API just called: * * 0 - no warning or error @@ -2993,7 +3405,7 @@ typedef struct * * When the simplified API needs to convert between sRGB and linear colorspaces, * the actual sRGB transfer curve defined in the sRGB specification (see the - * article at http://en.wikipedia.org/wiki/SRGB) is used, not the gamma=1/2.2 + * article at https://en.wikipedia.org/wiki/SRGB) is used, not the gamma=1/2.2 * approximation used elsewhere in libpng. * * When an alpha channel is present it is expected to denote pixel coverage @@ -3039,20 +3451,79 @@ typedef struct #define PNG_FORMAT_FLAG_COLOR 0x02U /* color format: otherwise grayscale */ #define PNG_FORMAT_FLAG_LINEAR 0x04U /* 2 byte channels else 1 byte */ #define PNG_FORMAT_FLAG_COLORMAP 0x08U /* image data is color-mapped */ +#define PNG_FORMAT_FLAG_BGR 0x10U /* BGR colors, else order is RGB */ +#define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */ + /* other bits RESERVED */ -#ifdef PNG_FORMAT_BGR_SUPPORTED -# define PNG_FORMAT_FLAG_BGR 0x10U /* BGR colors, else order is RGB */ -#endif +/* The PNG color type value can be derived from a format which repesents a valid + * PNG format using the following macro. Note that if any of the non-PNG + * format elements are use, such as BGR or AFIRST, the color type value that + * results does represent the number of channels in the format but may not + * represent their order or encoding. + * + * NOTE: the format can encode illegal PNG formats, such as a colormap with + * alpha or without color; these are legal simplified API formats which produce + * data that cannot be represented as PNG regardless of channel order or + * encoding. + * + * The macro below is the bit shift version, a multiplicative version which only + * evaluates 'f' once is: + * + * ((((((((f) * 0x111) & 0x128) * 0x3) & 0x130) * 0x5) >> 6) & 0x7) + */ +#define PNG_COLOR_TYPE_FROM_FORMAT(f)\ + ((((f) & (!((f) & PNG_FORMAT_FLAG_AFILLER))) << 2) |\ + (((f) & PNG_FORMAT_FLAG_COLOR) ) |\ + (((f) & PNG_FORMAT_FLAG_COLORMAP) >> 3)) -#ifdef PNG_FORMAT_AFIRST_SUPPORTED -# define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */ -#endif +/* The inverse: note that PNG_FORMAT_FLAG_LINEAR is not set by this macro and + * that there is no handling for a 'filler' channel, consequently the macro must + * only be used on genuine PNG color types, not the result of png_get_color_type + * after transforms have been applied to the original PNG data. + */ +#define PNG_FORMAT_FROM_COLOR_TYPE(c)\ + ((((c) & PNG_COLOR_MASK_ALPHA) >> 2) |\ + (((c) & PNG_COLOR_MASK_COLOR) ) |\ + (((c) & PNG_COLOR_MASK_PALETTE) << 3)) + +/* The following flags are not used by the simplified API but may be returned + * by png_memory_format. Presence of any of these flags means that the values + * in the image (in memory) cannot be handled 'normally'. + */ +#define PNG_FORMAT_FLAG_AFILLER 0x40U /* The 'alpha' channel is a filler: + * PNG_FORMAT_FLAG_ALPHA is set however the value in the alpha channel + * is not an alpha value and (therefore) cannot be used for alpha + * computations, it is just a filler value. PNG_COLOR_TYPE_FROM_FORMAT + * will return a color type *without* PNG_COLOR_MASK_ALPHA, however + * PNG_FORMAT_CHANNELS will return the correct number, including the + * filler channel. + */ +#define PNG_FORMAT_FLAG_SWAPPED 0x80U /* bytes or bits swapped: + * When the bit depth is 16 this means that the bytes within the + * components have been swapped, when the bit depth is less than 8 + * it means the pixels within the bytes have been swapped. It should + * not be set for 8-bit compononents (it is meaningless). */ +#define PNG_FORMAT_FLAG_RANGE 0x100U /* component range not 0..bit-depth: + * Low-bit-depth grayscale components have been unpacked into bytes + * without scaling, or RGB[A] pixels have been shifted back to the + * significant-bit range from the sBIT chunk or channels (currently + * alpha or gray) have been inverted. */ +#define PNG_FORMAT_FLAG_INVALID 0x8000U /* Invalid simplified API channel depth: + * For single channel grayscale and palette indexed image data PNG + * supports bit depths of 1, 2 or 4 bits per pixel (and per channel) + * packed into bytes. The simplified API macros will not work with + * these formats (the simplified API always uses 8 or 16-bit channels). + * In the simplified API 'linear' images always have 16-bit channels + * and non-linear images are always sRGB encoded. If the INVALID flag + * is set then this may not be true; it is necessary to check the + * memory format bit-depth and gamma separately. + */ /* Commonly used formats have predefined macros. * * First the single byte (sRGB) formats: */ -#define PNG_FORMAT_GRAY 0 +#define PNG_FORMAT_GRAY 0U #define PNG_FORMAT_GA PNG_FORMAT_FLAG_ALPHA #define PNG_FORMAT_AG (PNG_FORMAT_GA|PNG_FORMAT_FLAG_AFIRST) #define PNG_FORMAT_RGB PNG_FORMAT_FLAG_COLOR @@ -3143,6 +3614,12 @@ typedef struct * color-mapped image. */ +#define PNG_FORMAT_CHANNELS(fmt) PNG_IMAGE_PIXEL_CHANNELS(fmt) + /* Synonym for the above for use with the result of png_get_memory_format. + * This exists to avoid confusion with the PNG_IMAGE_ macros which do not + * work on all possible results of png_get_memory_format. + */ + #define PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)\ PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_COMPONENT_SIZE,fmt) /* The size, in bytes, of each component in a pixel; 1 for a color-mapped @@ -3154,17 +3631,36 @@ typedef struct /* Information about the whole row, or whole image */ #define PNG_IMAGE_ROW_STRIDE(image)\ - (PNG_IMAGE_PIXEL_CHANNELS((image).format) * (image).width) + (PNG_IMAGE_PIXEL_CHANNELS((image).format) * (png_alloc_size_t)(image).width) /* Return the total number of components in a single row of the image; this * is the minimum 'row stride', the minimum count of components between each * row. For a color-mapped image this is the minimum number of bytes in a * row. + * + * WARNING: libpng 1.7: this macro now returns a png_alloc_size_t, previous + * versions returned a png_uint_32 and could overflow for images that fit in + * memory. This macro can still overflow, but if it does the row will not + * fit in memory. The simplified API functions detect this and refuse to + * handle the image. */ #define PNG_IMAGE_BUFFER_SIZE(image, row_stride)\ (PNG_IMAGE_PIXEL_COMPONENT_SIZE((image).format)*(image).height*(row_stride)) /* Return the size, in bytes, of an image buffer given a png_image and a row * stride - the number of components to leave space for in each row. + * + * WARNING: This is the total size of the image, for large images it will + * overflow on a 32-bit system. In libpng 1.7 (but not before) it returns a + * png_alloc_size_t which means that the result only overflows for + * ridiculously large PNG files. libpng checks and will refuse to handle + * such data (the PNG is probably invalid.) + * + * Take great care over the type of 'row_stride'; libpng assumes that the + * type is png_alloc_size_t, as returned by PNG_IMAGE_ROW_STRIDE. You can + * use any type you like but libpng only checks for overflow when the type is + * png_alloc_size_t. In particular for png_uint_32 on a 64-bit system you + * must do your own overflow checking. Cast row_stride as (png_alloc_size_t) + * to avoid this (check for overflow before the cast of course!) */ #define PNG_IMAGE_SIZE(image)\ @@ -3237,14 +3733,14 @@ PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image, PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image, FILE* file)); /* The PNG header is read from the stdio FILE object. */ -#endif /* PNG_STDIO_SUPPORTED */ +#endif /* STDIO */ PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image, png_const_voidp memory, size_t size)); /* The PNG header is read from the given memory buffer. */ PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image, - png_const_colorp background, void *buffer, png_int_32 row_stride, + png_const_colorp background, void *buffer, ptrdiff_t row_stride, void *colormap)); /* Finish reading the image into the supplied buffer and clean up the * png_image structure. @@ -3282,10 +3778,9 @@ PNG_EXPORT(238, void, png_image_free, (png_imagep image)); /* Free any data allocated by libpng in image->opaque, setting the pointer to * NULL. May be called at any time after the structure is initialized. */ -#endif /* PNG_SIMPLIFIED_READ_SUPPORTED */ +#endif /* SIMPLIFIED_READ */ #ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED -#ifdef PNG_STDIO_SUPPORTED /* WRITE APIS * ---------- * For write you must initialize a png_image structure to describe the image to @@ -3302,17 +3797,19 @@ PNG_EXPORT(238, void, png_image_free, (png_imagep image)); * values do not correspond to the colors in sRGB. * colormap_entries: set to the number of entries in the color-map (0 to 256) */ +#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image, const char *file, int convert_to_8bit, const void *buffer, - png_int_32 row_stride, const void *colormap)); + ptrdiff_t row_stride, const void *colormap)); /* Write the image to the named file. */ PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, - int convert_to_8_bit, const void *buffer, png_int_32 row_stride, + int convert_to_8_bit, const void *buffer, ptrdiff_t row_stride, const void *colormap)); /* Write the image to the given (FILE*). */ +#endif /* SIMPLIFIED_WRITE_STDIO */ -/* With both write APIs if image is in one of the linear formats with 16-bit +/* With all write APIs if image is in one of the linear formats with 16-bit * data then setting convert_to_8_bit will cause the output to be an 8-bit PNG * gamma encoded according to the sRGB specification, otherwise a 16-bit linear * encoded PNG file is written. @@ -3324,22 +3821,426 @@ PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, * * With all APIs row_stride is handled as in the read APIs - it is the spacing * from one row to the next in component sized units (1 or 2 bytes) and if - * negative indicates a bottom-up row layout in the buffer. + * negative indicates a bottom-up row layout in the buffer. If row_stride is + * zero, libpng will calculate it for you from the image width and number of + * channels. * - * Note that the write API does not support interlacing or sub-8-bit pixels. + * Note that the write API does not support interlacing, sub-8-bit pixels or + * most ancillary chunks. If you need to write text chunks (e.g. for copyright + * notices) you need to use one of the other APIs. */ -#endif /* PNG_STDIO_SUPPORTED */ -#endif /* PNG_SIMPLIFIED_WRITE_SUPPORTED */ + +PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory, + png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8_bit, + const void *buffer, ptrdiff_t row_stride, const void *colormap)); + /* Write the image to the given memory buffer. The function both writes the + * whole PNG data stream to *memory and updates *memory_bytes with the count + * of bytes written. + * + * 'memory' may be NULL. In this case *memory_bytes is not read however on + * success the number of bytes which would have been written will still be + * stored in *memory_bytes. On failure *memory_bytes will contain 0. + * + * If 'memory' is not NULL it must point to memory[*memory_bytes] of + * writeable memory. + * + * If the function returns success memory[*memory_bytes] (if 'memory' is not + * NULL) contains the written PNG data. *memory_bytes will always be less + * than or equal to the original value. + * + * If the function returns false and *memory_bytes was not changed an error + * occured during write. If *memory_bytes was changed, or is not 0 if + * 'memory' was NULL, the write would have succeeded but for the memory + * buffer being too small. *memory_bytes contains the required number of + * bytes and will be bigger that the original value. + */ + +#define png_image_write_get_memory_size(image, size, convert_to_8_bit, buffer,\ + row_stride, colormap)\ + png_image_write_to_memory(&(image), 0, &(size), convert_to_8_bit, buffer,\ + row_stride, colormap) + /* Return the amount of memory in 'size' required to compress this image. + * The png_image structure 'image' must be filled in as in the above + * function and must not be changed before the actual write call, the buffer + * and all other parameters must also be identical to that in the final + * write call. The 'size' variable need not be initialized. + * + * NOTE: the macro returns true/false, if false is returned 'size' will be + * set to zero and the write failed and probably will fail if tried again. + */ + +/* You can pre-allocate the buffer by making sure it is of sufficient size + * regardless of the amount of compression achieved. The buffer size will + * always be bigger than the original image and it will never be filled. The + * following macros are provided to assist in allocating the buffer. + */ +#define PNG_IMAGE_DATA_SIZE(image) (PNG_IMAGE_SIZE(image)+(image).height) + /* The number of uncompressed bytes in the PNG byte encoding of the image; + * uncompressing the PNG IDAT data will give this number of bytes. + */ +#ifndef PNG_ZLIB_MAX_SIZE +# define PNG_ZLIB_MAX_SIZE(b) ((b)+(((b)+7U)>>3)+(((b)+63U)>>6)+11U) + /* An upper bound on the number of compressed bytes given 'b' uncompressed + * bytes. This is based on deflateBounds() in zlib; different + * implementations of zlib compression may conceivably produce more data so + * if your zlib implementation is not zlib itself redefine this macro + * appropriately. + */ +#endif + +#define PNG_IMAGE_COMPRESSED_SIZE_MAX(image)\ + PNG_ZLIB_MAX_SIZE((png_alloc_size_t)PNG_IMAGE_DATA_SIZE(image)) + /* An upper bound on the size of the data in the PNG IDAT chunks. */ + +#define PNG_IMAGE_PNG_SIZE_MAX_(image, image_size)\ + ((8U/*sig*/+25U/*IHDR*/+16U/*gAMA*/+44U/*cHRM*/+12U/*IEND*/+\ + (((image).format&PNG_FORMAT_FLAG_COLORMAP)?/*colormap: PLTE, tRNS*/\ + 12U+3U*(image).colormap_entries/*PLTE data*/+\ + (((image).format&PNG_FORMAT_FLAG_ALPHA)?\ + 12U/*tRNS*/+(image).colormap_entries:0U):0U)+\ + 12U)+(12U*((image_size)/PNG_ZBUF_SIZE))/*IDAT*/+(image_size)) + /* A helper for the following macro; if your compiler cannot handle the + * following macro use this one with the result of + * PNG_IMAGE_COMPRESSED_SIZE_MAX(image) as the second argument (most + * compilers should handle this just fine.) + */ + +#define PNG_IMAGE_PNG_SIZE_MAX(image)\ + PNG_IMAGE_PNG_SIZE_MAX_(image, PNG_IMAGE_COMPRESSED_SIZE_MAX(image)) + /* An upper bound on the total length of the PNG data stream for 'image'. + * The result is of type png_alloc_size_t, on 32-bit systems this may + * overflow even though PNG_IMAGE_DATA_SIZE does not overflow; the write will + * run out of buffer space but return a corrected size which should work. + * + * NOTE: while PNG_IMAGE_SIZE cannot overflow for an image in memory this + * macro can because of the extra bytes used in the PNG byte encoding. You + * need to avoid this macro if your image size approaches the limit of your + * system memory; typically the maximum value of size_t. Use the above + * function call instead. + */ +#endif /* SIMPLIFIED_WRITE */ /******************************************************************************* * END OF SIMPLIFIED API ******************************************************************************/ +/******************************************************************************* + * Section 6: IMPLEMENTATION OPTIONS + ******************************************************************************* + * + * Change of options used during read and/or write. + * + * A number of internal options can (but do not need to be) changed to + * fine tune the implementation. These options control such things as the + * precise settings for compression, the accuracy of arithmetic used internally + * for image processing operations (gamma transformations) and, in some cases, + * the specific implementations (hardware or software optimizations.) + * + * To avoid API proliferation there is a single general API (new in 1.7) to do + * this. When a particular option is not supported by the build in libpng an + * attempt to set it will return a failure code but will be totally ignored + * unless the PNG_SF_ERROR flag is set (see below). + */ +PNG_EXPORT(249, png_int_32, png_setting, (png_structrp png_ptr, + png_uint_32 setting, png_uint_32 parameter, png_int_32 value)); + /* Alter setting 'setting' using the values of 'parameter' and 'value'. The + * result is either one of the following failure codes or a setting/parameter + * specific result code. + * + * The failure codes match the POSIX 1003.1 values (section 2.5, + * error numbers) with a preceding PNG_. (png_uint_32)result gives a number + * in the range 0x80000001U to 0x8000000fU. + */ +# define PNG_EBADF (-0x7fffffff) /* read/write error */ + /* An attempt was made to apply a read setting to a write structure or + * vice versa. + */ +# define PNG_EINVAL (-0x7ffffffe) /* invalid argument */ + /* 'png_ptr' was NULL or 'parameter' or 'value' is invalid for the given + * setting. + */ +# define PNG_EDOM (-0x7ffffffd) /* out of range */ + /* Either 'parameter' or 'value' is out of range for the given setting + * (only returned when paramter or value are used and are numeric; for + * flag values PNG_EINVAL will be returned.) + */ +# define PNG_ENOSYS (-0x7ffffff1) /* unsupported setting/param */ + /* The setting was not recognized; typically this means that libpng was + * built without the appropriate support. + */ +# define PNG_UNSUPPORTED_SETTING PNG_ENOSYS + /* For backware compatibility with earlier libpng versions and + * 'png_set_option' return codes. + */ +# define PNG_UNSET (-0x7ffffff0) /* NOT an erro code: no previous setting */ + /* The setting was not (previously) set. Returned when there is no built + * in default for a setting. Normally this means that the default will + * depend on other settings or the PNG itself. + */ + /* Results larger (more positive) than PNG_ENOSYS are success codes (even if + * negative). The value is interpreted as follows (as defined by the + * setting): + * + * 1) A signed 31-bit number in the range -0x7fffffef to +0x7fffffff + * 2) An unsigned 31 bit number in the range 0U to 0x7fffffffU + * 3) An unsigned 32 bit bit set/flag value in the range 0U to 0xfffffffU + * but excluding values in the range 0x80000000U to 0x80000000FU + * encoded as follows: + * + * if (v <= 0x7fffffffU) + * v + * else if (v > 0x8000000FU) + * -(png_int_32)-v + * + * The result can be converted by to the original (png_uint_32) simply + * by casting it as such. + */ +# define PNG_FAILED(result) ((result) <= PNG_ENOSYS) + /* The setting did not take; this includes both errors making the setting + * (e.g. parameter or value errors) and unsupported settings. Check the + * result code itself for more information. + */ +# define PNG_OK(result) ((result) > PNG_ENOSYS) + /* The setting succeeded; the result is a return code which depends on the + * particular setting. (E.g. it might be a return code or it might be the + * previous value.) + */ + +/* SETTING VALUES (generic) + * + * These are flag values that are added to the setting definitions below to + * simplify processing inside libpng and self-document the setting behavior. + * All these values have the prefix PNG_SF_ + */ +#define PNG_SF_ERROR (0x80000000U) + /* If this is set on the 'setting' argument to png_setting and a failure code + * would otherwise be returned call png_error instead. This is a convenience + * for applications that do not want to check the result code. It is never + * set by default. The error string is cryptic. + */ +#define PNG_SF_GET (0x40000000U) + /* Do not set the setting. With most settings this just allows for the + * presence of support for the setting to be checked at run time; if the + * setting is not support PNG_ENOSYS will be returned. + * + * With some settings checking of the parameter or value may be done, but + * there is no guarantee, so always supply valid parameter and value. + * + * With some settings the current setting is returned. This is typically + * only done when the default setting is configurable and not even always + * then. If the setting does this it will document the behavior. + */ +#define PNG_SF_READ (0x20000000U) + /* The setting may be applied to a read png_struct. If this is not set and + * an attempt is made to apply the setting to a read struct + * PNG_EBADF will be returned. + */ +#define PNG_SF_WRITE (0x10000000U) + /* The setting may be applied to a write png_struct. If this is not set + * and an attempt is made to apply the setting to a write struct + * PNG_EBADF will be returned. + */ + +/*********************************** WRITE ************************************/ +/* WRITE COMPRESSION SUPPORT + * + * These settings are normally accessed using the macros that are defined above; + * the function-like macros replace the API calls present in previous versions + * of libpng. + * + * 'setting' is as follows, 'parameter' is a chunk name; png_IDAT for IDAT + * compression, png_iCCP for iCCP chunk compression png_zTXt for zTZt *and* iTXt + * text chunk compression. Other values must not be used; they will result in + * PNG_ENOSYS at present but may alter compression of new chunks in the future. + * + * The value is the new compression setting. The result is is the old + * compression setting or an error code. Compression settings are documented + * in text above describing the function-like macros. PNG_UNSET is returned + * when the setting was not previously set; in this case the default may vary + * according to the actual data (e.g. length, PNG format). + * + * 0 is valid as a parameter if PNG_SF_GET is set, in that case the current or + * last setting is returned. + */ +#define PNG_SW_COMPRESS_zlib_level (PNG_SF_WRITE + 0U) +#define PNG_SW_COMPRESS_windowBits (PNG_SF_WRITE + 1U) +#define PNG_SW_COMPRESS_memLevel (PNG_SF_WRITE + 2U) +#define PNG_SW_COMPRESS_strategy (PNG_SF_WRITE + 3U) +#define PNG_SW_COMPRESS_png_level (PNG_SF_WRITE + 4U) +#define PNG_SW_COMPRESS_method (PNG_SF_WRITE + 5U) + +/* WRITE IDAT size. + * + * The size of the IDAT chunks that are written (the last may be smaller). + */ +#define PNG_SW_IDAT_size (PNG_SF_WRITE + 6U) + +/* WRITE FILTER CONTROL + * + * These settings are used by png_set_filter and png_set_row_buffers to control + * the filters used during compression. The 'filters' setting has two arguments + * however the first is the filter method (or type) and must be 0 for PNG. + * Standards based on PNG may define additional values, as with other base file + * characteristics such as the compression type, however the result would not be + * a PNG. + */ +#define PNG_SW_COMPRESS_filters (PNG_SF_WRITE + 7U) +#define PNG_SW_COMPRESS_row_buffers (PNG_SF_WRITE + 8U) + +/* WRITE ROW FLUSH CONTROL + * + * This sets the number of rows between flush calls. '0' was used to indicate + * no flushing (before the end). The maximum number of rows in a PNG is + * actually greater than the maximum of a 31-bit integer for interlaced images, + * however this doesn't matter much; the number of rows was always declared as + * 'int', so it is still passed in the 'value' argument. + */ +#define PNG_SW_FLUSH (PNG_SF_WRITE + 9U) + +/*********************************** READ *************************************/ +/* The size of the buffer used while reading IDAT chunks and, potentially, other + * compressed chunks. + */ +#define PNG_SR_COMPRESS_buffer_size (PNG_SF_READ + 1U) + /* Read compressed data buffer size, in 'parameter'. The result is 0. */ + +#define PNG_SR_GAMMA_threshold (PNG_SF_READ + 2U) +#define png_set_gamma_threshold(png_ptr, threshold)\ + (png_setting((png_ptr), PNG_SR_GAMMA_threshold, (threshold), 0)) + /* SETTING: threshold below which gamma correction is not done, the default + * (set when the library is built) is PNG_GAMMA_THRESHOLD_FIXED, the + * 'parameter' is a png_fixed_point number, the difference from PNG_FP_1 + * above which gamma correction will be performed. + * + * The value '153' is sufficient to maintain 1% accuracy in 16-bit linear + * calculations over a 655:1 range; over the maximum range possible with the + * 16-bit linear representation. Reasonable values are: + * + * 0: always do gamma correction, even if the gamma values are + * identical. The only point to this is to avoid a bug in the + * optimized (no gamma correction) code path, or for testing. + * 2: always do gamma correction if there is any significant + * difference. Notice that '1' will result in gamma correction in + * many cases when the screen gamma is the inverse of the encoding + * gamma because of inaccuracies in the representation of gamma. + * 153: do gamma correction if it is needed to maintain the accuracy of + * 16-bit linear calculations at 1% or below. + * 216: maintain 1% accuracy over a 100:1 dynamic range in 16-bit linear + * calculations. This matches the widely accepted numbers for human + * perception of differences within an image, however that doesn't + * mean that such high accuracy is required to avoid artefacts; such + * accuracy (such a low number) is only required if versions of the + * same image with and without gamma correction are to be compared + * visually. + * 5000: this is the default from libpng 1.6 and earlier. Using this + * produces changes in image contrast that are visible when suitable + * images are compared side-by-side however they are not obvious and + * it is inconceivable that a user would notice the change unless + * the user was very familiar with the image and the viewing + * environment. + * + * Values between 216 and 5000 produce varying very small changes in image + * contrast. Values above 10,000 (10%) produce noticeable increase or + * decrease in contrast which will probably change how the image is + * perceived. There is an internal limit on the maximum value which is + * currently 65%; PNG_EDOM will be returned for higher values. + * + * The result is the value that was set. + */ +#if 0 /*NYI*/ +#define PNG_SR_GAMMA_accuracy /*NYI*/ + /* SETTING: controls the accuracy of the gamma calculations when the results + * are cached. The default is PNG_DEFAULT_GAMMA_ACCURACY. The number is 100 + * times the number of bits, 'b', used in the internal tables when the input + * is linear, permitted values are 0..1600 however '0' causes the caching to + * be skipped entirely (so gives maximum accuracy with no caching!) + * + * The accuracy in the linear domain for a value 'a' is: + * + * +/-(.5/2^a) + * + * so for the default-default of 665 this means the accuracy is +/-0.5% and + * this ensures that almost-equal input values do not differ by more than 1% + * in the output, meeting the accepted requirement for human vision. + * + * The default value has no effect on input narrower than 16 bits. For n-bit + * input the total table size is ((n-v)+1)*(2^v), where 'v' is a/gamma and + * 'gamma' is the gamma encoding of the input: + * + * n a gamma 'v' table size + * 8 6.65 .45455 14.6 256 + * 16 6.65 .45455 14.6 65536 + * 16 6.65 1.0 6.65 1280 + * 16 6 1.0 6 704 + * 16 5 1.0 5 384 + */ +#endif /*NYI*/ + +#define PNG_SR_CRC_ACTION (PNG_SF_READ + 4U) + /* 'parameter' is what to do with critical chunks, 'value' is what to do with + * ancillary ones when the CRC does not match on read. 0 is returned. See + * png_set_crc_action for more information. + */ + +/*********************************** OPTIONS **********************************/ +/* png_set_option is implemented via png_setting to provide API compatibility + * with releases prior to 1.7.0 + */ +/* HARDWARE OPTIMIZATIONS + * + * Normally hardware capabilites, such as the Intel SSE instructions, are + * detected at run time, however sometimes it may be impossible to do this in + * user mode, in which case it is necessary to discover the capabilities in an + * OS specific way. Such capabilities are listed here when libpng has support + * for them and must be turned ON by the application if present. Check + * pnglibconf.h for options appropriate to your hardware. + * + * In general 'PNG_EXTENSIONS' controls hardware optimizations; these are not + * supported parts of libpng and, if there are problems with them, bugs should + * be ported to the implementers. Depending on the configuration it may not be + * possible to disable extensions at run time. + */ +#define PNG_SRW_OPTION (PNG_SF_READ+PNG_SF_WRITE + 0U) +#ifdef PNG_SET_OPTION_SUPPORTED +PNG_REMOVED(244, int, png_set_option, (png_structrp png_ptr, int option, + int onoff), PNG_EMPTY) + +#define png_set_option(p, opt, onoff)\ + (png_setting((p), PNG_SRW_OPTION, (opt), (onoff))) + /* Pre 1.7 API; in 1.7 the result values have changed numerically but not by + * name. For backward API compatibility this setting only returns one error + * code, PNG_ENOSYS and that only for option numbers out of range, otherwise + * if the option isn't supported PNG_OPTION_UNSET (PNG_UNSET) is returned. + */ +#endif /* SET_OPTION */ +#define PNG_OPTION_UNSET PNG_UNSET /* Unset - defaults to off */ +#define PNG_OPTION_INVALID PNG_ENOSYS /* Option number out of range */ +#define PNG_OPTION_OFF 0 +#define PNG_OPTION_ON 1 + +/* Specific options: */ +#define PNG_EXTENSIONS 0 /* HARDWARE: switch extensions on or off */ +#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */ +#define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */ +#define PNG_OPTION_NEXT 6 /* Next option - numbers are even */ + +#define PNG_SRW_CHECK_FOR_INVALID_INDEX (PNG_SF_READ+PNG_SF_WRITE + 1U) + /* Turn the palette index check on or off; see + * png_set_check_for_invalid_index above. + */ + +#define PNG_SRW_ERROR_HANDLING (PNG_SF_READ+PNG_SF_WRITE + 2U) + /* Change the action on issues that can be handled. */ + +/******************************************************************************* + * END OF HARDWARE OPTIONS + ******************************************************************************/ + /* The last ordinal number (this is the *last* one already used; the next * one to use is one more than this.) Maintainer, remember to add an entry to * scripts/symbols.def as well. */ #ifdef PNG_EXPORT_LAST_ORDINAL - PNG_EXPORT_LAST_ORDINAL(244); + PNG_EXPORT_LAST_ORDINAL(249); #endif #ifdef __cplusplus diff --git a/ext/libpng17/pngchunk.h b/ext/libpng17/pngchunk.h new file mode 100644 index 0000000000..319eb5ec17 --- /dev/null +++ b/ext/libpng17/pngchunk.h @@ -0,0 +1,50 @@ +#ifdef _MSC_VER +#pragma warning (disable:4028) +#endif + +/* pngchunk.h - PNG chunk descriptions + * + * Last changed in libpng 1.7.0 [(PENDING RELEASE)] + * Copyright (c) 2016 Glenn Randers-Pehrson + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * If this list is changed in any way scripts/chunkhash.c must be rebuilt and + * run to regenerate the lookup functions for the tables described from this + * list. + * + * IDAT MUST be first in the list; it must have index '0'. The order of the + * remaining chunks comes from section 5.6 "Chunk ordering" in the ISO spec + * plus the ordering rules in the PNG extensions documnet. + * + * Keep PNG_CHUNK_BEGIN and PNG_CHUNK_END at the beginning and end. + */ +PNG_CHUNK_BEGIN(IDAT, 73, 68, 65, 84, within_IDAT, after_start) +PNG_CHUNK( IHDR, 73, 72, 68, 82, before_start, at_start) +PNG_CHUNK( cHRM, 99, 72, 82, 77, before_PLTE, after_start) +PNG_CHUNK( gAMA, 103, 65, 77, 65, before_PLTE, after_start) +PNG_CHUNK( iCCP, 105, 67, 67, 80, before_PLTE, after_start) +PNG_CHUNK( sBIT, 115, 66, 73, 84, before_PLTE, after_start) +PNG_CHUNK( sRGB, 115, 82, 71, 66, before_PLTE, after_start) +PNG_CHUNK( PLTE, 80, 76, 84, 69, before_PLTE, after_start) +PNG_CHUNK( bKGD, 98, 75, 71, 68, before_IDAT, after_PLTE) +PNG_CHUNK( hIST, 104, 73, 83, 84, before_IDAT, after_PLTE) +PNG_CHUNK( tRNS, 116, 82, 78, 83, before_IDAT, after_PLTE) +PNG_CHUNK( oFFs, 111, 70, 70, 115, before_IDAT, after_start) +PNG_CHUNK( pCAL, 112, 67, 65, 76, before_IDAT, after_start) +PNG_CHUNK( sCAL, 115, 67, 65, 76, before_IDAT, after_start) +PNG_CHUNK( sTER, 115, 84, 69, 82, before_IDAT, after_start) +PNG_CHUNK( pHYs, 112, 72, 89, 115, before_IDAT, after_start) +PNG_CHUNK( sPLT, 115, 80, 76, 84, before_IDAT, after_start) +PNG_CHUNK( tIME, 116, 73, 77, 69, before_end, after_start) +PNG_CHUNK( iTXt, 105, 84, 88, 116, before_end, after_start) +PNG_CHUNK( tEXt, 116, 69, 88, 116, before_end, after_start) +PNG_CHUNK( zTXt, 122, 84, 88, 116, before_end, after_start) +PNG_CHUNK( fRAc, 102, 82, 65, 99, before_end, after_start) +PNG_CHUNK( gIFg, 103, 73, 70, 103, before_end, after_start) +PNG_CHUNK( gIFt, 103, 73, 70, 116, before_end, after_start) +PNG_CHUNK( gIFx, 103, 73, 70, 120, before_end, after_start) +PNG_CHUNK( dSIG, 100, 83, 73, 71, before_end, after_start) +PNG_CHUNK_END( IEND, 73, 69, 78, 68, before_end, after_IDAT) diff --git a/ext/libpng17/pngconf.h b/ext/libpng17/pngconf.h index de3ce894f6..89adb39afb 100644 --- a/ext/libpng17/pngconf.h +++ b/ext/libpng17/pngconf.h @@ -1,9 +1,9 @@ /* pngconf.h - machine configurable file for libpng * - * libpng version 1.7.0beta35 - March 17, 2014 + * libpng version 1.7.0beta90, August 28, 2017 * - * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -11,9 +11,7 @@ * For conditions of distribution and use, see the disclaimer * and license in png.h * - */ - -/* Any machine specific code is near the front of this file, so if you + * Any machine specific code is near the front of this file, so if you * are configuring libpng for a machine, you may want to read the section * starting here down to where it starts to typedef png_color, png_text, * and png_info. @@ -22,26 +20,6 @@ #ifndef PNGCONF_H #define PNGCONF_H -/* To do: Do all of this in scripts/pnglibconf.dfa */ -#ifdef PNG_SAFE_LIMITS_SUPPORTED -# ifdef PNG_USER_WIDTH_MAX -# undef PNG_USER_WIDTH_MAX -# define PNG_USER_WIDTH_MAX 1000000L -# endif -# ifdef PNG_USER_HEIGHT_MAX -# undef PNG_USER_HEIGHT_MAX -# define PNG_USER_HEIGHT_MAX 1000000L -# endif -# ifdef PNG_USER_CHUNK_MALLOC_MAX -# undef PNG_USER_CHUNK_MALLOC_MAX -# define PNG_USER_CHUNK_MALLOC_MAX 4000000L -# endif -# ifdef PNG_USER_CHUNK_CACHE_MAX -# undef PNG_USER_CHUNK_CACHE_MAX -# define PNG_USER_CHUNK_CACHE_MAX 128 -# endif -#endif - #ifndef PNG_BUILDING_SYMBOL_TABLE /* else includes may cause problems */ /* From libpng 1.6.0 libpng requires an ANSI X3.159-1989 ("ISOC90") compliant C @@ -210,27 +188,27 @@ * compatible with GCC or Visual C because of different calling conventions. */ # if PNG_API_RULE == 2 - /* If this line results in an error, either because __watcall is not - * understood or because of a redefine just below you cannot use *this* - * build of the library with the compiler you are using. *This* build was - * build using Watcom and applications must also be built using Watcom! - */ + /* If this line results in an error, either because __watcall is not + * understood or because of a redefine just below you cannot use *this* + * build of the library with the compiler you are using. *This* build was + * build using Watcom and applications must also be built using Watcom! + */ # define PNGCAPI __watcall # endif # if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800)) # define PNGCAPI __cdecl # if PNG_API_RULE == 1 - /* If this line results in an error __stdcall is not understood and - * PNG_API_RULE should not have been set to '1'. - */ + /* If this line results in an error __stdcall is not understood and + * PNG_API_RULE should not have been set to '1'. + */ # define PNGAPI __stdcall # endif # else - /* An older compiler, or one not detected (erroneously) above, - * if necessary override on the command line to get the correct - * variants for the compiler. - */ + /* An older compiler, or one not detected (erroneously) above, + * if necessary override on the command line to get the correct + * variants for the compiler. + */ # ifndef PNGCAPI # define PNGCAPI _cdecl # endif @@ -238,6 +216,7 @@ # define PNGAPI _stdcall # endif # endif /* compiler/api */ + /* NOTE: PNGCBAPI always defaults to PNGCAPI. */ # if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD) @@ -246,10 +225,10 @@ # if (defined(_MSC_VER) && _MSC_VER < 800) ||\ (defined(__BORLANDC__) && __BORLANDC__ < 0x500) - /* older Borland and MSC - * compilers used '__export' and required this to be after - * the type. - */ + /* older Borland and MSC + * compilers used '__export' and required this to be after + * the type. + */ # ifndef PNG_EXPORT_TYPE # define PNG_EXPORT_TYPE(type) type PNG_IMPEXP # endif @@ -265,9 +244,9 @@ # if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) # define PNGAPI _System # else /* !Windows/x86 && !OS/2 */ - /* Use the defaults, or define PNG*API on the command line (but - * this will have to be done for every compile!) - */ + /* Use the defaults, or define PNG*API on the command line (but + * this will have to be done for every compile!) + */ # endif /* other system, !OS/2 */ #endif /* !Windows/x86 */ @@ -288,7 +267,7 @@ */ #ifndef PNG_IMPEXP # if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT) - /* This forces use of a DLL, disallowing static linking */ + /* This forces use of a DLL, disallowing static linking */ # define PNG_IMPEXP PNG_DLL_IMPORT # endif @@ -360,8 +339,8 @@ * version 1.2.41. Disabling these removes the warnings but may also produce * less efficient code. */ -# if defined(__clang__) - /* Clang defines both __clang__ and __GNUC__. Check __clang__ first. */ +# if defined(__clang__) && defined(__has_attribute) + /* Clang defines both __clang__ and __GNUC__. Check __clang__ first. */ # if !defined(PNG_USE_RESULT) && __has_attribute(__warn_unused_result__) # define PNG_USE_RESULT __attribute__((__warn_unused_result__)) # endif @@ -385,6 +364,7 @@ # ifndef PNG_RESTRICT # define PNG_RESTRICT __restrict # endif + # elif defined(__GNUC__) # ifndef PNG_USE_RESULT # define PNG_USE_RESULT __attribute__((__warn_unused_result__)) diff --git a/ext/libpng17/pngdebug.h b/ext/libpng17/pngdebug.h index b43c59cde5..4bfff4de34 100644 --- a/ext/libpng17/pngdebug.h +++ b/ext/libpng17/pngdebug.h @@ -1,12 +1,11 @@ /* pngdebug.h - Debugging macros for libpng, also used in pngtest.c * - * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * Last changed in libpng 1.6.8 [December 19, 2013] + * Copyright (c) 1998-2002,2004,2006-2013 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * - * Last changed in libpng 1.6.8 [December 19, 2013] - * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h @@ -36,14 +35,11 @@ #define PNGDEBUG_H /* These settings control the formatting of messages in png.c and pngerror.c */ /* Moved to pngdebug.h at 1.5.0 */ -# ifndef PNG_LITERAL_SHARP -# define PNG_LITERAL_SHARP 0x23 -# endif # ifndef PNG_LITERAL_LEFT_SQUARE_BRACKET -# define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b +# define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5bU # endif # ifndef PNG_LITERAL_RIGHT_SQUARE_BRACKET -# define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5d +# define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5dU # endif # ifndef PNG_STRING_NEWLINE # define PNG_STRING_NEWLINE "\n" diff --git a/ext/libpng17/pngerror.c b/ext/libpng17/pngerror.c index 828cac4b3d..0b39ae4f1d 100644 --- a/ext/libpng17/pngerror.c +++ b/ext/libpng17/pngerror.c @@ -2,7 +2,7 @@ /* pngerror.c - stub functions for i/o and memory allocation * * Last changed in libpng 1.7.0 [(PENDING RELEASE)] - * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -17,6 +17,7 @@ */ #include "pngpriv.h" +#define PNG_SRC_FILE PNG_SRC_FILE_pngerror #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) @@ -26,8 +27,8 @@ static PNG_FUNCTION(void, png_default_error,PNGARG((png_const_structrp png_ptr, #ifdef PNG_WARNINGS_SUPPORTED static void /* PRIVATE */ png_default_warning PNGARG((png_const_structrp png_ptr, - png_const_charp warning_message)); -#endif /* PNG_WARNINGS_SUPPORTED */ + png_const_charp warning_message)); +#endif /* WARNINGS */ /* This function is called whenever there is a fatal error. This function * should not be changed. If there is a need to handle errors differently, @@ -37,48 +38,8 @@ png_default_warning PNGARG((png_const_structrp png_ptr, #ifdef PNG_ERROR_TEXT_SUPPORTED PNG_FUNCTION(void,PNGAPI png_error,(png_const_structrp png_ptr, png_const_charp error_message), - PNG_NORETURN) + PNG_NORETURN) { -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - char msg[16]; - if (png_ptr != NULL) - { - if (png_ptr->flags& - (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) - { - if (*error_message == PNG_LITERAL_SHARP) - { - /* Strip "#nnnn " from beginning of error message. */ - int offset; - for (offset = 1; offset<15; offset++) - if (error_message[offset] == ' ') - break; - - if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT) - { - int i; - for (i = 0; i < offset - 1; i++) - msg[i] = error_message[i + 1]; - msg[i - 1] = '\0'; - error_message = msg; - } - - else - error_message += offset; - } - - else - { - if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT) - { - msg[0] = '0'; - msg[1] = '\0'; - error_message = msg; - } - } - } - } -#endif if (png_ptr != NULL && png_ptr->error_fn != NULL) (*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr), error_message); @@ -103,14 +64,14 @@ png_err,(png_const_structrp png_ptr),PNG_NORETURN) use the default handler, which will not return. */ png_default_error(png_ptr, ""); } -#endif /* PNG_ERROR_TEXT_SUPPORTED */ +#endif /* ERROR_TEXT */ /* Utility to safely appends strings to a buffer. This never errors out so * error checking is not required in the caller. */ size_t png_safecat(png_charp buffer, size_t bufsize, size_t pos, - png_const_charp string) + png_const_charp string) { if (buffer != NULL && pos < bufsize) { @@ -129,9 +90,10 @@ png_safecat(png_charp buffer, size_t bufsize, size_t pos, * and end pointer (which should point just *beyond* the end of the buffer!) * Returns the pointer to the start of the formatted string. */ +#define PNG_HAVE_FORMAT_NUMBER /* for the code below */ png_charp png_format_number(png_const_charp start, png_charp end, int format, - png_alloc_size_t number) + png_alloc_size_t number) { int count = 0; /* number of digits output */ int mincount = 1; /* minimum number required */ @@ -152,7 +114,7 @@ png_format_number(png_const_charp start, png_charp end, int format, case PNG_NUMBER_FORMAT_fixed: /* Needs five digits (the fraction) */ mincount = 5; - if (output || number % 10 != 0) + if (output != 0 || number % 10 != 0) { *--end = digits[number % 10]; output = 1; @@ -189,7 +151,7 @@ png_format_number(png_const_charp start, png_charp end, int format, ++count; /* Float a fixed number here: */ - if (format == PNG_NUMBER_FORMAT_fixed) if (count == 5) if (end > start) + if ((format == PNG_NUMBER_FORMAT_fixed) && (count == 5) && (end > start)) { /* End of the fraction, but maybe nothing was output? In that case * drop the decimal point. If the number is a true zero handle that @@ -216,24 +178,9 @@ void PNGAPI png_warning(png_const_structrp png_ptr, png_const_charp warning_message) { int offset = 0; - if (png_ptr != NULL) - { -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - if (png_ptr->flags& - (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) -#endif - { - if (*warning_message == PNG_LITERAL_SHARP) - { - for (offset = 1; offset < 15; offset++) - if (warning_message[offset] == ' ') - break; - } - } - } if (png_ptr != NULL && png_ptr->warning_fn != NULL) (*(png_ptr->warning_fn))(png_constcast(png_structrp,png_ptr), - warning_message + offset); + warning_message + offset); else png_default_warning(png_ptr, warning_message + offset); } @@ -245,7 +192,7 @@ png_warning(png_const_structrp png_ptr, png_const_charp warning_message) */ void png_warning_parameter(png_warning_parameters p, int number, - png_const_charp string) + png_const_charp string) { if (number > 0 && number <= PNG_WARNING_PARAMETER_COUNT) (void)png_safecat(p[number-1], (sizeof p[number-1]), 0, string); @@ -253,7 +200,7 @@ png_warning_parameter(png_warning_parameters p, int number, void png_warning_parameter_unsigned(png_warning_parameters p, int number, int format, - png_alloc_size_t value) + png_alloc_size_t value) { char buffer[PNG_NUMBER_BUFFER_SIZE]; png_warning_parameter(p, number, PNG_FORMAT_NUMBER(buffer, format, value)); @@ -261,7 +208,7 @@ png_warning_parameter_unsigned(png_warning_parameters p, int number, int format, void png_warning_parameter_signed(png_warning_parameters p, int number, int format, - png_int_32 value) + png_int_32 value) { png_alloc_size_t u; png_charp str; @@ -282,7 +229,7 @@ png_warning_parameter_signed(png_warning_parameters p, int number, int format, void png_formatted_warning(png_const_structrp png_ptr, png_warning_parameters p, - png_const_charp message) + png_const_charp message) { /* The internal buffer is just 192 bytes - enough for all our messages, * overflow doesn't happen because this code checks! If someone figures @@ -355,32 +302,47 @@ png_formatted_warning(png_const_structrp png_ptr, png_warning_parameters p, */ png_warning(png_ptr, msg); } -#endif /* PNG_WARNINGS_SUPPORTED */ +#endif /* WARNINGS */ #ifdef PNG_BENIGN_ERRORS_SUPPORTED void PNGAPI png_benign_error(png_const_structrp png_ptr, png_const_charp error_message) { - if (png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) + switch (png_ptr->benign_error_action) { -# ifdef PNG_READ_SUPPORTED - if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 && - png_ptr->chunk_name != 0) - png_chunk_warning(png_ptr, error_message); - else -# endif - png_warning(png_ptr, error_message); + case PNG_ERROR: + png_chunk_error(png_ptr, error_message); + break; + + case PNG_WARN: + png_chunk_warning(png_ptr, error_message); + break; + + default: /* PNG_IGNORE */ + break; } - else +# ifndef PNG_ERROR_TEXT_SUPPORTED + PNG_UNUSED(error_message) +# endif +} + +static void +app_error(png_const_structrp png_ptr, png_const_charp error_message, + unsigned int error_action) +{ + switch (error_action) { -# ifdef PNG_READ_SUPPORTED - if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 && - png_ptr->chunk_name != 0) - png_chunk_error(png_ptr, error_message); - else -# endif - png_error(png_ptr, error_message); + case PNG_ERROR: + png_error(png_ptr, error_message); + break; + + case PNG_WARN: + png_warning(png_ptr, error_message); + break; + + default: /* PNG_IGNORE */ + break; } # ifndef PNG_ERROR_TEXT_SUPPORTED @@ -391,35 +353,28 @@ png_benign_error(png_const_structrp png_ptr, png_const_charp error_message) void /* PRIVATE */ png_app_warning(png_const_structrp png_ptr, png_const_charp error_message) { - if (png_ptr->flags & PNG_FLAG_APP_WARNINGS_WARN) - png_warning(png_ptr, error_message); - else - png_error(png_ptr, error_message); - -# ifndef PNG_ERROR_TEXT_SUPPORTED - PNG_UNUSED(error_message) -# endif + app_error(png_ptr, error_message, png_ptr->app_warning_action); } void /* PRIVATE */ png_app_error(png_const_structrp png_ptr, png_const_charp error_message) { - if (png_ptr->flags & PNG_FLAG_APP_ERRORS_WARN) - png_warning(png_ptr, error_message); - else - png_error(png_ptr, error_message); - -# ifndef PNG_ERROR_TEXT_SUPPORTED - PNG_UNUSED(error_message) -# endif + app_error(png_ptr, error_message, png_ptr->app_error_action); } #endif /* BENIGN_ERRORS */ +#define PNG_MAX_ERROR_TEXT 196 /* Currently limited by profile_error in png.c */ +#if defined(PNG_WARNINGS_SUPPORTED) || \ + (defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED)) /* These utilities are used internally to build an error message that relates - * to the current chunk. The chunk name comes from png_ptr->chunk_name, - * this is used to prefix the message. The message is limited in length - * to 63 bytes, the name characters are output as hex digits wrapped in [] - * if the character is invalid. + * to the current chunk. The chunk name comes from png_ptr->chunk_name unless + * png_ptr->zowner is set in which case that is used in preference. This is + * used to prefix the message. The message is limited in length to 63 bytes. + * The name characters are output as hex digits wrapped in [] if the character + * is invalid. + * + * Using 'zowner' means that IDAT errors at the end of the IDAT stream are still + * reported as from the IDAT chunks. */ #define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) static PNG_CONST char png_digit[16] = { @@ -427,22 +382,22 @@ static PNG_CONST char png_digit[16] = { 'A', 'B', 'C', 'D', 'E', 'F' }; -#define PNG_MAX_ERROR_TEXT 196 /* Currently limited be profile_error in png.c */ -#if defined(PNG_WARNINGS_SUPPORTED) || \ - (defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED)) static void /* PRIVATE */ png_format_buffer(png_const_structrp png_ptr, png_charp buffer, png_const_charp error_message) { - png_uint_32 chunk_name = png_ptr->chunk_name; + png_uint_32 chunk_name = png_ptr->zowner; int iout = 0, ishift = 24; + if (chunk_name == 0) + chunk_name = png_ptr->chunk_name; + while (ishift >= 0) { int c = (int)(chunk_name >> ishift) & 0xff; ishift -= 8; - if (isnonalpha(c)) + if (isnonalpha(c) != 0) { buffer[iout++] = PNG_LITERAL_LEFT_SQUARE_BRACKET; buffer[iout++] = png_digit[(c & 0xf0) >> 4]; @@ -452,7 +407,7 @@ png_format_buffer(png_const_structrp png_ptr, png_charp buffer, png_const_charp else { - buffer[iout++] = (char)c; + buffer[iout++] = png_check_char(png_ptr, c); } } @@ -473,15 +428,15 @@ png_format_buffer(png_const_structrp png_ptr, png_charp buffer, png_const_charp buffer[iout] = '\0'; } } -#endif /* PNG_WARNINGS_SUPPORTED || PNG_ERROR_TEXT_SUPPORTED */ +#endif /* WARNINGS || ERROR_TEXT */ #if defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED) PNG_FUNCTION(void,PNGAPI png_chunk_error,(png_const_structrp png_ptr, png_const_charp error_message), - PNG_NORETURN) + PNG_NORETURN) { char msg[18+PNG_MAX_ERROR_TEXT]; - if (png_ptr == NULL) + if (png_ptr == NULL || png_ptr->chunk_name == 0U) png_error(png_ptr, error_message); else @@ -490,14 +445,14 @@ png_chunk_error,(png_const_structrp png_ptr, png_const_charp error_message), png_error(png_ptr, msg); } } -#endif /* PNG_READ_SUPPORTED && PNG_ERROR_TEXT_SUPPORTED */ +#endif /* READ && ERROR_TEXT */ #ifdef PNG_WARNINGS_SUPPORTED void PNGAPI png_chunk_warning(png_const_structrp png_ptr, png_const_charp warning_message) { char msg[18+PNG_MAX_ERROR_TEXT]; - if (png_ptr == NULL) + if (png_ptr == NULL || png_ptr->chunk_name == 0U) png_warning(png_ptr, warning_message); else @@ -506,7 +461,7 @@ png_chunk_warning(png_const_structrp png_ptr, png_const_charp warning_message) png_warning(png_ptr, msg); } } -#endif /* PNG_WARNINGS_SUPPORTED */ +#endif /* WARNINGS */ #ifdef PNG_READ_SUPPORTED #ifdef PNG_BENIGN_ERRORS_SUPPORTED @@ -514,31 +469,36 @@ void PNGAPI png_chunk_benign_error(png_const_structrp png_ptr, png_const_charp error_message) { - if (png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) - png_chunk_warning(png_ptr, error_message); + switch (png_ptr->benign_error_action) + { + case PNG_ERROR: + png_chunk_error(png_ptr, error_message); + break; - else - png_chunk_error(png_ptr, error_message); + case PNG_WARN: + png_chunk_warning(png_ptr, error_message); + break; + + default: /* PNG_IGNORE */ + break; + } # ifndef PNG_ERROR_TEXT_SUPPORTED PNG_UNUSED(error_message) # endif } -#endif -#endif /* PNG_READ_SUPPORTED */ +#endif /* BENIGN_ERRORS */ +#endif /* READ */ void /* PRIVATE */ -png_chunk_report(png_const_structrp png_ptr, png_const_charp message, int error) +(png_chunk_report)(png_const_structrp png_ptr, png_const_charp message, + int error) { -# ifndef PNG_WARNINGS_SUPPORTED - PNG_UNUSED(message) -# endif - /* This is always supported, but for just read or just write it * unconditionally does the right thing. */ # if defined(PNG_READ_SUPPORTED) && defined(PNG_WRITE_SUPPORTED) - if (png_ptr->mode & PNG_IS_READ_STRUCT) + if (png_ptr->read_struct) # endif # ifdef PNG_READ_SUPPORTED @@ -546,13 +506,16 @@ png_chunk_report(png_const_structrp png_ptr, png_const_charp message, int error) if (error < PNG_CHUNK_ERROR) png_chunk_warning(png_ptr, message); - else + else if (error < PNG_CHUNK_FATAL) png_chunk_benign_error(png_ptr, message); + + else + png_chunk_error(png_ptr, message); } # endif # if defined(PNG_READ_SUPPORTED) && defined(PNG_WRITE_SUPPORTED) - else if (!(png_ptr->mode & PNG_IS_READ_STRUCT)) + else if (!png_ptr->read_struct) # endif # ifdef PNG_WRITE_SUPPORTED @@ -560,10 +523,17 @@ png_chunk_report(png_const_structrp png_ptr, png_const_charp message, int error) if (error < PNG_CHUNK_WRITE_ERROR) png_app_warning(png_ptr, message); - else + else if (error < PNG_CHUNK_FATAL) png_app_error(png_ptr, message); + + else + png_error(png_ptr, message); } # endif + +# ifndef PNG_ERROR_TEXT_SUPPORTED + PNG_UNUSED(message) +# endif } #ifdef PNG_ERROR_TEXT_SUPPORTED @@ -583,11 +553,12 @@ png_fixed_error,(png_const_structrp png_ptr, png_const_charp name),PNG_NORETURN) char msg[fixed_message_ln+PNG_MAX_ERROR_TEXT]; memcpy(msg, fixed_message, fixed_message_ln); iin = 0; - if (name != NULL) while (iin < (PNG_MAX_ERROR_TEXT-1) && name[iin] != 0) - { - msg[fixed_message_ln + iin] = name[iin]; - ++iin; - } + if (name != NULL) + while (iin < (PNG_MAX_ERROR_TEXT-1) && name[iin] != 0) + { + msg[fixed_message_ln + iin] = name[iin]; + ++iin; + } msg[fixed_message_ln + iin] = 0; png_error(png_ptr, msg); } @@ -625,7 +596,7 @@ png_set_longjmp_fn(png_structrp png_ptr, png_longjmp_ptr longjmp_fn, else { png_ptr->jmp_buf_ptr = png_voidcast(jmp_buf *, - png_malloc_warn(png_ptr, jmp_buf_size)); + png_malloc_warn(png_ptr, jmp_buf_size)); if (png_ptr->jmp_buf_ptr == NULL) return NULL; /* new NULL return on OOM */ @@ -646,9 +617,9 @@ png_set_longjmp_fn(png_structrp png_ptr, png_longjmp_ptr longjmp_fn, /* This is an internal error in libpng: somehow we have been left * with a stack allocated jmp_buf when the application regained * control. It's always possible to fix this up, but for the moment - * this is a png_error because that makes it easy to detect. + * this is an affirm because that makes it easy to detect. */ - png_error(png_ptr, "Libpng jmp_buf still allocated"); + impossible("Libpng jmp_buf still allocated"); /* png_ptr->jmp_buf_ptr = &png_ptr->jmp_buf_local; */ } } @@ -714,40 +685,9 @@ png_free_jmpbuf(png_structrp png_ptr) */ static PNG_FUNCTION(void /* PRIVATE */, png_default_error,(png_const_structrp png_ptr, png_const_charp error_message), - PNG_NORETURN) + PNG_NORETURN) { #ifdef PNG_CONSOLE_IO_SUPPORTED -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - /* Check on NULL only added in 1.5.4 */ - if (error_message != NULL && *error_message == PNG_LITERAL_SHARP) - { - /* Strip "#nnnn " from beginning of error message. */ - int offset; - char error_number[16]; - for (offset = 0; offset<15; offset++) - { - error_number[offset] = error_message[offset + 1]; - if (error_message[offset] == ' ') - break; - } - - if ((offset > 1) && (offset < 15)) - { - error_number[offset - 1] = '\0'; - fprintf(stderr, "libpng error no. %s: %s", - error_number, error_message + offset + 1); - fprintf(stderr, PNG_STRING_NEWLINE); - } - - else - { - fprintf(stderr, "libpng error: %s, offset=%d", - error_message, offset); - fprintf(stderr, PNG_STRING_NEWLINE); - } - } - else -#endif { fprintf(stderr, "libpng error: %s", error_message ? error_message : "undefined"); @@ -763,7 +703,8 @@ PNG_FUNCTION(void,PNGAPI png_longjmp,(png_const_structrp png_ptr, int val),PNG_NORETURN) { #ifdef PNG_SETJMP_SUPPORTED - if (png_ptr && png_ptr->longjmp_fn && png_ptr->jmp_buf_ptr) + if (png_ptr != NULL && png_ptr->longjmp_fn != NULL && + png_ptr->jmp_buf_ptr != NULL) png_ptr->longjmp_fn(*png_ptr->jmp_buf_ptr, val); #else PNG_UNUSED(png_ptr) @@ -772,7 +713,7 @@ png_longjmp,(png_const_structrp png_ptr, int val),PNG_NORETURN) /* If control reaches this point, png_longjmp() must not return. The only * choice is to terminate the whole process (or maybe the thread); to do - * this the ANSI-C abort() function is used unless a different method is + * this the ANSI-C abort() function is used unless a different method is * implemented by overriding the default configuration setting for * PNG_ABORT (see scripts/pnglibconf.dfa). * @@ -793,36 +734,6 @@ static void /* PRIVATE */ png_default_warning(png_const_structrp png_ptr, png_const_charp warning_message) { #ifdef PNG_CONSOLE_IO_SUPPORTED -# ifdef PNG_ERROR_NUMBERS_SUPPORTED - if (*warning_message == PNG_LITERAL_SHARP) - { - int offset; - char warning_number[16]; - for (offset = 0; offset < 15; offset++) - { - warning_number[offset] = warning_message[offset + 1]; - if (warning_message[offset] == ' ') - break; - } - - if ((offset > 1) && (offset < 15)) - { - warning_number[offset + 1] = '\0'; - fprintf(stderr, "libpng warning no. %s: %s", - warning_number, warning_message + offset); - fprintf(stderr, PNG_STRING_NEWLINE); - } - - else - { - fprintf(stderr, "libpng warning: %s", - warning_message); - fprintf(stderr, PNG_STRING_NEWLINE); - } - } - else -# endif - { fprintf(stderr, "libpng warning: %s", warning_message); fprintf(stderr, PNG_STRING_NEWLINE); @@ -832,7 +743,7 @@ png_default_warning(png_const_structrp png_ptr, png_const_charp warning_message) #endif PNG_UNUSED(png_ptr) /* Make compiler happy */ } -#endif /* PNG_WARNINGS_SUPPORTED */ +#endif /* WARNINGS */ /* This function is called when the application wants to use another method * of handling errors and warnings. Note that the error function MUST NOT @@ -870,19 +781,6 @@ png_get_error_ptr(png_const_structrp png_ptr) } -#ifdef PNG_ERROR_NUMBERS_SUPPORTED -void PNGAPI -png_set_strip_error_numbers(png_structrp png_ptr, png_uint_32 strip_mode) -{ - if (png_ptr != NULL) - { - png_ptr->flags &= - ((~(PNG_FLAG_STRIP_ERROR_NUMBERS | - PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode); - } -} -#endif - #if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\ defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) /* Currently the above both depend on SETJMP_SUPPORTED, however it would be @@ -891,7 +789,7 @@ png_set_strip_error_numbers(png_structrp png_ptr, png_uint_32 strip_mode) */ PNG_FUNCTION(void /* PRIVATE */, (PNGCBAPI png_safe_error),(png_structp png_nonconst_ptr, png_const_charp error_message), - PNG_NORETURN) + PNG_NORETURN) { const png_const_structrp png_ptr = png_nonconst_ptr; png_imagep image = png_voidcast(png_imagep, png_ptr->error_ptr); @@ -914,7 +812,7 @@ png_safe_error),(png_structp png_nonconst_ptr, png_const_charp error_message), /* Missing longjmp buffer, the following is to help debugging: */ { size_t pos = png_safecat(image->message, (sizeof image->message), 0, - "bad longjmp: "); + "bad longjmp: "); png_safecat(image->message, (sizeof image->message), pos, error_message); } @@ -967,5 +865,259 @@ png_safe_execute(png_imagep image_in, int (*function)(png_voidp), png_voidp arg) return result; } -#endif /* SIMPLIFIED READ/WRITE */ -#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ +#endif /* SIMPLIFIED READ || SIMPLIFIED_WRITE */ + +/* Affirms: minimal code in 'STABLE' builds to return control to the + * application via png_error(), more verbose code followed by PNG_ABORT for + * all other builds to ensure that internal errors are detected. + * + * The code always produces a message if it is possible, regardless of the + * setting of PNG_ERROR_TEXT_SUPPORTED, except that in stable builds + * PNG_ERROR_TEXT_SUPPORTED is honored. See pngpriv.h for the calculation of + * the two control macros PNG_RELEASE_BUILD (don't abort; stable build or rc) + * and PNG_AFFIRM_TEXT (output text.) + */ +#if PNG_AFFIRM_TEXT +# ifdef PNG_HAVE_FORMAT_NUMBER +static size_t +png_affirm_number(png_charp buffer, size_t bufsize, size_t pos, + unsigned int number, int format) +{ + char numbuf[PNG_NUMBER_BUFFER_SIZE]; + return png_safecat(buffer, bufsize, pos, + png_format_number(numbuf, numbuf + sizeof numbuf, format, number)); +} +# define affirm_number(a,b,c,d,e) png_affirm_number(a,b,c,d,e) +# else /* !HAVE_FORMAT_NUMBER */ +static size_t +png_affirm_number(png_charp buffer, size_t bufsize, size_t pos, + unsigned int number) +{ + /* binhex it; highly non-portable, assumes the ASCII character set, but + * if warnings are turned off then it is unlikely the text will get read + * anyway. This binhex variant is (48+val), where 'val' is the next 6 + * bits of the number, so it starts as '0' (for 0) and ends at 'I' for + * 63. The number is wrapped in {}, so 0 comes out as '{}' and 9 comes + * out as '{9}' and so on. + */ + char numbuf[32]; + int i = sizeof numbuf; + + numbuf[--i] = 0; + numbuf[--i] = '}'; + + do + { + if (number > 0) + numbuf[--i] = (char)/*SAFE*/((number & 63) + 48), number >>= 6; + else + { + numbuf[--i] = '{'; + break; + } + } + while (i > 0); + + return png_safecat(buffer, bufsize, pos, numbuf+i); +} +# define affirm_number(a,b,c,d,e) png_affirm_number(a,b,c,d) +#endif /* !HAVE_FORMAT_NUMBER */ + +static void +affirm_text(png_charp buffer, size_t bufsize, + param_deb(png_const_charp condition) unsigned int position) +{ + /* Format the 'position' number and output: + * + * " : affirm 'condition' failed\n" + * " libpng version - \n" + * " translated __DATE__ __TIME__" + * + * In the STABLE versions the output is the same for the last two lines + * but the first line becomes: + * + * ": affirm failed" + * + * If there is no number formatting the numbers just get replaced by + * some binhex (see the utility above). + */ + size_t pos = 0; + +# if PNG_RELEASE_BUILD /* no 'condition' parameter: minimal text */ + pos = affirm_number(buffer, bufsize, pos, position, PNG_NUMBER_FORMAT_x); + pos = png_safecat(buffer, bufsize, pos, ": affirm failed"); +# else /* !STABLE */ + /* Break down 'position' into a file name and a line number: */ + { +# define PNG_apply(f) { #f "\0", PNG_SRC_FILE_ ## f }, +# define PNG_end { "", PNG_SRC_FILE_LAST } + static struct { + char filename[28]; /* GCC checks this size */ + unsigned int base; + } fileinfo[] = { PNG_FILES }; +# undef PNG_apply +# undef PNG_end + + unsigned int i; + png_const_charp filename; + + /* Do 'nfiles' this way to avoid problems with g++ where it whines + * about (size_t) being larger than (int), even though this is a + * compile time constant: + */ +# define nfiles ((sizeof fileinfo)/(sizeof (fileinfo[0]))) + for (i=0; i < nfiles && position > fileinfo[i].base; ++i) {} + + if (i == 0 || i > nfiles) + filename = "UNKNOWN"; + else + { + filename = fileinfo[i-1].filename; + position -= fileinfo[i-1].base; + } +# undef nfiles + + pos = png_safecat(buffer, bufsize, pos, filename); + pos = png_safecat(buffer, bufsize, pos, ".c "); + pos = affirm_number(buffer, bufsize, pos, position, + PNG_NUMBER_FORMAT_u); + } + + pos = png_safecat(buffer, bufsize, pos, ": affirm '"); + pos = png_safecat(buffer, bufsize, pos, condition); + pos = png_safecat(buffer, bufsize, pos, "' failed\n"); +# endif /* !STABLE */ + + pos = png_safecat(buffer, bufsize, pos, PNG_HEADER_VERSION_STRING); + pos = png_safecat(buffer, bufsize, pos, + " translated " __DATE__ " " __TIME__); +} + +#define affirm_text(b, c, p)\ + do {\ + (affirm_text)(b, sizeof b, param_deb(c) (p));\ + } while (0) + +#endif /* AFFIRM_TEXT */ + +PNG_FUNCTION(void,png_affirm,(png_const_structrp png_ptr, + param_deb(png_const_charp condition) unsigned int position),PNG_NORETURN) +{ +# if PNG_AFFIRM_TEXT + char buffer[512]; + + affirm_text(buffer, condition, position); +# else /* !AFFIRM_TEXT */ + PNG_UNUSED(position) +# if !PNG_RELEASE_BUILD + PNG_UNUSED(condition) +# endif +# endif /* AFFIRM_TEXT */ + + /* Now in STABLE do a png_error, but in other builds output the message + * (if possible) then abort (PNG_ABORT). + */ +# if PNG_RELEASE_BUILD + png_error(png_ptr, buffer/*macro parameter used only if ERROR_TEXT*/); +# else /* !AFFIRM_ERROR */ + /* Use console IO if possible; this is because there is no guarantee that + * the app 'warning' will output anything. For certain the simplified + * API implementation just copies the message (truncated) to the image + * message buffer, which makes debugging much more difficult. + * + * Note that it is possible that neither WARNINGS nor CONSOLE_IO are + * supported; in that case no text will be output (and PNG_AFFIRM_TEXT + * will be false.) + */ +# ifdef PNG_CONSOLE_IO_SUPPORTED + fprintf(stderr, "%s\n", buffer); + PNG_UNUSED(png_ptr) +# elif defined PNG_WARNINGS_SUPPORTED + if (png_ptr != NULL && png_ptr->warning_fn != NULL) + png_ptr->warning_fn(png_constcast(png_structrp, png_ptr), buffer); + /* else no way to output the text */ +# else + PNG_UNUSED(png_ptr) +# endif + + PNG_ABORT +# endif /* AFFIRM_ERROR */ +} + +#if !PNG_RELEASE_BUILD +void /* PRIVATE */ +png_handled_affirm(png_const_structrp png_ptr, png_const_charp message, + unsigned int position) +{ +# if PNG_RELEASE_BUILD + /* testing in RC: we want to return control to the caller, so do not + * use png_affirm. + */ + char buffer[512]; + + affirm_text(buffer, message, position); + +# ifdef PNG_CONSOLE_IO_SUPPORTED + fprintf(stderr, "%s\n", buffer); +# elif defined PNG_WARNINGS_SUPPORTED + if (png_ptr != NULL && png_ptr->warning_fn != NULL) + png_ptr->warning_fn(png_constcast(png_structrp, png_ptr), buffer); + /* else no way to output the text */ +# else + PNG_UNUSED(png_ptr) +# endif +# else + png_affirm(png_ptr, message, position); +# endif +} +#endif /* !RELEASE_BUILD */ + +#ifdef PNG_RANGE_CHECK_SUPPORTED +/* The character/byte checking APIs. These do their own calls to png_affirm + * because the caller provides the position. + */ +unsigned int /* PRIVATE */ +png_bit_affirm(png_const_structrp png_ptr, unsigned int position, + unsigned int u, unsigned int bits) +{ + /* The following avoids overflow errors even if 'bits' is 16 or 32: */ + if (u <= (1U << bits)-1U) + return u; + + png_affirm(png_ptr, param_deb("(bit field) range") position); +} + +char /* PRIVATE */ +png_char_affirm(png_const_structrp png_ptr, unsigned int position, int c) +{ + if (c >= CHAR_MIN && c <= CHAR_MAX) + return (char)/*SAFE*/c; + + png_affirm(png_ptr, param_deb("(char) range") position); +} + +png_byte /* PRIVATE */ +png_byte_affirm(png_const_structrp png_ptr, unsigned int position, int b) +{ + /* For the type png_byte the limits.h values are ignored and we check + * against the values PNG expects to store in a byte: + */ + if (b >= 0 && b <= 255) + return (png_byte)/*SAFE*/b; + + png_affirm(png_ptr, param_deb("PNG byte range") position); +} + +#if INT_MAX >= 65535 +png_uint_16 /* PRIVATE */ +png_u16_affirm(png_const_structrp png_ptr, unsigned int position, int b) +{ + /* Check against the PNG 16-bit limit, as with png_byte. */ + if (b >= 0 && b <= 65535) + return (png_uint_16)/*SAFE*/b; + + png_affirm(png_ptr, param_deb("PNG 16-bit range") position); +} +#endif /* INT_MAX >= 65535 */ +#endif /* RANGE_CHECK */ +#endif /* READ || WRITE */ diff --git a/ext/libpng17/pngget.c b/ext/libpng17/pngget.c index 1db8041995..52f6fbf6c7 100644 --- a/ext/libpng17/pngget.c +++ b/ext/libpng17/pngget.c @@ -2,7 +2,7 @@ /* pngget.c - retrieval of values from info struct * * Last changed in libpng 1.7.0 [(PENDING RELEASE)] - * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -13,6 +13,7 @@ */ #include "pngpriv.h" +#define PNG_SRC_FILE PNG_SRC_FILE_pngget #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) @@ -26,16 +27,18 @@ png_get_valid(png_const_structrp png_ptr, png_const_inforp info_ptr, return(0); } -png_size_t PNGAPI +png_alloc_size_t PNGAPI png_get_rowbytes(png_const_structrp png_ptr, png_const_inforp info_ptr) { if (png_ptr != NULL && info_ptr != NULL) - return(info_ptr->rowbytes); + return png_calc_rowbytes(png_ptr, + PNG_FORMAT_CHANNELS(info_ptr->format) * info_ptr->bit_depth, + info_ptr->width); - return(0); + return 0; } -#ifdef PNG_INFO_IMAGE_SUPPORTED +#ifdef PNG_READ_PNG_SUPPORTED png_bytepp PNGAPI png_get_rows(png_const_structrp png_ptr, png_const_inforp info_ptr) { @@ -79,7 +82,8 @@ png_byte PNGAPI png_get_color_type(png_const_structrp png_ptr, png_const_inforp info_ptr) { if (png_ptr != NULL && info_ptr != NULL) - return info_ptr->color_type; + return png_check_byte(png_ptr, + PNG_COLOR_TYPE_FROM_FORMAT(info_ptr->format)); return (0); } @@ -116,7 +120,8 @@ png_get_x_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp info_ptr) { #ifdef PNG_pHYs_SUPPORTED - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pHYs) != 0) { png_debug1(1, "in %s retrieval function", "png_get_x_pixels_per_meter"); @@ -137,7 +142,8 @@ png_get_y_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp info_ptr) { #ifdef PNG_pHYs_SUPPORTED - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pHYs) != 0) { png_debug1(1, "in %s retrieval function", "png_get_y_pixels_per_meter"); @@ -157,7 +163,8 @@ png_uint_32 PNGAPI png_get_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp info_ptr) { #ifdef PNG_pHYs_SUPPORTED - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pHYs) != 0) { png_debug1(1, "in %s retrieval function", "png_get_pixels_per_meter"); @@ -179,7 +186,8 @@ png_get_pixel_aspect_ratio(png_const_structrp png_ptr, png_const_inforp info_ptr) { #ifdef PNG_READ_pHYs_SUPPORTED - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pHYs) != 0) { png_debug1(1, "in %s retrieval function", "png_get_aspect_ratio"); @@ -202,10 +210,11 @@ png_get_pixel_aspect_ratio_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr) { #ifdef PNG_READ_pHYs_SUPPORTED - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs) - && info_ptr->x_pixels_per_unit > 0 && info_ptr->y_pixels_per_unit > 0 - && info_ptr->x_pixels_per_unit <= PNG_UINT_31_MAX - && info_ptr->y_pixels_per_unit <= PNG_UINT_31_MAX) + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pHYs) != 0 && + info_ptr->x_pixels_per_unit > 0 && info_ptr->y_pixels_per_unit > 0 && + info_ptr->x_pixels_per_unit <= PNG_UINT_31_MAX && + info_ptr->y_pixels_per_unit <= PNG_UINT_31_MAX) { png_fixed_point res; @@ -215,7 +224,7 @@ png_get_pixel_aspect_ratio_fixed(png_const_structrp png_ptr, * range of 0..2^31-1; otherwise the cast might overflow. */ if (png_muldiv(&res, (png_int_32)info_ptr->y_pixels_per_unit, PNG_FP_1, - (png_int_32)info_ptr->x_pixels_per_unit)) + (png_int_32)info_ptr->x_pixels_per_unit) != 0) return res; } #else @@ -231,7 +240,8 @@ png_int_32 PNGAPI png_get_x_offset_microns(png_const_structrp png_ptr, png_const_inforp info_ptr) { #ifdef PNG_oFFs_SUPPORTED - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_oFFs) != 0) { png_debug1(1, "in %s retrieval function", "png_get_x_offset_microns"); @@ -250,7 +260,8 @@ png_int_32 PNGAPI png_get_y_offset_microns(png_const_structrp png_ptr, png_const_inforp info_ptr) { #ifdef PNG_oFFs_SUPPORTED - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_oFFs) != 0) { png_debug1(1, "in %s retrieval function", "png_get_y_offset_microns"); @@ -269,7 +280,8 @@ png_int_32 PNGAPI png_get_x_offset_pixels(png_const_structrp png_ptr, png_const_inforp info_ptr) { #ifdef PNG_oFFs_SUPPORTED - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_oFFs) != 0) { png_debug1(1, "in %s retrieval function", "png_get_x_offset_pixels"); @@ -288,7 +300,8 @@ png_int_32 PNGAPI png_get_y_offset_pixels(png_const_structrp png_ptr, png_const_inforp info_ptr) { #ifdef PNG_oFFs_SUPPORTED - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_oFFs) != 0) { png_debug1(1, "in %s retrieval function", "png_get_y_offset_pixels"); @@ -307,7 +320,7 @@ png_get_y_offset_pixels(png_const_structrp png_ptr, png_const_inforp info_ptr) static png_uint_32 ppi_from_ppm(png_uint_32 ppm) { -#if 0 +#if 0 /*NOT USED*/ /* The conversion is *(2.54/100), in binary (32 digits): * .00000110100000001001110101001001 */ @@ -328,7 +341,7 @@ ppi_from_ppm(png_uint_32 ppm) */ png_fixed_point result; if (ppm <= PNG_UINT_31_MAX && png_muldiv(&result, (png_int_32)ppm, 127, - 5000)) + 5000) != 0) return result; /* Overflow. */ @@ -356,14 +369,25 @@ png_get_y_pixels_per_inch(png_const_structrp png_ptr, png_const_inforp info_ptr) #ifdef PNG_FIXED_POINT_SUPPORTED static png_fixed_point -png_fixed_inches_from_microns(png_const_structrp png_ptr, png_int_32 microns) +png_fixed_inches_from_microns(png_const_structrp png_ptr, + png_int_32 microns) { /* Convert from metres * 1,000,000 to inches * 100,000, meters to * inches is simply *(100/2.54), so we want *(10/2.54) == 500/127. * Notice that this can overflow - a warning is output and 0 is * returned. */ - return png_muldiv_warn(png_ptr, microns, 500, 127); + png_fixed_point result; + + if (png_muldiv(&result, microns, 500, 127) != 0) + return result; + + /* TODO: review this, png_error might be better. */ + png_warning(png_ptr, "inch to microns overflow"); + return 0; +# ifndef PNG_WARNINGS_SUPPORTED + PNG_UNUSED(png_ptr) +# endif } png_fixed_point PNGAPI @@ -373,9 +397,7 @@ png_get_x_offset_inches_fixed(png_const_structrp png_ptr, return png_fixed_inches_from_microns(png_ptr, png_get_x_offset_microns(png_ptr, info_ptr)); } -#endif -#ifdef PNG_FIXED_POINT_SUPPORTED png_fixed_point PNGAPI png_get_y_offset_inches_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr) @@ -383,7 +405,7 @@ png_get_y_offset_inches_fixed(png_const_structrp png_ptr, return png_fixed_inches_from_microns(png_ptr, png_get_y_offset_microns(png_ptr, info_ptr)); } -#endif +#endif /* FIXED_POINT */ #ifdef PNG_FLOATING_POINT_SUPPORTED float PNGAPI @@ -414,7 +436,8 @@ png_get_pHYs_dpi(png_const_structrp png_ptr, png_const_inforp info_ptr, { png_uint_32 retval = 0; - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pHYs) != 0) { png_debug1(1, "in %s retrieval function", "pHYs"); @@ -445,19 +468,19 @@ png_get_pHYs_dpi(png_const_structrp png_ptr, png_const_inforp info_ptr, return (retval); } -#endif /* PNG_pHYs_SUPPORTED */ -#endif /* PNG_INCH_CONVERSIONS_SUPPORTED */ +#endif /* pHYs */ +#endif /* INCH_CONVERSIONS */ /* png_get_channels really belongs in here, too, but it's been around longer */ -#endif /* PNG_EASY_ACCESS_SUPPORTED */ +#endif /* EASY_ACCESS */ png_byte PNGAPI png_get_channels(png_const_structrp png_ptr, png_const_inforp info_ptr) { if (png_ptr != NULL && info_ptr != NULL) - return(info_ptr->channels); + return png_check_byte(png_ptr, PNG_FORMAT_CHANNELS(info_ptr->format)); return (0); } @@ -476,10 +499,11 @@ png_get_signature(png_const_structrp png_ptr, png_const_inforp info_ptr) #ifdef PNG_bKGD_SUPPORTED png_uint_32 PNGAPI png_get_bKGD(png_const_structrp png_ptr, png_inforp info_ptr, - png_color_16p *background) + png_color_16p *background) { - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) - && background != NULL) + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_bKGD) != 0 && + background != NULL) { png_debug1(1, "in %s retrieval function", "bKGD"); @@ -509,34 +533,34 @@ png_get_cHRM(png_const_structrp png_ptr, png_const_inforp info_ptr, * consistent. */ if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS)) + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) { png_debug1(1, "in %s retrieval function", "cHRM"); if (white_x != NULL) *white_x = png_float(png_ptr, - info_ptr->colorspace.end_points_xy.whitex, "cHRM white X"); + info_ptr->colorspace.end_points_xy.whitex, "cHRM white X"); if (white_y != NULL) *white_y = png_float(png_ptr, - info_ptr->colorspace.end_points_xy.whitey, "cHRM white Y"); + info_ptr->colorspace.end_points_xy.whitey, "cHRM white Y"); if (red_x != NULL) *red_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redx, - "cHRM red X"); + "cHRM red X"); if (red_y != NULL) *red_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redy, - "cHRM red Y"); + "cHRM red Y"); if (green_x != NULL) *green_x = png_float(png_ptr, - info_ptr->colorspace.end_points_xy.greenx, "cHRM green X"); + info_ptr->colorspace.end_points_xy.greenx, "cHRM green X"); if (green_y != NULL) *green_y = png_float(png_ptr, - info_ptr->colorspace.end_points_xy.greeny, "cHRM green Y"); + info_ptr->colorspace.end_points_xy.greeny, "cHRM green Y"); if (blue_x != NULL) *blue_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluex, - "cHRM blue X"); + "cHRM blue X"); if (blue_y != NULL) *blue_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluey, - "cHRM blue Y"); + "cHRM blue Y"); return (PNG_INFO_cHRM); } @@ -545,42 +569,42 @@ png_get_cHRM(png_const_structrp png_ptr, png_const_inforp info_ptr, png_uint_32 PNGAPI png_get_cHRM_XYZ(png_const_structrp png_ptr, png_const_inforp info_ptr, - double *red_X, double *red_Y, double *red_Z, double *green_X, - double *green_Y, double *green_Z, double *blue_X, double *blue_Y, - double *blue_Z) + double *red_X, double *red_Y, double *red_Z, double *green_X, + double *green_Y, double *green_Z, double *blue_X, double *blue_Y, + double *blue_Z) { if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS)) + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) { png_debug1(1, "in %s retrieval function", "cHRM_XYZ(float)"); if (red_X != NULL) *red_X = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_X, - "cHRM red X"); + "cHRM red X"); if (red_Y != NULL) *red_Y = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Y, - "cHRM red Y"); + "cHRM red Y"); if (red_Z != NULL) *red_Z = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Z, - "cHRM red Z"); + "cHRM red Z"); if (green_X != NULL) *green_X = png_float(png_ptr, - info_ptr->colorspace.end_points_XYZ.green_X, "cHRM green X"); + info_ptr->colorspace.end_points_XYZ.green_X, "cHRM green X"); if (green_Y != NULL) *green_Y = png_float(png_ptr, - info_ptr->colorspace.end_points_XYZ.green_Y, "cHRM green Y"); + info_ptr->colorspace.end_points_XYZ.green_Y, "cHRM green Y"); if (green_Z != NULL) *green_Z = png_float(png_ptr, - info_ptr->colorspace.end_points_XYZ.green_Z, "cHRM green Z"); + info_ptr->colorspace.end_points_XYZ.green_Z, "cHRM green Z"); if (blue_X != NULL) *blue_X = png_float(png_ptr, - info_ptr->colorspace.end_points_XYZ.blue_X, "cHRM blue X"); + info_ptr->colorspace.end_points_XYZ.blue_X, "cHRM blue X"); if (blue_Y != NULL) *blue_Y = png_float(png_ptr, - info_ptr->colorspace.end_points_XYZ.blue_Y, "cHRM blue Y"); + info_ptr->colorspace.end_points_XYZ.blue_Y, "cHRM blue Y"); if (blue_Z != NULL) *blue_Z = png_float(png_ptr, - info_ptr->colorspace.end_points_XYZ.blue_Z, "cHRM blue Z"); + info_ptr->colorspace.end_points_XYZ.blue_Z, "cHRM blue Z"); return (PNG_INFO_cHRM); } @@ -598,7 +622,7 @@ png_get_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, png_fixed_point *int_blue_Z) { if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS)) + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) { png_debug1(1, "in %s retrieval function", "cHRM_XYZ"); @@ -635,7 +659,7 @@ png_get_cHRM_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, png_debug1(1, "in %s retrieval function", "cHRM"); if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS)) + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) { if (white_x != NULL) *white_x = info_ptr->colorspace.end_points_xy.whitex; @@ -670,8 +694,8 @@ png_get_gAMA_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, png_debug1(1, "in %s retrieval function", "gAMA"); if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) && - file_gamma != NULL) + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 && + file_gamma != NULL) { *file_gamma = info_ptr->colorspace.gamma; return (PNG_INFO_gAMA); @@ -689,11 +713,11 @@ png_get_gAMA(png_const_structrp png_ptr, png_const_inforp info_ptr, png_debug1(1, "in %s retrieval function", "gAMA(float)"); if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 && file_gamma != NULL) { *file_gamma = png_float(png_ptr, info_ptr->colorspace.gamma, - "png_get_gAMA"); + "png_get_gAMA"); return (PNG_INFO_gAMA); } @@ -709,8 +733,8 @@ png_get_sRGB(png_const_structrp png_ptr, png_const_inforp info_ptr, { png_debug1(1, "in %s retrieval function", "sRGB"); - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB) - && file_srgb_intent != NULL) + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sRGB) != 0 && file_srgb_intent != NULL) { *file_srgb_intent = info_ptr->colorspace.rendering_intent; return (PNG_INFO_sRGB); @@ -728,9 +752,10 @@ png_get_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, { png_debug1(1, "in %s retrieval function", "iCCP"); - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP) - && name != NULL && compression_type != NULL && profile != NULL && - proflen != NULL) + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_iCCP) != 0 && + name != NULL && compression_type != NULL && profile != NULL && + proflen != NULL) { *name = info_ptr->iccp_name; *profile = info_ptr->iccp_profile; @@ -768,8 +793,8 @@ png_get_hIST(png_const_structrp png_ptr, png_inforp info_ptr, { png_debug1(1, "in %s retrieval function", "hIST"); - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) - && hist != NULL) + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_hIST) != 0 && hist != NULL) { *hist = info_ptr->hist; return (PNG_INFO_hIST); @@ -787,14 +812,20 @@ png_get_IHDR(png_const_structrp png_ptr, png_const_inforp info_ptr, { png_debug1(1, "in %s retrieval function", "IHDR"); - if (png_ptr == NULL || info_ptr == NULL || width == NULL || - height == NULL || bit_depth == NULL || color_type == NULL) + if (png_ptr == NULL || info_ptr == NULL) return (0); - *width = info_ptr->width; - *height = info_ptr->height; - *bit_depth = info_ptr->bit_depth; - *color_type = info_ptr->color_type; + if (width != NULL) + *width = info_ptr->width; + + if (height != NULL) + *height = info_ptr->height; + + if (bit_depth != NULL) + *bit_depth = info_ptr->bit_depth; + + if (color_type != NULL) + *color_type = PNG_COLOR_TYPE_FROM_FORMAT(info_ptr->format); if (compression_type != NULL) *compression_type = info_ptr->compression_type; @@ -805,16 +836,7 @@ png_get_IHDR(png_const_structrp png_ptr, png_const_inforp info_ptr, if (interlace_type != NULL) *interlace_type = info_ptr->interlace_type; - /* This is redundant if we can be sure that the info_ptr values were all - * assigned in png_set_IHDR(). We do the check anyhow in case an - * application has ignored our advice not to mess with the members - * of info_ptr directly. - */ - png_check_IHDR(png_ptr, info_ptr->width, info_ptr->height, - info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type, - info_ptr->compression_type, info_ptr->filter_type); - - return (1); + return 1; } #ifdef PNG_oFFs_SUPPORTED @@ -824,8 +846,9 @@ png_get_oFFs(png_const_structrp png_ptr, png_const_inforp info_ptr, { png_debug1(1, "in %s retrieval function", "oFFs"); - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs) - && offset_x != NULL && offset_y != NULL && unit_type != NULL) + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_oFFs) != 0 && + offset_x != NULL && offset_y != NULL && unit_type != NULL) { *offset_x = info_ptr->x_offset; *offset_y = info_ptr->y_offset; @@ -845,8 +868,9 @@ png_get_pCAL(png_const_structrp png_ptr, png_inforp info_ptr, { png_debug1(1, "in %s retrieval function", "pCAL"); - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL) - && purpose != NULL && X0 != NULL && X1 != NULL && type != NULL && + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pCAL) != 0 && + purpose != NULL && X0 != NULL && X1 != NULL && type != NULL && nparams != NULL && units != NULL && params != NULL) { *purpose = info_ptr->pcal_purpose; @@ -871,7 +895,7 @@ png_get_sCAL_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, png_fixed_point *width, png_fixed_point *height) { if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->valid & PNG_INFO_sCAL)) + (info_ptr->valid & PNG_INFO_sCAL) != 0) { *unit = info_ptr->scal_unit; /*TODO: make this work without FP support; the API is currently eliminated @@ -880,7 +904,7 @@ png_get_sCAL_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, */ *width = png_fixed(png_ptr, atof(info_ptr->scal_s_width), "sCAL width"); *height = png_fixed(png_ptr, atof(info_ptr->scal_s_height), - "sCAL height"); + "sCAL height"); return (PNG_INFO_sCAL); } @@ -894,7 +918,7 @@ png_get_sCAL(png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, double *width, double *height) { if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->valid & PNG_INFO_sCAL)) + (info_ptr->valid & PNG_INFO_sCAL) != 0) { *unit = info_ptr->scal_unit; *width = atof(info_ptr->scal_s_width); @@ -912,7 +936,7 @@ png_get_sCAL_s(png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, png_charpp width, png_charpp height) { if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->valid & PNG_INFO_sCAL)) + (info_ptr->valid & PNG_INFO_sCAL) != 0) { *unit = info_ptr->scal_unit; *width = info_ptr->scal_s_width; @@ -934,7 +958,7 @@ png_get_pHYs(png_const_structrp png_ptr, png_const_inforp info_ptr, png_debug1(1, "in %s retrieval function", "pHYs"); if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->valid & PNG_INFO_pHYs)) + (info_ptr->valid & PNG_INFO_pHYs) != 0) { if (res_x != NULL) { @@ -965,8 +989,8 @@ png_get_PLTE(png_const_structrp png_ptr, png_inforp info_ptr, { png_debug1(1, "in %s retrieval function", "PLTE"); - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_PLTE) - && palette != NULL) + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_PLTE) != 0 && palette != NULL) { *palette = info_ptr->palette; *num_palette = info_ptr->num_palette; @@ -984,8 +1008,8 @@ png_get_sBIT(png_const_structrp png_ptr, png_inforp info_ptr, { png_debug1(1, "in %s retrieval function", "sBIT"); - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT) - && sig_bit != NULL) + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sBIT) != 0 && sig_bit != NULL) { *sig_bit = &(info_ptr->sig_bit); return (PNG_INFO_sBIT); @@ -1028,8 +1052,8 @@ png_get_tIME(png_const_structrp png_ptr, png_inforp info_ptr, { png_debug1(1, "in %s retrieval function", "tIME"); - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME) - && mod_time != NULL) + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_tIME) != 0 && mod_time != NULL) { *mod_time = &(info_ptr->mod_time); return (PNG_INFO_tIME); @@ -1045,11 +1069,12 @@ png_get_tRNS(png_const_structrp png_ptr, png_inforp info_ptr, png_bytep *trans_alpha, int *num_trans, png_color_16p *trans_color) { png_uint_32 retval = 0; - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_tRNS) != 0) { png_debug1(1, "in %s retrieval function", "tRNS"); - if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + if ((info_ptr->format & PNG_FORMAT_FLAG_COLORMAP) != 0) { if (trans_alpha != NULL) { @@ -1061,7 +1086,7 @@ png_get_tRNS(png_const_structrp png_ptr, png_inforp info_ptr, *trans_color = &(info_ptr->trans_color); } - else /* if (info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) */ + else /* if (info_ptr->format not colormapped */ { if (trans_color != NULL) { @@ -1095,15 +1120,17 @@ png_get_unknown_chunks(png_const_structrp png_ptr, png_inforp info_ptr, return info_ptr->unknown_chunks_num; } - return (0); + return 0; } #endif #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED png_byte PNGAPI -png_get_rgb_to_gray_status (png_const_structrp png_ptr) +png_get_rgb_to_gray_status(png_const_structrp png_ptr) { - return (png_byte)(png_ptr ? png_ptr->rgb_to_gray_status : 0); + if (png_ptr) + return png_ptr->rgb_to_gray_status; + return 0; } #endif @@ -1111,30 +1138,24 @@ png_get_rgb_to_gray_status (png_const_structrp png_ptr) png_voidp PNGAPI png_get_user_chunk_ptr(png_const_structrp png_ptr) { - return (png_ptr ? png_ptr->user_chunk_ptr : NULL); + if (png_ptr) + return png_ptr->user_chunk_ptr; + + return NULL; } #endif -png_size_t PNGAPI +png_alloc_size_t PNGAPI png_get_compression_buffer_size(png_const_structrp png_ptr) { if (png_ptr == NULL) return 0; -# ifdef PNG_WRITE_SUPPORTED - if (png_ptr->mode & PNG_IS_READ_STRUCT) -# endif - { -# ifdef PNG_SEQUENTIAL_READ_SUPPORTED - return png_ptr->IDAT_read_size; -# else - return PNG_IDAT_READ_SIZE; -# endif - } +# if defined(PNG_SEQUENTIAL_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + return png_ptr->IDAT_size; -# ifdef PNG_WRITE_SUPPORTED - else - return png_ptr->zbuffer_size; +# else + return PNG_IDAT_READ_SIZE; /* progressive reader */ # endif } @@ -1166,7 +1187,7 @@ png_get_chunk_malloc_max (png_const_structrp png_ptr) { return (png_ptr ? png_ptr->user_chunk_malloc_max : 0); } -#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ +#endif /* SET_USER_LIMITS */ /* These functions were added to libpng 1.4.0 */ #ifdef PNG_IO_STATE_SUPPORTED @@ -1181,19 +1202,5 @@ png_get_io_chunk_type (png_const_structrp png_ptr) { return png_ptr->chunk_name; } -#endif /* ?PNG_IO_STATE_SUPPORTED */ - -#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED -# ifdef PNG_GET_PALETTE_MAX_SUPPORTED -int PNGAPI -png_get_palette_max(png_const_structrp png_ptr, png_const_inforp info_ptr) -{ - if (png_ptr != NULL && info_ptr != NULL) - return png_ptr->num_palette_max; - - return (-1); -} -# endif -#endif - -#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ +#endif /* IO_STATE */ +#endif /* READ || WRITE */ diff --git a/ext/libpng17/pnginfo.h b/ext/libpng17/pnginfo.h index 26bf26502c..a0b5d5c761 100644 --- a/ext/libpng17/pnginfo.h +++ b/ext/libpng17/pnginfo.h @@ -1,18 +1,17 @@ /* pnginfo.h - header file for PNG reference library * - * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * Last changed in libpng 1.6.1 [March 28, 2013] + * Copyright (c) 1998-2002,2004,2006-2013 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * - * Last changed in libpng 1.6.1 [March 28, 2013] - * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h */ - /* png_info is a structure that holds the information in a PNG file so +/* png_info is a structure that holds the information in a PNG file so * that the application can find out the characteristics of the image. * If you are reading the file, this structure will tell you what is * in the PNG file. If you are writing the file, fill in the information @@ -58,25 +57,17 @@ struct png_info_def /* The following are necessary for every PNG file */ png_uint_32 width; /* width of image in pixels (from IHDR) */ png_uint_32 height; /* height of image in pixels (from IHDR) */ - png_uint_32 valid; /* valid chunk data (see PNG_INFO_ below) */ - png_size_t rowbytes; /* bytes needed to hold an untransformed row */ + unsigned int valid; /* valid chunk data (see PNG_INFO_ in png.h) */ png_colorp palette; /* array of color values (valid & PNG_INFO_PLTE) */ - png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */ - png_uint_16 num_trans; /* number of transparent palette color (tRNS) */ - png_byte bit_depth; /* 1, 2, 4, 8, or 16 bits/channel (from IHDR) */ - png_byte color_type; /* see PNG_COLOR_TYPE_ below (from IHDR) */ + unsigned int num_palette:9; /* number of color entries in "palette" (PLTE) */ + unsigned int num_trans:9; /* number of transparent palette color (tRNS) */ + unsigned int bit_depth:6; /* 1, 2, 4, 8, 16 or 32 bits/channel */ + unsigned int format:PNG_RF_BITS; /* row format; see png_struct.h */ /* The following three should have been named *_method not *_type */ png_byte compression_type; /* must be PNG_COMPRESSION_TYPE_BASE (IHDR) */ png_byte filter_type; /* must be PNG_FILTER_TYPE_BASE (from IHDR) */ png_byte interlace_type; /* One of PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ - /* The following are set by png_set_IHDR, called from the application on - * write, but the are never actually used by the write code. - */ - png_byte channels; /* number of data channels per pixel (1, 2, 3, 4) */ - png_byte pixel_depth; /* number of bits per pixel */ - png_byte spare_byte; /* to align the data, and for future use */ - #ifdef PNG_READ_SUPPORTED /* This is never set during write */ png_byte signature[8]; /* magic bytes read by libpng from start of file */ @@ -106,7 +97,6 @@ struct png_info_def /* iCCP chunk data. */ png_charp iccp_name; /* profile name */ png_bytep iccp_profile; /* International Color Consortium profile data */ - png_uint_32 iccp_proflen; /* ICC profile data length */ #endif #ifdef PNG_TEXT_SUPPORTED @@ -121,13 +111,14 @@ struct png_info_def int num_text; /* number of comments read or comments to write */ int max_text; /* current size of text array */ png_textp text; /* array of comments read or comments to write */ -#endif /* PNG_TEXT_SUPPORTED */ +#endif /* TEXT */ #ifdef PNG_tIME_SUPPORTED /* The tIME chunk holds the last time the displayed image data was * modified. See the png_time struct for the contents of this struct. */ png_time mod_time; + png_byte time_location; #endif #ifdef PNG_sBIT_SUPPORTED @@ -224,7 +215,7 @@ defined(PNG_READ_BACKGROUND_SUPPORTED) /* Storage for unknown chunks that the library doesn't recognize. */ png_unknown_chunkp unknown_chunks; - /* The type of this field is limited by the type of + /* The type of this field is limited by the type of * png_struct::user_chunk_cache_max, else overflow can occur. */ int unknown_chunks_num; diff --git a/ext/libpng17/pnglibconf.h b/ext/libpng17/pnglibconf.h index b93120c791..f75c7c3a35 100644 --- a/ext/libpng17/pnglibconf.h +++ b/ext/libpng17/pnglibconf.h @@ -1,10 +1,10 @@ -/* libpng 1.7.0beta35 STANDARD API DEFINITION */ +/* libpng 1.7.0beta90 STANDARD API DEFINITION */ /* pnglibconf.h - library build configuration */ -/* Libpng version 1.7.0beta35 - March 22, 2014 */ +/* libpng version 1.7.0beta90, August 28, 2017 */ -/* Copyright (c) 1998-2013 Glenn Randers-Pehrson */ +/* Copyright (c) 1998-2016 Glenn Randers-Pehrson */ /* This code is released under the libpng license. */ /* For conditions of distribution and use, see the disclaimer */ @@ -18,8 +18,6 @@ /* options */ #define PNG_16BIT_SUPPORTED #define PNG_ALIGNED_MEMORY_SUPPORTED -/*#undef PNG_ARM_NEON_API_SUPPORTED*/ -/*#undef PNG_ARM_NEON_CHECK_SUPPORTED*/ #define PNG_BENIGN_ERRORS_SUPPORTED #define PNG_BENIGN_READ_ERRORS_SUPPORTED /*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/ @@ -29,7 +27,6 @@ #define PNG_CONSOLE_IO_SUPPORTED #define PNG_CONVERT_tIME_SUPPORTED #define PNG_EASY_ACCESS_SUPPORTED -/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/ #define PNG_ERROR_TEXT_SUPPORTED #define PNG_FIXED_POINT_SUPPORTED #define PNG_FLOATING_ARITHMETIC_SUPPORTED @@ -43,7 +40,9 @@ #define PNG_INFO_IMAGE_SUPPORTED #define PNG_IO_STATE_SUPPORTED #define PNG_MNG_FEATURES_SUPPORTED -#define PNG_POINTER_INDEXING_SUPPORTED +#define PNG_MNG_READ_FEATURES_SUPPORTED +#define PNG_MNG_WRITE_FEATURES_SUPPORTED +#define PNG_PALETTE_MAX_SUPPORTED #define PNG_PROGRESSIVE_READ_SUPPORTED #define PNG_READ_16BIT_SUPPORTED #define PNG_READ_ALPHA_MODE_SUPPORTED @@ -59,6 +58,7 @@ #define PNG_READ_GAMMA_SUPPORTED #define PNG_READ_GET_PALETTE_MAX_SUPPORTED #define PNG_READ_GRAY_TO_RGB_SUPPORTED +#define PNG_READ_IMAGE_SUPPORTED #define PNG_READ_INTERLACING_SUPPORTED #define PNG_READ_INT_FUNCTIONS_SUPPORTED #define PNG_READ_INVERT_ALPHA_SUPPORTED @@ -66,6 +66,7 @@ #define PNG_READ_OPT_PLTE_SUPPORTED #define PNG_READ_PACKSWAP_SUPPORTED #define PNG_READ_PACK_SUPPORTED +#define PNG_READ_PNG_SUPPORTED #define PNG_READ_QUANTIZE_SUPPORTED #define PNG_READ_RGB_TO_GRAY_SUPPORTED #define PNG_READ_SCALE_16_TO_8_SUPPORTED @@ -97,9 +98,8 @@ #define PNG_READ_tIME_SUPPORTED #define PNG_READ_tRNS_SUPPORTED #define PNG_READ_zTXt_SUPPORTED -/*#undef PNG_SAFE_LIMITS_SUPPORTED*/ -#define PNG_SAVE_INT_32_SUPPORTED #define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SELECT_FILTER_SUPPORTED #define PNG_SEQUENTIAL_READ_SUPPORTED #define PNG_SETJMP_SUPPORTED #define PNG_SET_OPTION_SUPPORTED @@ -110,11 +110,13 @@ #define PNG_SIMPLIFIED_READ_SUPPORTED #define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED #define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED #define PNG_SIMPLIFIED_WRITE_SUPPORTED #define PNG_STDIO_SUPPORTED #define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED #define PNG_TEXT_SUPPORTED #define PNG_TIME_RFC1123_SUPPORTED +#define PNG_TRANSFORM_MECH_SUPPORTED #define PNG_UNKNOWN_CHUNKS_SUPPORTED #define PNG_USER_CHUNKS_SUPPORTED #define PNG_USER_LIMITS_SUPPORTED @@ -127,6 +129,7 @@ #define PNG_WRITE_BGR_SUPPORTED #define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED #define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED #define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED #define PNG_WRITE_FILLER_SUPPORTED #define PNG_WRITE_FILTER_SUPPORTED @@ -139,6 +142,7 @@ #define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED #define PNG_WRITE_PACKSWAP_SUPPORTED #define PNG_WRITE_PACK_SUPPORTED +#define PNG_WRITE_PNG_SUPPORTED #define PNG_WRITE_SHIFT_SUPPORTED #define PNG_WRITE_SUPPORTED #define PNG_WRITE_SWAP_ALPHA_SUPPORTED @@ -186,24 +190,23 @@ /* settings */ #define PNG_ABORT { (abort()); } #define PNG_API_RULE 0 -#define PNG_COST_SHIFT 3 +#define PNG_DEFAULT_COMPRESSION_LEVEL PNG_COMPRESSION_MEDIUM +#define PNG_DEFAULT_GAMMA_ACCURACY 665 #define PNG_DEFAULT_READ_MACROS 1 -#define PNG_GAMMA_THRESHOLD_FIXED 5000 -#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE +#define PNG_GAMMA_THRESHOLD_FIXED 153 +#define PNG_IDAT_READ_SIZE 4096 #define PNG_INFLATE_BUF_SIZE 1024 #define PNG_MAX_GAMMA_8 11 #define PNG_QUANTIZE_BLUE_BITS 5 #define PNG_QUANTIZE_GREEN_BITS 5 #define PNG_QUANTIZE_RED_BITS 5 -#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1) -#define PNG_TEXT_Z_DEFAULT_STRATEGY 0 -#define PNG_WEIGHT_SHIFT 8 -#define PNG_ZLIB_VERNUM 0 /* unknown */ -#define PNG_ZBUF_SIZE 8192 +#define PNG_USER_CHUNK_CACHE_MAX 1000 +#define PNG_USER_CHUNK_MALLOC_MAX 8000000 +#define PNG_USER_HEIGHT_MAX 1000000 +#define PNG_USER_WIDTH_MAX 1000000 +#define PNG_ZBUF_SIZE 4096 #define PNG_ZLIB_HEADER -#define PNG_Z_DEFAULT_COMPRESSION (-1) -#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0 -#define PNG_Z_DEFAULT_STRATEGY 1 +#define PNG_ZLIB_VERNUM 0 #define PNG_sCAL_PRECISION 5 #define PNG_sRGB_PROFILE_CHECKS 2 /* end of settings */ diff --git a/ext/libpng17/pngmem.c b/ext/libpng17/pngmem.c index db336c8698..0593bb35f2 100644 --- a/ext/libpng17/pngmem.c +++ b/ext/libpng17/pngmem.c @@ -2,7 +2,7 @@ /* pngmem.c - stub functions for memory allocation * * Last changed in libpng 1.7.0 [(PENDING RELEASE)] - * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -18,6 +18,7 @@ */ #include "pngpriv.h" +#define PNG_SRC_FILE PNG_SRC_FILE_pngmem #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) /* Free a png_struct */ @@ -41,7 +42,7 @@ png_destroy_png_struct(png_structrp png_ptr) } /* Allocate memory. For reasonable files, size should never exceed - * 64K. However, zlib may allocate more then 64K if you don't tell + * 64K. However, zlib may allocate more than 64K if you don't tell * it not to. See zconf.h and png.h for more information. zlib does * need to allocate exactly 64K, so whatever you call here must * have the ability to do that. @@ -66,7 +67,7 @@ png_calloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED) */ PNG_FUNCTION(png_voidp /* PRIVATE */, png_malloc_base,(png_const_structrp png_ptr, png_alloc_size_t size), - PNG_ALLOCATED) + PNG_ALLOCATED) { /* Moved to png_malloc_base from png_malloc_default in 1.6.0; the DOS * allocators have also been removed in 1.6.0, so any 16-bit system now has @@ -77,19 +78,26 @@ png_malloc_base,(png_const_structrp png_ptr, png_alloc_size_t size), PNG_UNUSED(png_ptr) #endif - if (size > 0 && size <= PNG_SIZE_MAX -# ifdef PNG_MAX_MALLOC_64K - && size <= 65536U -# endif - ) + /* Some compilers complain that this is always true. However, it + * can be false when integer overflow happens. + */ +#ifdef PNG_MAX_MALLOC_64K + if (size > 0 && size <= PNG_SIZE_MAX && size <= 65536U) +#else + if (size > 0 && size <= PNG_SIZE_MAX) +#endif { + png_voidp result; + #ifdef PNG_USER_MEM_SUPPORTED if (png_ptr != NULL && png_ptr->malloc_fn != NULL) - return png_ptr->malloc_fn(png_constcast(png_structrp,png_ptr), size); + result = png_ptr->malloc_fn(png_constcast(png_structrp,png_ptr), size); else #endif - return malloc((size_t)size); /* checked for truncation above */ + result = malloc((size_t)size); /* checked for truncation above */ + + return result; } else @@ -104,7 +112,7 @@ png_malloc_base,(png_const_structrp png_ptr, png_alloc_size_t size), */ static png_voidp png_malloc_array_checked(png_const_structrp png_ptr, int nelements, - size_t element_size) + size_t element_size) { png_alloc_size_t req = nelements; /* known to be > 0 */ @@ -117,22 +125,19 @@ png_malloc_array_checked(png_const_structrp png_ptr, int nelements, PNG_FUNCTION(png_voidp /* PRIVATE */, png_malloc_array,(png_const_structrp png_ptr, int nelements, - size_t element_size),PNG_ALLOCATED) + size_t element_size),PNG_ALLOCATED) { - if (nelements <= 0 || element_size == 0) - png_error(png_ptr, "internal error: array alloc"); - + affirm(nelements > 0 && element_size > 0); return png_malloc_array_checked(png_ptr, nelements, element_size); } PNG_FUNCTION(png_voidp /* PRIVATE */, png_realloc_array,(png_structrp png_ptr, png_const_voidp old_array, - int old_elements, int add_elements, size_t element_size),PNG_ALLOCATED) + int old_elements, int add_elements, size_t element_size),PNG_ALLOCATED) { /* These are internal errors: */ - if (add_elements <= 0 || element_size == 0 || old_elements < 0 || - (old_array == NULL && old_elements > 0)) - png_error(png_ptr, "internal error: array realloc"); + affirm(add_elements > 0 && element_size > 0 && old_elements >= 0 && + (old_array != NULL || old_elements == 0)); /* Check for overflow on the elements count (so the caller does not have to * check.) @@ -140,7 +145,7 @@ png_realloc_array,(png_structrp png_ptr, png_const_voidp old_array, if (add_elements <= INT_MAX - old_elements) { png_voidp new_array = png_malloc_array_checked(png_ptr, - old_elements+add_elements, element_size); + old_elements+add_elements, element_size); if (new_array != NULL) { @@ -151,7 +156,7 @@ png_realloc_array,(png_structrp png_ptr, png_const_voidp old_array, memcpy(new_array, old_array, element_size*(unsigned)old_elements); memset((char*)new_array + element_size*(unsigned)old_elements, 0, - element_size*(unsigned)add_elements); + element_size*(unsigned)add_elements); return new_array; } @@ -196,7 +201,7 @@ png_malloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED) */ PNG_FUNCTION(png_voidp,PNGAPI png_malloc_warn,(png_const_structrp png_ptr, png_alloc_size_t size), - PNG_ALLOCATED) + PNG_ALLOCATED) { if (png_ptr != NULL) { @@ -225,8 +230,11 @@ png_free(png_const_structrp png_ptr, png_voidp ptr) png_ptr->free_fn(png_constcast(png_structrp,png_ptr), ptr); else -#endif /* PNG_USER_MEM_SUPPORTED */ free(ptr); +#else + free(ptr); +#endif /* USER_MEM */ + } #ifdef PNG_USER_MEM_SUPPORTED @@ -257,5 +265,5 @@ png_get_mem_ptr(png_const_structrp png_ptr) return png_ptr->mem_ptr; } -#endif /* PNG_USER_MEM_SUPPORTED */ -#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ +#endif /* USER_MEM */ +#endif /* READ || WRITE */ diff --git a/ext/libpng17/pngpread.c b/ext/libpng17/pngpread.c index 0b251f7363..c7e5425aaf 100644 --- a/ext/libpng17/pngpread.c +++ b/ext/libpng17/pngpread.c @@ -2,7 +2,7 @@ /* pngpread.c - read a png file in push mode * * Last changed in libpng 1.7.0 [(PENDING RELEASE)] - * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -12,608 +12,12 @@ */ #include "pngpriv.h" +#define PNG_SRC_FILE PNG_SRC_FILE_pngpread #ifdef PNG_PROGRESSIVE_READ_SUPPORTED -/* Push model modes */ -#define PNG_READ_SIG_MODE 0 -#define PNG_READ_CHUNK_MODE 1 -#define PNG_READ_IDAT_MODE 2 -#define PNG_SKIP_MODE 3 -#define PNG_READ_tEXt_MODE 4 -#define PNG_READ_zTXt_MODE 5 -#define PNG_READ_DONE_MODE 6 -#define PNG_READ_iTXt_MODE 7 -#define PNG_ERROR_MODE 8 - -void PNGAPI -png_process_data(png_structrp png_ptr, png_inforp info_ptr, - png_bytep buffer, png_size_t buffer_size) -{ - if (png_ptr == NULL || info_ptr == NULL) - return; - - png_push_restore_buffer(png_ptr, buffer, buffer_size); - - while (png_ptr->buffer_size) - { - png_process_some_data(png_ptr, info_ptr); - } -} - -png_size_t PNGAPI -png_process_data_pause(png_structrp png_ptr, int save) -{ - if (png_ptr != NULL) - { - /* It's easiest for the caller if we do the save, then the caller doesn't - * have to supply the same data again: - */ - if (save != 0) - png_push_save_buffer(png_ptr); - else - { - /* This includes any pending saved bytes: */ - png_size_t remaining = png_ptr->buffer_size; - png_ptr->buffer_size = 0; - - /* So subtract the saved buffer size, unless all the data - * is actually 'saved', in which case we just return 0 - */ - if (png_ptr->save_buffer_size < remaining) - return remaining - png_ptr->save_buffer_size; - } - } - - return 0; -} - -png_uint_32 PNGAPI -png_process_data_skip(png_structrp png_ptr) -{ - png_uint_32 remaining = 0; - - if (png_ptr != NULL && png_ptr->process_mode == PNG_SKIP_MODE && - png_ptr->skip_length > 0) - { - /* At the end of png_process_data the buffer size must be 0 (see the loop - * above) so we can detect a broken call here: - */ - if (png_ptr->buffer_size != 0) - png_error(png_ptr, - "png_process_data_skip called inside png_process_data"); - - /* If is impossible for there to be a saved buffer at this point - - * otherwise we could not be in SKIP mode. This will also happen if - * png_process_skip is called inside png_process_data (but only very - * rarely.) - */ - if (png_ptr->save_buffer_size != 0) - png_error(png_ptr, "png_process_data_skip called with saved data"); - - remaining = png_ptr->skip_length; - png_ptr->skip_length = 0; - png_ptr->process_mode = PNG_READ_CHUNK_MODE; - } - - return remaining; -} - -/* What we do with the incoming data depends on what we were previously - * doing before we ran out of data... - */ -void /* PRIVATE */ -png_process_some_data(png_structrp png_ptr, png_inforp info_ptr) -{ - if (png_ptr == NULL) - return; - - switch (png_ptr->process_mode) - { - case PNG_READ_SIG_MODE: - { - png_push_read_sig(png_ptr, info_ptr); - break; - } - - case PNG_READ_CHUNK_MODE: - { - png_push_read_chunk(png_ptr, info_ptr); - break; - } - - case PNG_READ_IDAT_MODE: - { - png_push_read_IDAT(png_ptr); - break; - } - - case PNG_SKIP_MODE: - { - png_push_crc_finish(png_ptr); - break; - } - - default: - { - png_ptr->buffer_size = 0; - break; - } - } -} - -/* Read any remaining signature bytes from the stream and compare them with - * the correct PNG signature. It is possible that this routine is called - * with bytes already read from the signature, either because they have been - * checked by the calling application, or because of multiple calls to this - * routine. - */ -void /* PRIVATE */ -png_push_read_sig(png_structrp png_ptr, png_inforp info_ptr) -{ - png_size_t num_checked = png_ptr->sig_bytes, /* SAFE, does not exceed 8 */ - num_to_check = 8 - num_checked; - - if (png_ptr->buffer_size < num_to_check) - { - num_to_check = png_ptr->buffer_size; - } - - png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]), - num_to_check); - png_ptr->sig_bytes = (png_byte)(png_ptr->sig_bytes + num_to_check); - - if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) - { - if (num_checked < 4 && - png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) - png_error(png_ptr, "Not a PNG file"); - - else - png_error(png_ptr, "PNG file corrupted by ASCII conversion"); - } - else - { - if (png_ptr->sig_bytes >= 8) - { - png_ptr->process_mode = PNG_READ_CHUNK_MODE; - } - } -} - -void /* PRIVATE */ -png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr) -{ - png_uint_32 chunk_name; -#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED - int keep; /* unknown handling method */ -#endif - - /* First we make sure we have enough data for the 4 byte chunk name - * and the 4 byte chunk length before proceeding with decoding the - * chunk data. To fully decode each of these chunks, we also make - * sure we have enough data in the buffer for the 4 byte CRC at the - * end of every chunk (except IDAT, which is handled separately). - */ - if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER)) - { - png_byte chunk_length[4]; - png_byte chunk_tag[4]; - - if (png_ptr->buffer_size < 8) - { - png_push_save_buffer(png_ptr); - return; - } - - png_push_fill_buffer(png_ptr, chunk_length, 4); - png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length); - png_reset_crc(png_ptr); - png_crc_read(png_ptr, chunk_tag, 4); - png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag); - png_check_chunk_name(png_ptr, png_ptr->chunk_name); - png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; - } - - chunk_name = png_ptr->chunk_name; - - if (chunk_name == png_IDAT) - { - if (png_ptr->mode & PNG_AFTER_IDAT) - png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; - - /* If we reach an IDAT chunk, this means we have read all of the - * header chunks, and we can start reading the image (or if this - * is called after the image has been read - we have an error). - */ - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before IDAT"); - - else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && - !(png_ptr->mode & PNG_HAVE_PLTE)) - png_error(png_ptr, "Missing PLTE before IDAT"); - - png_ptr->mode |= PNG_HAVE_IDAT; - png_ptr->process_mode = PNG_READ_IDAT_MODE; - - if (!(png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) - if (png_ptr->push_length == 0) - return; - - if (png_ptr->mode & PNG_AFTER_IDAT) - png_benign_error(png_ptr, "Too many IDATs found[p]"); - } - - if (chunk_name == png_IHDR) - { - if (png_ptr->push_length != 13) - png_error(png_ptr, "Invalid IHDR length"); - - if (png_ptr->push_length + 4 > png_ptr->buffer_size) - { - png_push_save_buffer(png_ptr); - return; - } - - png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length); - } - - else if (chunk_name == png_IEND) - { - if (png_ptr->push_length + 4 > png_ptr->buffer_size) - { - png_push_save_buffer(png_ptr); - return; - } - - png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length); - - png_ptr->process_mode = PNG_READ_DONE_MODE; - png_push_have_end(png_ptr, info_ptr); - } - -#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED - else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0) - { - if (png_ptr->push_length + 4 > png_ptr->buffer_size) - { - png_push_save_buffer(png_ptr); - return; - } - - png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length, keep); - - if (chunk_name == png_PLTE) - png_ptr->mode |= PNG_HAVE_PLTE; - } -#endif - - else if (chunk_name == png_PLTE) - { - if (png_ptr->push_length + 4 > png_ptr->buffer_size) - { - png_push_save_buffer(png_ptr); - return; - } - png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length); - } - - else if (chunk_name == png_IDAT) - { - png_ptr->idat_size = png_ptr->push_length; - png_ptr->process_mode = PNG_READ_IDAT_MODE; - png_push_have_info(png_ptr, info_ptr); - png_ptr->zstream.avail_out = - (uInt) PNG_ROWBYTES(png_ptr->pixel_depth, - png_ptr->iwidth) + 1; - png_ptr->zstream.next_out = png_ptr->row_buf; - return; - } - -#ifdef PNG_READ_gAMA_SUPPORTED - else if (png_ptr->chunk_name == png_gAMA) - { - if (png_ptr->push_length + 4 > png_ptr->buffer_size) - { - png_push_save_buffer(png_ptr); - return; - } - - png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_sBIT_SUPPORTED - else if (png_ptr->chunk_name == png_sBIT) - { - if (png_ptr->push_length + 4 > png_ptr->buffer_size) - { - png_push_save_buffer(png_ptr); - return; - } - - png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_cHRM_SUPPORTED - else if (png_ptr->chunk_name == png_cHRM) - { - if (png_ptr->push_length + 4 > png_ptr->buffer_size) - { - png_push_save_buffer(png_ptr); - return; - } - - png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_sRGB_SUPPORTED - else if (chunk_name == png_sRGB) - { - if (png_ptr->push_length + 4 > png_ptr->buffer_size) - { - png_push_save_buffer(png_ptr); - return; - } - - png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_iCCP_SUPPORTED - else if (png_ptr->chunk_name == png_iCCP) - { - if (png_ptr->push_length + 4 > png_ptr->buffer_size) - { - png_push_save_buffer(png_ptr); - return; - } - - png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_sPLT_SUPPORTED - else if (chunk_name == png_sPLT) - { - if (png_ptr->push_length + 4 > png_ptr->buffer_size) - { - png_push_save_buffer(png_ptr); - return; - } - - png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_tRNS_SUPPORTED - else if (chunk_name == png_tRNS) - { - if (png_ptr->push_length + 4 > png_ptr->buffer_size) - { - png_push_save_buffer(png_ptr); - return; - } - - png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_bKGD_SUPPORTED - else if (chunk_name == png_bKGD) - { - if (png_ptr->push_length + 4 > png_ptr->buffer_size) - { - png_push_save_buffer(png_ptr); - return; - } - - png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_hIST_SUPPORTED - else if (chunk_name == png_hIST) - { - if (png_ptr->push_length + 4 > png_ptr->buffer_size) - { - png_push_save_buffer(png_ptr); - return; - } - - png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_pHYs_SUPPORTED - else if (chunk_name == png_pHYs) - { - if (png_ptr->push_length + 4 > png_ptr->buffer_size) - { - png_push_save_buffer(png_ptr); - return; - } - - png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_oFFs_SUPPORTED - else if (chunk_name == png_oFFs) - { - if (png_ptr->push_length + 4 > png_ptr->buffer_size) - { - png_push_save_buffer(png_ptr); - return; - } - - png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length); - } -#endif - -#ifdef PNG_READ_pCAL_SUPPORTED - else if (chunk_name == png_pCAL) - { - if (png_ptr->push_length + 4 > png_ptr->buffer_size) - { - png_push_save_buffer(png_ptr); - return; - } - - png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_sCAL_SUPPORTED - else if (chunk_name == png_sCAL) - { - if (png_ptr->push_length + 4 > png_ptr->buffer_size) - { - png_push_save_buffer(png_ptr); - return; - } - - png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_tIME_SUPPORTED - else if (chunk_name == png_tIME) - { - if (png_ptr->push_length + 4 > png_ptr->buffer_size) - { - png_push_save_buffer(png_ptr); - return; - } - - png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_tEXt_SUPPORTED - else if (chunk_name == png_tEXt) - { - if (png_ptr->push_length + 4 > png_ptr->buffer_size) - { - png_push_save_buffer(png_ptr); - return; - } - - png_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_zTXt_SUPPORTED - else if (chunk_name == png_zTXt) - { - if (png_ptr->push_length + 4 > png_ptr->buffer_size) - { - png_push_save_buffer(png_ptr); - return; - } - - png_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_iTXt_SUPPORTED - else if (chunk_name == png_iTXt) - { - if (png_ptr->push_length + 4 > png_ptr->buffer_size) - { - png_push_save_buffer(png_ptr); - return; - } - - png_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length); - } -#endif - - else - { - if (png_ptr->push_length + 4 > png_ptr->buffer_size) - { - png_push_save_buffer(png_ptr); - return; - } - png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length, - PNG_HANDLE_CHUNK_AS_DEFAULT); - } - - png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; -} - -void /* PRIVATE */ -png_push_crc_skip(png_structrp png_ptr, png_uint_32 skip) -{ - png_ptr->process_mode = PNG_SKIP_MODE; - png_ptr->skip_length = skip; -} - -void /* PRIVATE */ -png_push_crc_finish(png_structrp png_ptr) -{ - if (png_ptr->skip_length && png_ptr->save_buffer_size) - { - png_size_t save_size = png_ptr->save_buffer_size; - png_uint_32 skip_length = png_ptr->skip_length; - - /* We want the smaller of 'skip_length' and 'save_buffer_size', but - * they are of different types and we don't know which variable has the - * fewest bits. Carefully select the smaller and cast it to the type of - * the larger - this cannot overflow. Do not cast in the following test - * - it will break on either 16 or 64 bit platforms. - */ - if (skip_length < save_size) - save_size = (png_size_t)skip_length; - - else - skip_length = (png_uint_32)save_size; - - png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); - - png_ptr->skip_length -= skip_length; - png_ptr->buffer_size -= save_size; - png_ptr->save_buffer_size -= save_size; - png_ptr->save_buffer_ptr += save_size; - } - if (png_ptr->skip_length && png_ptr->current_buffer_size) - { - png_size_t save_size = png_ptr->current_buffer_size; - png_uint_32 skip_length = png_ptr->skip_length; - - /* We want the smaller of 'skip_length' and 'current_buffer_size', here, - * the same problem exists as above and the same solution. - */ - if (skip_length < save_size) - save_size = (png_size_t)skip_length; - - else - skip_length = (png_uint_32)save_size; - - png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); - - png_ptr->skip_length -= skip_length; - png_ptr->buffer_size -= save_size; - png_ptr->current_buffer_size -= save_size; - png_ptr->current_buffer_ptr += save_size; - } - if (!png_ptr->skip_length) - { - if (png_ptr->buffer_size < 4) - { - png_push_save_buffer(png_ptr); - return; - } - - png_crc_finish(png_ptr, 0); - png_ptr->process_mode = PNG_READ_CHUNK_MODE; - } -} - -void PNGCBAPI +/* Standard callbacks */ +static void PNGCBAPI png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) { png_bytep ptr; @@ -622,7 +26,9 @@ png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) return; ptr = buffer; - if (png_ptr->save_buffer_size) + debug(length > 0); + + if (length > 0 && png_ptr->save_buffer_size > 0) { png_size_t save_size; @@ -639,7 +45,8 @@ png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) png_ptr->save_buffer_size -= save_size; png_ptr->save_buffer_ptr += save_size; } - if (length && png_ptr->current_buffer_size) + + if (length > 0 && png_ptr->current_buffer_size > 0) { png_size_t save_size; @@ -656,615 +63,782 @@ png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) } } -void /* PRIVATE */ +/* Push model modes: png_chunk_op plus extras */ +typedef enum +{ + /* all the png_chunk_op codes, plus: */ + png_read_signature = 0, /* starting value */ + png_read_chunk_header, + png_read_end_IDAT, + png_read_done, + png_read_chunk, /* Not a state, use these derived from png_chunk_op: */ + png_read_chunk_skip = png_read_chunk+png_chunk_skip, + png_read_chunk_unknown = png_read_chunk+png_chunk_unknown, + png_read_chunk_process_all = png_read_chunk+png_chunk_process_all, + png_read_chunk_process_part = png_read_chunk+png_chunk_process_part +} png_read_mode; + +static void +png_push_save_buffer_partial(png_structrp png_ptr, size_t amount) +{ + /* Copy 'amount' bytes to the end of the save buffer from the current + * buffer. + */ + png_bytep buffer; + size_t save_size = png_ptr->save_buffer_size; + + if (save_size > PNG_SIZE_MAX - amount) + png_error(png_ptr, "save buffer overflow"); + + if (png_ptr->save_buffer_max < save_size + amount) + { + /* Reallocate the save buffer. */ + buffer = png_voidcast(png_bytep, png_malloc(png_ptr, save_size + amount)); + memcpy(buffer, png_ptr->save_buffer_ptr, save_size); + png_free(png_ptr, png_ptr->save_buffer); + png_ptr->save_buffer_ptr = png_ptr->save_buffer = buffer; + } + + else if (png_ptr->save_buffer_max - + (png_ptr->save_buffer_ptr - png_ptr->save_buffer) < save_size + amount) + { + /* Move the existing saved data */ + buffer = png_ptr->save_buffer; + memmove(buffer, png_ptr->save_buffer_ptr, save_size); + png_ptr->save_buffer_ptr = buffer; + } + + else /* Just copy the data */ + buffer = png_ptr->save_buffer_ptr; + + memcpy(buffer+save_size, png_ptr->current_buffer_ptr, amount); + png_ptr->save_buffer_size = save_size + amount; + png_ptr->current_buffer_ptr += amount; + png_ptr->current_buffer_size -= amount; + png_ptr->buffer_size -= amount; +} + +static void png_push_save_buffer(png_structrp png_ptr) { - if (png_ptr->save_buffer_size) - { - if (png_ptr->save_buffer_ptr != png_ptr->save_buffer) - { - png_size_t i, istop; - png_bytep sp; - png_bytep dp; - - istop = png_ptr->save_buffer_size; - for (i = 0, sp = png_ptr->save_buffer_ptr, dp = png_ptr->save_buffer; - i < istop; i++, sp++, dp++) - { - *dp = *sp; - } - } - } - if (png_ptr->save_buffer_size + png_ptr->current_buffer_size > - png_ptr->save_buffer_max) - { - png_size_t new_max; - png_bytep old_buffer; - - if (png_ptr->save_buffer_size > PNG_SIZE_MAX - - (png_ptr->current_buffer_size + 256)) - { - png_error(png_ptr, "Potential overflow of save_buffer"); - } - - new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256; - old_buffer = png_ptr->save_buffer; - png_ptr->save_buffer = (png_bytep)png_malloc_warn(png_ptr, - (png_size_t)new_max); - - if (png_ptr->save_buffer == NULL) - { - png_free(png_ptr, old_buffer); - png_error(png_ptr, "Insufficient memory for save_buffer"); - } - - memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size); - png_free(png_ptr, old_buffer); - png_ptr->save_buffer_max = new_max; - } - if (png_ptr->current_buffer_size) - { - memcpy(png_ptr->save_buffer + png_ptr->save_buffer_size, - png_ptr->current_buffer_ptr, png_ptr->current_buffer_size); - png_ptr->save_buffer_size += png_ptr->current_buffer_size; - png_ptr->current_buffer_size = 0; - } - png_ptr->save_buffer_ptr = png_ptr->save_buffer; + png_push_save_buffer_partial(png_ptr, png_ptr->current_buffer_size); + /* This terminates the process loop. */ png_ptr->buffer_size = 0; } -void /* PRIVATE */ -png_push_restore_buffer(png_structrp png_ptr, png_bytep buffer, - png_size_t buffer_length) -{ - png_ptr->current_buffer = buffer; - png_ptr->current_buffer_size = buffer_length; - png_ptr->buffer_size = buffer_length + png_ptr->save_buffer_size; - png_ptr->current_buffer_ptr = png_ptr->current_buffer; -} +#define PNG_PUSH_SAVE_BUFFER_IF_FULL \ +if (png_ptr->chunk_length + 4 > png_ptr->buffer_size) \ + { png_push_save_buffer(png_ptr); return; } +#define PNG_PUSH_SAVE_BUFFER_IF_LT(N) \ +if (png_ptr->buffer_size < N) \ + { png_push_save_buffer(png_ptr); return; } -void /* PRIVATE */ -png_push_read_IDAT(png_structrp png_ptr) +png_size_t PNGAPI +png_process_data_pause(png_structrp png_ptr, int save) { - if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER)) + if (png_ptr != NULL) { - png_byte chunk_length[4]; - png_byte chunk_tag[4]; - - /* TODO: this code can be commoned up with the same code in push_read */ - if (png_ptr->buffer_size < 8) - { + /* It's easiest for the caller if we do the save; then the caller doesn't + * have to supply the same data again: + */ + if (save != 0) png_push_save_buffer(png_ptr); - return; - } - - png_push_fill_buffer(png_ptr, chunk_length, 4); - png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length); - png_reset_crc(png_ptr); - png_crc_read(png_ptr, chunk_tag, 4); - png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag); - png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; - - if (png_ptr->chunk_name != png_IDAT) + else { - png_ptr->process_mode = PNG_READ_CHUNK_MODE; + /* This includes any pending saved bytes: */ + png_size_t remaining = png_ptr->buffer_size; + png_ptr->buffer_size = 0; /* Terminate the process loop */ - if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED)) - png_error(png_ptr, "Not enough compressed data"); - - return; + /* So subtract the saved buffer size, unless all the data + * is actually 'saved', in which case we just return 0 + */ + if (png_ptr->save_buffer_size < remaining) + return remaining - png_ptr->save_buffer_size; } - - png_ptr->idat_size = png_ptr->push_length; } - if (png_ptr->idat_size && png_ptr->save_buffer_size) - { - png_size_t save_size = png_ptr->save_buffer_size; - png_uint_32 idat_size = png_ptr->idat_size; + return 0; +} - /* We want the smaller of 'idat_size' and 'current_buffer_size', but they - * are of different types and we don't know which variable has the fewest - * bits. Carefully select the smaller and cast it to the type of the - * larger - this cannot overflow. Do not cast in the following test - it - * will break on either 16 or 64 bit platforms. +png_uint_32 PNGAPI +png_process_data_skip(png_structrp png_ptr) +{ + if (png_ptr != NULL && png_ptr->process_mode == png_read_chunk_skip) + { + /* At the end of png_process_data the buffer size must be 0 (see the loop + * above) so we can detect a broken call here: */ - if (idat_size < save_size) - save_size = (png_size_t)idat_size; + if (png_ptr->buffer_size != 0) + png_app_error(png_ptr, + "png_process_data_skip called inside png_process_data"); + + /* If is impossible for there to be a saved buffer at this point - + * otherwise we could not be in SKIP mode. This will also happen if + * png_process_skip is called inside png_process_data (but only very + * rarely.) + */ + else if (png_ptr->save_buffer_size != 0) + png_app_error(png_ptr, "png_process_data_skip called with saved data"); else - idat_size = (png_uint_32)save_size; + { + /* Skipping png_ptr->chunk_length of data then checking the CRC, after + * that a new chunk header will be read. + */ + png_ptr->process_mode = png_read_chunk_header; + return png_ptr->chunk_length + 4; + } + } + + return 0; +} + +static void +png_push_restore_buffer(png_structrp png_ptr, png_bytep buffer, + png_size_t buffer_length) +{ + png_ptr->current_buffer_size = buffer_length; + png_ptr->buffer_size = buffer_length + png_ptr->save_buffer_size; + png_ptr->current_buffer_ptr = buffer; +} + +/* Read any remaining signature bytes from the stream and compare them with + * the correct PNG signature. It is possible that this routine is called + * with bytes already read from the signature, either because they have been + * checked by the calling application, or because of multiple calls to this + * routine. + */ +static void +png_push_read_signature(png_structrp png_ptr, png_inforp info_ptr) +{ + unsigned int num_checked = png_ptr->sig_bytes; + unsigned int num_to_check = 8 - num_checked; + + if (png_ptr->buffer_size < num_to_check) + num_to_check = (int)/*SAFE*/png_ptr->buffer_size; + + png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]), + num_to_check); + png_ptr->sig_bytes = png_check_byte(png_ptr, + png_ptr->sig_bytes + num_to_check); + + if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) + { + if (num_checked < 4 && + png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) + png_error(png_ptr, "Not a PNG file"); + + else + png_error(png_ptr, "PNG file corrupted by ASCII conversion"); + } + + else if (png_ptr->sig_bytes >= 8) + png_ptr->process_mode = png_read_chunk_header; +} + +static void +png_push_crc_finish(png_structrp png_ptr) + /* CRC the remainder of the chunk data; png_struct::chunk_length must be the + * amount of data left to read excluding the CRC. + */ +{ + if (png_ptr->chunk_length != 0 && png_ptr->save_buffer_size != 0) + { + png_size_t save_size = png_ptr->save_buffer_size; + png_uint_32 skip_length = png_ptr->chunk_length; + + /* We want the smaller of 'skip_length' and 'save_buffer_size', but + * they are of different types and we don't know which variable has the + * fewest bits. Carefully select the smaller and cast it to the type of + * the larger - this cannot overflow. Do not cast in the following test + * - it will break on either 16 or 64 bit platforms. + */ + if (skip_length < save_size) + save_size = (png_size_t)/*SAFE*/skip_length; + + else + skip_length = (png_uint_32)/*SAFE*/save_size; png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); - png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size); - - png_ptr->idat_size -= idat_size; + png_ptr->chunk_length -= skip_length; png_ptr->buffer_size -= save_size; png_ptr->save_buffer_size -= save_size; png_ptr->save_buffer_ptr += save_size; } - if (png_ptr->idat_size && png_ptr->current_buffer_size) + if (png_ptr->chunk_length != 0 && png_ptr->current_buffer_size != 0) { png_size_t save_size = png_ptr->current_buffer_size; - png_uint_32 idat_size = png_ptr->idat_size; + png_uint_32 skip_length = png_ptr->chunk_length; - /* We want the smaller of 'idat_size' and 'current_buffer_size', but they - * are of different types and we don't know which variable has the fewest - * bits. Carefully select the smaller and cast it to the type of the - * larger - this cannot overflow. + /* We want the smaller of 'skip_length' and 'current_buffer_size', here, + * the same problem exists as above and the same solution. */ - if (idat_size < save_size) - save_size = (png_size_t)idat_size; + if (skip_length < save_size) + save_size = (png_size_t)/*SAFE*/skip_length; else - idat_size = (png_uint_32)save_size; + skip_length = (png_uint_32)/*SAFE*/save_size; png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); - png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size); - - png_ptr->idat_size -= idat_size; + png_ptr->chunk_length -= skip_length; png_ptr->buffer_size -= save_size; png_ptr->current_buffer_size -= save_size; png_ptr->current_buffer_ptr += save_size; } - if (!png_ptr->idat_size) - { - if (png_ptr->buffer_size < 4) - { - png_push_save_buffer(png_ptr); - return; - } + if (png_ptr->chunk_length == 0) + { + PNG_PUSH_SAVE_BUFFER_IF_LT(4) png_crc_finish(png_ptr, 0); - png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; - png_ptr->mode |= PNG_AFTER_IDAT; - png_ptr->zowner = 0; + png_ptr->process_mode = png_read_chunk_header; } } -void /* PRIVATE */ -png_process_IDAT_data(png_structrp png_ptr, png_bytep buffer, - png_size_t buffer_length) +static void +png_push_read_unknown(png_structrp png_ptr, png_inforp info_ptr) { - /* The caller checks for a non-zero buffer length. */ - if (!(buffer_length > 0) || buffer == NULL) - png_error(png_ptr, "No IDAT data (internal error)"); - - /* This routine must process all the data it has been given - * before returning, calling the row callback as required to - * handle the uncompressed results. + /* Handle an unknown chunk. All the data is available but it may not + * all be in the same buffer. png_handle_unknown needs the chunk data in + * just one buffer. */ - png_ptr->zstream.next_in = buffer; - /* TODO: WARNING: TRUNCATION ERROR: DANGER WILL ROBINSON: */ - png_ptr->zstream.avail_in = (uInt)buffer_length; + png_bytep buffer; + png_uint_32 chunk_length = png_ptr->chunk_length; - /* Keep going until the decompressed data is all processed - * or the stream marked as finished. - */ - while (png_ptr->zstream.avail_in > 0 && - !(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED)) + if (png_ptr->save_buffer_size > 0) { - int ret; + png_size_t save_size = png_ptr->save_buffer_size; - /* We have data for zlib, but we must check that zlib - * has someplace to put the results. It doesn't matter - * if we don't expect any results -- it may be the input - * data is just the LZ end code. - */ - if (!(png_ptr->zstream.avail_out > 0)) + if (save_size < chunk_length) { - /* TODO: WARNING: TRUNCATION ERROR: DANGER WILL ROBINSON: */ - png_ptr->zstream.avail_out = (uInt)(PNG_ROWBYTES(png_ptr->pixel_depth, - png_ptr->iwidth) + 1); - - png_ptr->zstream.next_out = png_ptr->row_buf; + /* Copy the current_buffer_ptr data into the save buffer. */ + png_push_save_buffer_partial(png_ptr, chunk_length - save_size); + save_size = chunk_length; } - /* Using Z_SYNC_FLUSH here means that an unterminated - * LZ stream (a stream with a missing end code) can still - * be handled, otherwise (Z_NO_FLUSH) a future zlib - * implementation might defer output and therefore - * change the current behavior (see comments in inflate.c - * for why this doesn't happen at present with zlib 1.2.5). - */ - ret = inflate(&png_ptr->zstream, Z_SYNC_FLUSH); + buffer = png_ptr->save_buffer_ptr; + png_ptr->save_buffer_ptr = buffer+chunk_length; + png_ptr->save_buffer_size = save_size-chunk_length; + png_ptr->buffer_size -= chunk_length; + affirm(png_ptr->buffer_size >= 4); + } - /* Check for any failure before proceeding. */ - if (ret != Z_OK && ret != Z_STREAM_END) + else + { + affirm(png_ptr->current_buffer_size >= chunk_length+4); + buffer = png_ptr->current_buffer_ptr; + png_ptr->current_buffer_ptr = buffer+chunk_length; + png_ptr->current_buffer_size -= chunk_length; + png_ptr->buffer_size -= chunk_length; + } + + /* Now check the CRC, before attempting the unknown handling. */ + png_calculate_crc(png_ptr, buffer, chunk_length); + png_crc_finish(png_ptr, 0); +# ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED + png_handle_unknown(png_ptr, info_ptr, buffer); +# else /* !READ_UNKNOWN_CHUNKS */ + PNG_UNUSED(info_ptr) +# endif /* !READ_UNKNOWN_CHUNKS */ + png_ptr->process_mode = png_read_chunk_header; +} + +static void +png_push_have_row(png_structrp png_ptr, png_bytep row) +{ + if (png_ptr->row_fn != NULL) + { + png_uint_32 row_number = png_ptr->row_number; + png_byte pass = png_ptr->pass; + + if (png_ptr->interlaced) { - /* Terminate the decompression. */ - png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; - png_ptr->zowner = 0; - - /* This may be a truncated stream (missing or - * damaged end code). Treat that as a warning. + /* If the row de-interlace is not being done by PNG this wacky API + * delivers the row number in the pass to the caller. We know that + * if we get here the row exists, so the number is just one less than + * the height of an interlaced image with just the rows up to this + * one: */ - if (png_ptr->row_number >= png_ptr->num_rows || - png_ptr->pass > 6) - png_warning(png_ptr, "Truncated compressed data in IDAT"); - - else - png_error(png_ptr, "Decompression error in IDAT"); - - /* Skip the check on unprocessed input */ - return; - } - - /* Did inflate output any data? */ - if (png_ptr->zstream.next_out != png_ptr->row_buf) - { - /* Is this unexpected data after the last row? - * If it is, artificially terminate the LZ output - * here. - */ - if (png_ptr->row_number >= png_ptr->num_rows || - png_ptr->pass > 6) +# ifdef PNG_READ_INTERLACING_SUPPORTED + if (!png_ptr->do_interlace) +# endif /* READ_INTERLACING */ { - /* Extra data. */ - png_warning(png_ptr, "Extra compressed data in IDAT"); - png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; - png_ptr->zowner = 0; + affirm(PNG_ROW_IN_INTERLACE_PASS(row_number, pass) && row != NULL); + row_number = PNG_PASS_ROWS(row_number+1, pass); + affirm(row_number > 0); + --row_number; + } + } - /* Do no more processing; skip the unprocessed - * input check below. + (*(png_ptr->row_fn))(png_ptr, row, row_number, pass); + } +} + +static void +png_push_read_sync_zstream(png_structp png_ptr, png_bytep *bufferp, + size_t *buffer_lengthp) + /* Synchronize the png_struct progressive read buffer + * {*bufferp,*buffer_lengthp} with png_struct::zstream.next_in, on the + * assumption that the zstream had previously been set up with *bufferp. + */ +{ + png_bytep original_start = *bufferp; + png_alloc_size_t bytes_consumed = png_ptr->zstream.next_in - original_start; + + affirm(buffer_lengthp != NULL); + + /* Calculate the CRC for the consumed data: */ + png_calculate_crc(png_ptr, original_start, bytes_consumed); + + /* Update the buffer pointers and the various lengths: */ + *bufferp = original_start + bytes_consumed; /* == png_ptr->zstream.next_in */ + + affirm(bytes_consumed <= *buffer_lengthp); + *buffer_lengthp -= (size_t)/*SAFE*/bytes_consumed; + + affirm(bytes_consumed <= png_ptr->chunk_length); + png_ptr->chunk_length -= (png_uint_32)/*SAFE*/bytes_consumed; + + affirm(bytes_consumed <= png_ptr->buffer_size); + png_ptr->buffer_size -= (size_t)/*SAFE*/bytes_consumed; +} + +static void +png_push_read_process_IDAT(png_structp png_ptr, png_bytep *bufferp, + size_t *buffer_lengthp) + /* If the the *buffer_lengthp parameter is NULL there is no more input, + * png_struct::mode & PNG_AFTER_IDAT must be set at this point. + */ +{ + png_alloc_size_t buffer_length; + + if (buffer_lengthp != NULL) + buffer_length = *buffer_lengthp; + + else /* end of IDAT */ + { + /* SECURITY: if this affirm fails the code would go into an infinite loop; + * see the handling of avail_in == 0 in png_inflate_IDAT. + */ + affirm(png_ptr->mode & PNG_AFTER_IDAT); + buffer_length = 0; + } + + /* This routine attempts to process all the data it has been given before + * returning, calling the row callback as required to handle the + * uncompressed results. + * + * If a pause happens during processing (png_ptr->buffer_size is set to 0) + * or the end of the chunk is encountered the routine may return without + * handling all the input data. + */ + if (buffer_length > png_ptr->chunk_length) + { + buffer_length = png_ptr->chunk_length; + + /* This works because the last part of a 'skip' is to read and check the + * CRC, then the process mode is set to png_read_chunk_header. + */ + if (buffer_length == 0) + png_ptr->process_mode = png_read_chunk_skip; + } + + /* It is possble for buffer_length to be zero at this point if the stream + * caontains a zero length IDAT. This is handled below. + */ + png_ptr->zstream.next_in = *bufferp; + + while (buffer_length > 0 || buffer_lengthp == NULL) + { + if (buffer_length >= ZLIB_IO_MAX) + { + png_ptr->zstream.avail_in = ZLIB_IO_MAX; + buffer_length -= ZLIB_IO_MAX; + } + + else + { + png_ptr->zstream.avail_in = (uInt)/*SAFE*/buffer_length; + buffer_length = 0; + } + + /* The last row may already have been processed. + * + * row_number is the *current* row number in the range 0..height-1. It is + * updated only by the call to png_read_process_IDAT that follows the call + * which returns something other than png_row_incomplete. + * + * At the end of the image that call must *NOT* be made; png_process_IDAT + * must not be called after the last row. png_struct::zstream_eod is set + * below to allow this condition to be detected. + * + * Note that png_read_process_IDAT handles errors in the LZ compressed + * data (i.e. the cases where png_struct::zstream_error is set) by filling + * the rows in with 0, which is a safe value, so keep calling it until we + * reach the end of the image. + */ + if (!png_ptr->zstream_eod) + { + png_bytep row_buffer = NULL; + png_row_op row_op = + png_read_process_IDAT(png_ptr, NULL, NULL, 1/*save row*/); + + if (row_op != png_row_incomplete) + { + /* Have a complete row, so check for end-of-image; do this here + * because interlaced images can end on earlier rows or passes but + * we keep calling png_read_process_IDAT until it updates row_number + * to the last row of the actual image: */ + if (png_ptr->row_number+1 >= png_ptr->height && + (!png_ptr->interlaced || png_ptr->pass == 6)) + png_ptr->zstream_eod = 1; /* end of image */ + } + + switch (row_op) + { + case png_row_incomplete: + /* more IDAT data needed for row */ + debug(png_ptr->zstream.avail_in == 0); + + /* png_inflate_IDAT is supposed to handle this and recognize a + * call with 0 avail_in as end of stream: + */ + affirm(buffer_lengthp != NULL); + continue; + + case png_row_process: + /* If a row was obtained after the end of the IDAT this was done + * by fabricating data, ensure this is reported, else there is a + * security issue; normally libpng does a png_error in this + * case, even if the error is ignored png_struct::zstream_error + * should be set so somehow the error wasn't noticed! + */ + affirm(buffer_lengthp != NULL || png_ptr->zstream_error); + + /* png_struct::transformed_row contains a complete, transformed, + * row; this is processed in both 'sparkle' and 'block' mode. + */ +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + row_buffer = png_ptr->transformed_row; + if (row_buffer == NULL) +# endif /* TRANSFORM_MECH */ + row_buffer = png_ptr->row_buffer; + break; + + case png_row_repeat: + /* row not in this pass, but the existing row in + * png_struct::transformed_row may be used, this is only required + * if the 'block' or 'rectangle' mode of display is done and + * libpng is handling the de-interlace; when the app does it it + * only see the real rows. + */ +# ifdef PNG_READ_INTERLACING_SUPPORTED + if (png_ptr->do_interlace) + { +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + row_buffer = png_ptr->transformed_row; + if (row_buffer == NULL) +# endif /* TRANSFORM_MECH */ + row_buffer = png_ptr->row_buffer; + break; + } +# endif /* READ_INTERLACING */ + continue; + + case png_row_skip: + /* row not in pass and no appropriate data; skip this row, + * nothing more need be done, except the read_row_fn. The use + * of 'NULL' to mean this row doesn't contribute to the output + * is historical and not documented; + */ +# ifdef PNG_READ_INTERLACING_SUPPORTED + if (png_ptr->do_interlace) + break; +# endif /* READ_INTERLACING */ + continue; + + default: + impossible("not reached"); + } + + /* Here if there is a row to process. */ + + /* Now adjust the buffer pointers before calling png_push_have_row + * because the callback might call png_process_data_pause and that + * calls png_push_save_row. (Yes, this is insane; it was forced on + * libpng by writers of an external app that ignored the instructions + * not to fiddle with the insides of png_struct in version 1.4. It + * will probably be fixed here before 1.7.0 is released by removing + * the need for the save buffer entirely.) + */ + if (buffer_lengthp != NULL) + png_push_read_sync_zstream(png_ptr, bufferp, buffer_lengthp); + + /* Process one row: */ + png_push_have_row(png_ptr, row_buffer); + + /* The buffer pointer and size may have changed at this point, + * so everything needs to be reloaded if we can continue reading. + */ + if (buffer_lengthp != NULL) /* not at end of IDATs */ + { + if (png_ptr->chunk_length == 0) + png_ptr->process_mode = png_read_chunk_skip; + + /* If the buffer_size has been set to zero more input is required, + * this may be a 'pause', and if the specific input buffer being + * processed has been exhaused then more input is also required. + * Otherwise we can keep going, however the input buffer may have + * been changed by the app callback, so do a complete reload: + */ + else if (png_ptr->buffer_size > 0 && *buffer_lengthp > 0) + png_push_read_process_IDAT(png_ptr, bufferp, buffer_lengthp); + return; } - /* Do we have a complete row? */ - if (png_ptr->zstream.avail_out == 0) - png_push_process_row(png_ptr); + /* If we can't continue reading because there is no more IDAT data this + * may still be a pause. + */ + if (png_ptr->buffer_size == 0) + return; + + /* Else continue, with zero data: */ + continue; } - /* And check for the end of the stream. */ - if (ret == Z_STREAM_END) - png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; - } + affirm(png_ptr->zstream_eod); - /* All the data should have been processed, if anything - * is left at this point we have bytes of IDAT data - * after the zlib end code. - */ - if (png_ptr->zstream.avail_in > 0) - png_warning(png_ptr, "Extra compression data in IDAT"); -} - -void /* PRIVATE */ -png_push_process_row(png_structrp png_ptr) -{ - /* 1.5.6: row_info moved out of png_struct to a local here. */ - png_row_info row_info; - - row_info.width = png_ptr->iwidth; /* NOTE: width of current interlaced row */ - row_info.color_type = png_ptr->color_type; - row_info.bit_depth = png_ptr->bit_depth; - row_info.channels = png_ptr->channels; - row_info.pixel_depth = png_ptr->pixel_depth; - row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width); - - if (png_ptr->row_buf[0] > PNG_FILTER_VALUE_NONE) - { - if (png_ptr->row_buf[0] < PNG_FILTER_VALUE_LAST) - png_read_filter_row(png_ptr, &row_info, png_ptr->row_buf + 1, - png_ptr->prev_row + 1, png_ptr->row_buf[0]); - else - png_error(png_ptr, "bad adaptive filter value"); - } - - /* libpng 1.5.6: the following line was copying png_ptr->rowbytes before - * 1.5.6, while the buffer really is this big in current versions of libpng - * it may not be in the future, so this was changed just to copy the - * interlaced row count: - */ - memcpy(png_ptr->prev_row, png_ptr->row_buf, row_info.rowbytes + 1); - -#ifdef PNG_READ_TRANSFORMS_SUPPORTED - if (png_ptr->transformations) - png_do_read_transformations(png_ptr, &row_info); -#endif - - /* The transformed pixel depth should match the depth now in row_info. */ - if (png_ptr->transformed_pixel_depth == 0) - { - png_ptr->transformed_pixel_depth = row_info.pixel_depth; - if (row_info.pixel_depth > png_ptr->maximum_pixel_depth) - png_error(png_ptr, "progressive row overflow"); - } - - else if (png_ptr->transformed_pixel_depth != row_info.pixel_depth) - png_error(png_ptr, "internal progressive row size calculation error"); - - -#ifdef PNG_READ_INTERLACING_SUPPORTED - /* Blow up interlaced rows to full size */ - if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) - { - if (png_ptr->pass < 6) - png_do_read_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass, - png_ptr->transformations); - - switch (png_ptr->pass) - { - case 0: - { - int i; - for (i = 0; i < 8 && png_ptr->pass == 0; i++) - { - png_push_have_row(png_ptr, png_ptr->row_buf + 1); - png_read_push_finish_row(png_ptr); /* Updates png_ptr->pass */ - } - - if (png_ptr->pass == 2) /* Pass 1 might be empty */ - { - for (i = 0; i < 4 && png_ptr->pass == 2; i++) - { - png_push_have_row(png_ptr, NULL); - png_read_push_finish_row(png_ptr); - } - } - - if (png_ptr->pass == 4 && png_ptr->height <= 4) - { - for (i = 0; i < 2 && png_ptr->pass == 4; i++) - { - png_push_have_row(png_ptr, NULL); - png_read_push_finish_row(png_ptr); - } - } - - if (png_ptr->pass == 6 && png_ptr->height <= 4) - { - png_push_have_row(png_ptr, NULL); - png_read_push_finish_row(png_ptr); - } - - break; - } - - case 1: - { - int i; - for (i = 0; i < 8 && png_ptr->pass == 1; i++) - { - png_push_have_row(png_ptr, png_ptr->row_buf + 1); - png_read_push_finish_row(png_ptr); - } - - if (png_ptr->pass == 2) /* Skip top 4 generated rows */ - { - for (i = 0; i < 4 && png_ptr->pass == 2; i++) - { - png_push_have_row(png_ptr, NULL); - png_read_push_finish_row(png_ptr); - } - } - - break; - } - - case 2: - { - int i; - - for (i = 0; i < 4 && png_ptr->pass == 2; i++) - { - png_push_have_row(png_ptr, png_ptr->row_buf + 1); - png_read_push_finish_row(png_ptr); - } - - for (i = 0; i < 4 && png_ptr->pass == 2; i++) - { - png_push_have_row(png_ptr, NULL); - png_read_push_finish_row(png_ptr); - } - - if (png_ptr->pass == 4) /* Pass 3 might be empty */ - { - for (i = 0; i < 2 && png_ptr->pass == 4; i++) - { - png_push_have_row(png_ptr, NULL); - png_read_push_finish_row(png_ptr); - } - } - - break; - } - - case 3: - { - int i; - - for (i = 0; i < 4 && png_ptr->pass == 3; i++) - { - png_push_have_row(png_ptr, png_ptr->row_buf + 1); - png_read_push_finish_row(png_ptr); - } - - if (png_ptr->pass == 4) /* Skip top two generated rows */ - { - for (i = 0; i < 2 && png_ptr->pass == 4; i++) - { - png_push_have_row(png_ptr, NULL); - png_read_push_finish_row(png_ptr); - } - } - - break; - } - - case 4: - { - int i; - - for (i = 0; i < 2 && png_ptr->pass == 4; i++) - { - png_push_have_row(png_ptr, png_ptr->row_buf + 1); - png_read_push_finish_row(png_ptr); - } - - for (i = 0; i < 2 && png_ptr->pass == 4; i++) - { - png_push_have_row(png_ptr, NULL); - png_read_push_finish_row(png_ptr); - } - - if (png_ptr->pass == 6) /* Pass 5 might be empty */ - { - png_push_have_row(png_ptr, NULL); - png_read_push_finish_row(png_ptr); - } - - break; - } - - case 5: - { - int i; - - for (i = 0; i < 2 && png_ptr->pass == 5; i++) - { - png_push_have_row(png_ptr, png_ptr->row_buf + 1); - png_read_push_finish_row(png_ptr); - } - - if (png_ptr->pass == 6) /* Skip top generated row */ - { - png_push_have_row(png_ptr, NULL); - png_read_push_finish_row(png_ptr); - } - - break; - } - - default: - case 6: - { - png_push_have_row(png_ptr, png_ptr->row_buf + 1); - png_read_push_finish_row(png_ptr); - - if (png_ptr->pass != 6) - break; - - png_push_have_row(png_ptr, NULL); - png_read_push_finish_row(png_ptr); - } - } - } - else -#endif - { - png_push_have_row(png_ptr, png_ptr->row_buf + 1); - png_read_push_finish_row(png_ptr); - } -} - -void /* PRIVATE */ -png_read_push_finish_row(png_structrp png_ptr) -{ -#ifdef PNG_READ_INTERLACING_SUPPORTED - /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ - - /* Start of interlace block */ - static PNG_CONST png_byte png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; - - /* Offset to next interlace block */ - static PNG_CONST png_byte png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; - - /* Start of interlace block in the y direction */ - static PNG_CONST png_byte png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1}; - - /* Offset to next interlace block in the y direction */ - static PNG_CONST png_byte png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2}; - - /* Height of interlace block. This is not currently used - if you need - * it, uncomment it here and in png.h - static PNG_CONST png_byte png_pass_height[] = {8, 8, 4, 4, 2, 2, 1}; - */ -#endif - - png_ptr->row_number++; - if (png_ptr->row_number < png_ptr->num_rows) - return; - -#ifdef PNG_READ_INTERLACING_SUPPORTED - if (png_ptr->interlaced) - { - png_ptr->row_number = 0; - memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1); - - do + if (png_ptr->zowner == 0 || png_read_finish_IDAT(png_ptr)) { - png_ptr->pass++; - if ((png_ptr->pass == 1 && png_ptr->width < 5) || - (png_ptr->pass == 3 && png_ptr->width < 3) || - (png_ptr->pass == 5 && png_ptr->width < 2)) - png_ptr->pass++; + /* The zlib stream has ended, there may still be input data in + * png_ptr->zstream.next_in, restore this. + */ + debug(png_ptr->zowner == 0 && png_ptr->zstream_ended); - if (png_ptr->pass > 7) - png_ptr->pass--; + if (buffer_lengthp != NULL) + { + png_push_read_sync_zstream(png_ptr, bufferp, buffer_lengthp); - if (png_ptr->pass >= 7) - break; + /* If the chunk_length is greater than 0 then there is extra data, + * report this once. Notice that for IDAT after the end of the + * stream we keep coming to this point and doing the skip. + */ + if (png_ptr->chunk_length > 0) + { + if (!png_ptr->zstream_error) + { + png_chunk_benign_error(png_ptr, + "too much IDAT data (progressive read)"); + png_ptr->zstream_error = 1; + } + } - png_ptr->iwidth = (png_ptr->width + - png_pass_inc[png_ptr->pass] - 1 - - png_pass_start[png_ptr->pass]) / - png_pass_inc[png_ptr->pass]; + /* In any case CRC the chunk, skipping any unneeded data: */ + png_ptr->process_mode = png_read_chunk_skip; + } + return; + } - if (png_ptr->transformations & PNG_INTERLACE) - break; - - png_ptr->num_rows = (png_ptr->height + - png_pass_yinc[png_ptr->pass] - 1 - - png_pass_ystart[png_ptr->pass]) / - png_pass_yinc[png_ptr->pass]; - - } while (png_ptr->iwidth == 0 || png_ptr->num_rows == 0); + /* else more input is required */ + /* NOTE: this test only fires on a small (less than 5 byte) IDAT chunk + * which just contains the LZ EOF and the Adler32 CRC. + */ + affirm(png_ptr->zowner == png_IDAT && !png_ptr->zstream_ended); } -#endif /* PNG_READ_INTERLACING_SUPPORTED */ + + /* At this point all the input has been consumed, however the CRC has not + * been done and the three length fields in png_struct, *buffer_lengthp, + * buffer_size and chunk_length, all need updating. + */ + png_push_read_sync_zstream(png_ptr, bufferp, buffer_lengthp); } -void /* PRIVATE */ +static void +png_push_read_IDAT(png_structrp png_ptr) +{ + if (png_ptr->save_buffer_size > 0) + { + png_push_read_process_IDAT(png_ptr, &png_ptr->save_buffer_ptr, + &png_ptr->save_buffer_size); + + /* This is a slight optimization; normally when the process mode changes + * there will still be something in the buffer: + */ + if (png_ptr->save_buffer_size > 0) + return; + + /* Check for a change in process mode or an application pause before + * checking the current input buffer. This is only rarely reached. + */ + if (png_ptr->process_mode != png_read_chunk_process_part || + png_ptr->buffer_size == 0) + return; + } + + if (png_ptr->current_buffer_size > 0) + png_push_read_process_IDAT(png_ptr, &png_ptr->current_buffer_ptr, + &png_ptr->current_buffer_size); +} + +static void +png_push_finish_IDAT(png_structrp png_ptr) + /* Called once when the first chunk after IDAT is seen. */ +{ + /* All of the IDAT data has been processed, however the stream may have + * been truncated and the image rows may not all have been processed. + * Clean up here (this doesn't read anything.) + * + * 1.7.0: this attempts some measure of compatibility with the sequential + * API, if the IDAT is truncated and the resultant error reporting doesn't + * abort the read the image is filled in using zeros of pixel data. This + * actually happens inside png_inflate_IDAT (pngrutil.c) when called with + * z_stream::avail_in == 0. + */ + while (png_ptr->zowner == png_IDAT) + { + png_byte b = 0, *pb = &b; + + png_push_read_process_IDAT(png_ptr, &pb, NULL/*end of IDAT*/); + + if (png_ptr->zowner == 0) + break; + + if (png_ptr->buffer_size == 0) /* pause */ + return; + } + + png_ptr->process_mode = png_check_bits(png_ptr, png_ptr->process_mode >> 4, + 4); +} + +static void png_push_have_info(png_structrp png_ptr, png_inforp info_ptr) { if (png_ptr->info_fn != NULL) (*(png_ptr->info_fn))(png_ptr, info_ptr); } -void /* PRIVATE */ +static void png_push_have_end(png_structrp png_ptr, png_inforp info_ptr) { if (png_ptr->end_fn != NULL) (*(png_ptr->end_fn))(png_ptr, info_ptr); } -void /* PRIVATE */ -png_push_have_row(png_structrp png_ptr, png_bytep row) +static void +png_push_read_chunk_header(png_structrp png_ptr, png_infop info_ptr) { - if (png_ptr->row_fn != NULL) - (*(png_ptr->row_fn))(png_ptr, row, png_ptr->row_number, - (int)png_ptr->pass); + /* Called to read a new chunk header and work out how to handle the remainder + * of the data. + */ + unsigned int mode; /* mode prior to the header */ + png_byte chunk_header[8]; + + PNG_PUSH_SAVE_BUFFER_IF_LT(8) + png_push_fill_buffer(png_ptr, chunk_header, 8); + png_ptr->chunk_length = png_get_uint_31(png_ptr, chunk_header); + png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_header+4); + png_reset_crc(png_ptr, chunk_header+4); + png_check_chunk_name(png_ptr, png_ptr->chunk_name); + png_check_chunk_length(png_ptr, png_ptr->chunk_length); + mode = png_ptr->mode; + png_ptr->process_mode = png_check_bits(png_ptr, + png_read_chunk+png_find_chunk_op(png_ptr), 4); + + /* Is this the first IDAT chunk? */ + if ((mode ^ png_ptr->mode) & PNG_HAVE_IDAT) + png_push_have_info(png_ptr, info_ptr); + + /* Is it the chunk after the last IDAT chunk? */ + else if (((mode ^ png_ptr->mode) & PNG_AFTER_IDAT) != 0) + png_ptr->process_mode = png_check_bits(png_ptr, + (png_ptr->process_mode << 4) + png_read_end_IDAT, 8); } -#ifdef PNG_READ_INTERLACING_SUPPORTED -void PNGAPI -png_progressive_combine_row(png_const_structrp png_ptr, png_bytep old_row, - png_const_bytep new_row) +/* What we do with the incoming data depends on what we were previously + * doing before we ran out of data... + */ +static void +png_process_some_data(png_structrp png_ptr, png_inforp info_ptr) { if (png_ptr == NULL) return; - /* new_row is a flag here - if it is NULL then the app callback was called - * from an empty row (see the calls to png_struct::row_fn below), otherwise - * it must be png_ptr->row_buf+1 - */ - if (new_row != NULL) - png_combine_row(png_ptr, old_row, 1/*display*/); + switch (png_ptr->process_mode & 0xf) + { + case png_read_signature: + png_push_read_signature(png_ptr, info_ptr); + return; + + case png_read_chunk_header: + png_push_read_chunk_header(png_ptr, info_ptr); + return; + + case png_read_chunk_skip: + png_push_crc_finish(png_ptr); + return; + + case png_read_chunk_unknown: + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_push_read_unknown(png_ptr, info_ptr); + return; + + case png_read_chunk_process_all: + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_chunk(png_ptr, info_ptr); + + if (png_ptr->mode & PNG_HAVE_IEND) + { + png_ptr->process_mode = png_read_done; + png_push_have_end(png_ptr, info_ptr); + png_ptr->buffer_size = 0; + } + + else + png_ptr->process_mode = png_read_chunk_header; + return; + + case png_read_chunk_process_part: + debug(png_ptr->chunk_name == png_IDAT && + (png_ptr->mode & PNG_HAVE_IDAT) && + !(png_ptr->mode & PNG_AFTER_IDAT)); + + if (png_ptr->zowner == 0 && !png_ptr->zstream_ended) /* first time */ + png_read_start_IDAT(png_ptr); + + png_push_read_IDAT(png_ptr); + return; + + case png_read_end_IDAT: + png_push_finish_IDAT(png_ptr); + return; + + case png_read_done: + png_app_error(png_ptr, "read beyond end of stream"); + png_ptr->buffer_size = 0; + return; + + default: + impossible("invalid process mode"); + } +} + +void PNGAPI +png_process_data(png_structrp png_ptr, png_inforp info_ptr, + png_bytep buffer, png_size_t buffer_size) +{ + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_push_restore_buffer(png_ptr, buffer, buffer_size); + + while (png_ptr->buffer_size) + png_process_some_data(png_ptr, info_ptr); } -#endif /* PNG_READ_INTERLACING_SUPPORTED */ void PNGAPI png_set_progressive_read_fn(png_structrp png_ptr, png_voidp progressive_ptr, @@ -1289,4 +863,4 @@ png_get_progressive_ptr(png_const_structrp png_ptr) return png_ptr->io_ptr; } -#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ +#endif /* PROGRESSIVE_READ */ diff --git a/ext/libpng17/pngpriv.h b/ext/libpng17/pngpriv.h index 1156092683..15a3226d3d 100644 --- a/ext/libpng17/pngpriv.h +++ b/ext/libpng17/pngpriv.h @@ -1,13 +1,11 @@ /* pngpriv.h - private declarations for use inside libpng * - * For conditions of distribution and use, see copyright notice in png.h - * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * Last changed in libpng 1.7.0 [(PENDING RELEASE)] + * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * - * Last changed in libpng 1.7.0 [(PENDING RELEASE)] - * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h @@ -125,94 +123,6 @@ #endif /* VERSION_INFO_ONLY */ -/* Compile time options. - * ===================== - * In a multi-arch build the compiler may compile the code several times for the - * same object module, producing different binaries for different architectures. - * When this happens configure-time setting of the target host options cannot be - * done and this interferes with the handling of the ARM NEON optimizations, and - * possibly other similar optimizations. Put additional tests here; in general - * this is needed when the same option can be changed at both compile time and - * run time depending on the target OS (i.e. iOS vs Android.) - * - * NOTE: symbol prefixing does not pass $(CFLAGS) to the preprocessor, because - * this is not possible with certain compilers (Oracle SUN OS CC), as a result - * it is necessary to ensure that all extern functions that *might* be used - * regardless of $(CFLAGS) get declared in this file. The test on __ARM_NEON__ - * below is one example of this behavior because it is controlled by the - * presence or not of -mfpu=neon on the GCC command line, it is possible to do - * this in $(CC), e.g. "CC=gcc -mfpu=neon", but people who build libpng rarely - * do this. - */ -#ifndef PNG_ARM_NEON_OPT - /* ARM NEON optimizations are being controlled by the compiler settings, - * typically the target FPU. If the FPU has been set to NEON (-mfpu=neon - * with GCC) then the compiler will define __ARM_NEON__ and we can rely - * unconditionally on NEON instructions not crashing, otherwise we must - * disable use of NEON instructions. - * - * NOTE: at present these optimizations depend on 'ALIGNED_MEMORY', so they - * can only be turned on automatically if that is supported too. If - * PNG_ARM_NEON_OPT is set in CPPFLAGS (to >0) then arm/arm_init.c will fail - * to compile with an appropriate #error if ALIGNED_MEMORY has been turned - * off. - */ -# if defined(__ARM_NEON__) && defined(PNG_ALIGNED_MEMORY_SUPPORTED) -# define PNG_ARM_NEON_OPT 2 -# else -# define PNG_ARM_NEON_OPT 0 -# endif -#endif - -#if PNG_ARM_NEON_OPT > 0 - /* NEON optimizations are to be at least considered by libpng, so enable the - * callbacks to do this. - */ -# define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_neon - - /* By default the 'intrinsics' code in arm/filter_neon_intrinsics.c is used - * if possible - if __ARM_NEON__ is set and the compiler version is not known - * to be broken. This is controlled by PNG_ARM_NEON_IMPLEMENTATION which can - * be: - * - * 1 The intrinsics code (the default with __ARM_NEON__) - * 2 The hand coded assembler (the default without __ARM_NEON__) - * - * It is possible to set PNG_ARM_NEON_IMPLEMENTATION in CPPFLAGS, however - * this is *NOT* supported and may cease to work even after a minor revision - * to libpng. It *is* valid to do this for testing purposes, e.g. speed - * testing or a new compiler, but the results should be communicated to the - * libpng implementation list for incorporation in the next minor release. - */ -# ifndef PNG_ARM_NEON_IMPLEMENTATION -# ifdef __ARM_NEON__ -# if defined(__clang__) - /* At present it is unknown by the libpng developers which versions - * of clang support the intrinsics, however some or perhaps all - * versions do not work with the assembler so this may be - * irrelevant, so just use the default (do nothing here.) - */ -# elif defined(__GNUC__) - /* GCC 4.5.4 NEON support is known to be broken. 4.6.3 is known to - * work, so if this *is* GCC, or G++, look for a version >4.5 - */ -# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6) -# define PNG_ARM_NEON_IMPLEMENTATION 2 -# endif /* no GNUC support */ -# endif /* __GNUC__ */ -# else /* !defined __ARM_NEON__ */ - /* The 'intrinsics' code simply won't compile without this -mfpu=neon: - */ -# define PNG_ARM_NEON_IMPLEMENTATION 2 -# endif /* __ARM_NEON__ */ -# endif /* !defined PNG_ARM_NEON_IMPLEMENTATION */ - -# ifndef PNG_ARM_NEON_IMPLEMENTATION - /* Use the intrinsics code by default. */ -# define PNG_ARM_NEON_IMPLEMENTATION 1 -# endif -#endif /* PNG_ARM_NEON_OPT > 0 */ - /* Is this a build of a DLL where compilation of the object modules requires * different preprocessor settings to those required for a simple library? If * so PNG_BUILD_DLL must be set. @@ -334,59 +244,191 @@ # define PNG_DLL_EXPORT #endif -/* asserts are turned off in release code, but are in even in release candidates - * because often system builders only check future libpng releases when a - * release candidate is available. +/* This is a global switch to set the compilation for an installed system + * (a release build). It can be set for testing debug builds to ensure that + * they will compile when the build type is switched to RC or STABLE, the + * default is just to use PNG_LIBPNG_BUILD_BASE_TYPE. Set this in CPPFLAGS + * with either: + * + * -DPNG_RELEASE_BUILD Turns on the release compile path + * -DPNG_RELEASE_BUILD=0 Turns it off + * or in your pngusr.h with + * #define PNG_RELEASE_BUILD=1 Turns on the release compile path + * #define PNG_RELEASE_BUILD=0 Turns it off */ -#if PNG_LIBPNG_BUILD_BASE_TYPE == PNG_LIBPNG_BUILD_STABLE -# define NDEBUG +#ifndef PNG_RELEASE_BUILD +# define PNG_RELEASE_BUILD (PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC) #endif -#ifndef PNG_VERSION_INFO_ONLY -# include + +/* General purpose macros avoid the need to put #if PNG_RELEASE_BUILD + * macro blocks around function declarations and definitions when the + * parameter number varies. Using these results in slightly cleaner code. + */ +#if PNG_RELEASE_BUILD +# define only_rel(text) text +# define only_deb(text) +# define param_rel(param) param, +# define param_deb(param) +#else +# define only_rel(text) +# define only_deb(text) text +# define param_rel(param) +# define param_deb(param) param, #endif +/* The affirm mechanism results in a minimal png_error() in released versions + * ('STABLE' versions) and a more descriptive PNG_ABORT in all other cases, + * when the "condition" is false (zero). If "condition" is true (nonzero), + * then the affirm mechanism does nothing. + * + * The PNG_RELEASE_BUILD macro, defined above, controls the behavior of + * 'affirm': if set to 1 affirm will call png_error (or png_err) rather than + * abort. The png_error text is the minimal (file location) text in this case, + * if it is produced. This flag indicates a STABLE (or RC) build. + * + * The macros rely on the naming convention throughout this code - png_ptr + * exists and is of type png_const_structrp or a compatible type - and the + * presence in each file of a uniquely defined macro PNG_SRC_FILE; a number + * indicating which file this is (this is to save space in released versions). + * + * 'affirm' is intended to look like the ANSI-C macro; note that + * this macro can coexist with the assert macro if is + * included. + * + * PNG_SRC_LINE is the position of the affirm macro. There are currently 15 + * main source files (4 bits) and the biggest (pngrtran.c) has more than 4095 + * lines (12 bits). However, to ensure the number will fit into 16-bits in the + * future and to allow hardware files to use affirm, the encoding is a bit-wise + * encoding based on the current number of lines. + * + * 'debug' is a version of 'affirm' that is completely removed from RELEASE + * builds. This is used when either an unexpected condition is completely + * handled or when it can't be handled even by png_error, for example after a + * memory overwrite. + * + * UNTESTED is used to mark code that has not been tested; it causes an assert + * if the code is executed and (therefore) tested. UNTESTED should not remain + * in release candidate code. + * + * PNG_AFFIRM_TEXT is set to 1 if affirm text should be produced, either + * the minimal text or, if PNG_RELEASE_BUILD is 0, the more verbose text + * including the 'condition' string. This value depends on whether the + * build supports an appropriate way of outputting the message. + * + * Note that PNG_AFFIRM_TEXT is not configurable but is worked out here: this + * is just the affirm code; there's no reason to allow configuration of this + * option. + */ +#if PNG_RELEASE_BUILD ?\ + (defined PNG_ERROR_TEXT_SUPPORTED) :\ + (defined PNG_WARNINGS_SUPPORTED) || (defined PNG_CONSOLE_IO_SUPPORTED) +# define PNG_AFFIRM_TEXT 1 +#else +# define PNG_AFFIRM_TEXT 0 +#endif /* PNG_AFFIRM_TEXT definition */ + +#define PNG_SRC_LINE (PNG_SRC_FILE + __LINE__) + +/* png_affirmpp and png_impossiblepp are macros to make the correct call to the + * png_affirm function; these macros do not assume that the png_structp is + * called png_ptr. + */ +#if PNG_RELEASE_BUILD +# define png_affirmpp(pp, cond)\ + do\ + if (!(cond)) png_affirm(pp, PNG_SRC_LINE);\ + while (0) +# define png_affirmexp(pp, cond)\ + ((cond) ? (void)0 : png_affirm(pp, PNG_SRC_LINE)) +# define png_handled(pp, m) ((void)0) +# define png_impossiblepp(pp, reason) png_affirm(pp, PNG_SRC_LINE) + +# define debug(cond) do {} while (0) +# define debug_handled(cond) do {} while (0) +# if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC + /* Make sure there are no 'UNTESTED' macros in released code: */ +# define UNTESTED libpng untested code +# endif +# define NOT_REACHED do {} while (0) +#else +# define png_affirmpp(pp, cond)\ + do\ + if (!(cond)) png_affirm(pp, #cond, PNG_SRC_LINE);\ + while (0) +# define png_affirmexp(pp, cond)\ + ((cond) ? (void)0 : png_affirm(pp, #cond, PNG_SRC_LINE)) +# define png_handled(pp, m) (png_handled_affirm((pp), (m), PNG_SRC_LINE)) +# define png_impossiblepp(pp, reason) png_affirm(pp, reason, PNG_SRC_LINE) + +# define debug(cond) png_affirmpp(png_ptr, cond) +# define debug_handled(cond)\ + do\ + if (!(cond)) png_handled(png_ptr, #cond);\ + while (0) +# define UNTESTED png_affirm(png_ptr, "untested code", PNG_SRC_LINE); +# define NOT_REACHED png_affirm(png_ptr, "NOT REACHED", PNG_SRC_LINE) +#endif + +#define affirm(cond) png_affirmpp(png_ptr, cond) +#define affirmexp(cond) png_affirmexp(png_ptr, cond) +#define handled(m) png_handled(png_ptr, (m)) +#define impossible(cond) png_impossiblepp(png_ptr, cond) +#define implies(a, b) debug(!(a) || (b)) + +/* The defines for PNG_SRC_FILE: */ +#define PNG_SRC_FILE_(f,lines) PNG_SRC_FILE_ ## f + lines + +#define PNG_SRC_FILE_png 0 +#define PNG_SRC_FILE_pngerror (PNG_SRC_FILE_png +8192) +#define PNG_SRC_FILE_pngget (PNG_SRC_FILE_pngerror +2048) +#define PNG_SRC_FILE_pngmem (PNG_SRC_FILE_pngget +2048) +#define PNG_SRC_FILE_pngpread (PNG_SRC_FILE_pngmem +1024) +#define PNG_SRC_FILE_pngread (PNG_SRC_FILE_pngpread +2048) +#define PNG_SRC_FILE_pngrio (PNG_SRC_FILE_pngread +8192) +#define PNG_SRC_FILE_pngrtran (PNG_SRC_FILE_pngrio +1024) +#define PNG_SRC_FILE_pngrutil (PNG_SRC_FILE_pngrtran +8192) +#define PNG_SRC_FILE_pngset (PNG_SRC_FILE_pngrutil +8192) +#define PNG_SRC_FILE_pngtrans (PNG_SRC_FILE_pngset +2048) +#define PNG_SRC_FILE_pngwio (PNG_SRC_FILE_pngtrans +4096) +#define PNG_SRC_FILE_pngwrite (PNG_SRC_FILE_pngwio +1024) +#define PNG_SRC_FILE_pngwtran (PNG_SRC_FILE_pngwrite +4096) +#define PNG_SRC_FILE_pngwutil (PNG_SRC_FILE_pngwtran +1024) + +#define PNG_SRC_FILE_arm_arm_init (PNG_SRC_FILE_pngwutil +8192) +#define PNG_SRC_FILE_arm_filter_neon_intrinsics\ + (PNG_SRC_FILE_arm_arm_init +1024) + +/* Add new files by changing the following line: */ +#define PNG_SRC_FILE_LAST (PNG_SRC_FILE_arm_filter_neon_intrinsics +1024) + +/* The following #define must list the files in exactly the same order as + * the above. + */ +#define PNG_FILES\ + PNG_apply(png)\ + PNG_apply(pngerror)\ + PNG_apply(pngget)\ + PNG_apply(pngmem)\ + PNG_apply(pngpread)\ + PNG_apply(pngread)\ + PNG_apply(pngrio)\ + PNG_apply(pngrtran)\ + PNG_apply(pngrutil)\ + PNG_apply(pngset)\ + PNG_apply(pngtrans)\ + PNG_apply(pngwio)\ + PNG_apply(pngwrite)\ + PNG_apply(pngwtran)\ + PNG_apply(pngwutil)\ + PNG_apply(arm_arm_init)\ + PNG_apply(arm_filter_neon_intrinsics)\ + PNG_end + /* SECURITY and SAFETY: * - * By default libpng is built without any internal limits on image size, - * individual heap (png_malloc) allocations or the total amount of memory used. - * If PNG_SAFE_LIMITS_SUPPORTED is defined, however, the limits below are used - * (unless individually overridden). These limits are believed to be fairly - * safe, but builders of secure systems should verify the values against the - * real system capabilities. - */ -#ifdef PNG_SAFE_LIMITS_SUPPORTED - /* 'safe' limits */ -# ifndef PNG_USER_WIDTH_MAX -# define PNG_USER_WIDTH_MAX 1000000 -# endif -# ifndef PNG_USER_HEIGHT_MAX -# define PNG_USER_HEIGHT_MAX 1000000 -# endif -# ifndef PNG_USER_CHUNK_CACHE_MAX -# define PNG_USER_CHUNK_CACHE_MAX 128 -# endif -# ifndef PNG_USER_CHUNK_MALLOC_MAX -# define PNG_USER_CHUNK_MALLOC_MAX 8000000 -# endif -#else - /* values for no limits */ -# ifndef PNG_USER_WIDTH_MAX -# define PNG_USER_WIDTH_MAX 0x7fffffff -# endif -# ifndef PNG_USER_HEIGHT_MAX -# define PNG_USER_HEIGHT_MAX 0x7fffffff -# endif -# ifndef PNG_USER_CHUNK_CACHE_MAX -# define PNG_USER_CHUNK_CACHE_MAX 0 -# endif -# ifndef PNG_USER_CHUNK_MALLOC_MAX -# define PNG_USER_CHUNK_MALLOC_MAX 0 -# endif -#endif - -/* Moved to pngpriv.h at libpng-1.5.0 */ -/* NOTE: some of these may have been used in external applications as - * these definitions were exposed in pngconf.h prior to 1.5. + * libpng is built with support for internal limits on image dimensions and + * memory usage. These are documented in scripts/pnglibconf.dfa of the + * source and recorded in the machine generated header file pnglibconf.h. */ /* If you are running on a machine where you cannot allocate more @@ -414,12 +456,17 @@ # define PNG_UNUSED(param) (void)param; #endif +/* This is a convenience for parameters which are not used in release + * builds. + */ +#define PNG_UNUSEDRC(param) only_rel(PNG_UNUSED(param)) + /* Just a little check that someone hasn't tried to define something * contradictory. */ -#if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K) +#if (PNG_ZBUF_SIZE > 32768) && defined(PNG_MAX_MALLOC_64K) # undef PNG_ZBUF_SIZE -# define PNG_ZBUF_SIZE 65536L +# define PNG_ZBUF_SIZE 32768 #endif /* If warnings or errors are turned off the code is disabled or redirected here. @@ -446,6 +493,7 @@ */ #ifdef __cplusplus # define png_voidcast(type, value) static_cast(value) +# define png_upcast(type, value) static_cast(value) # define png_constcast(type, value) const_cast(value) # define png_aligncast(type, value) \ static_cast(static_cast(value)) @@ -453,6 +501,7 @@ static_cast(static_cast(value)) #else # define png_voidcast(type, value) (value) +# define png_upcast(type, value) ((type)(value)) # define png_constcast(type, value) ((type)(value)) # define png_aligncast(type, value) ((void*)(value)) # define png_aligncastconst(type, value) ((const void*)(value)) @@ -513,6 +562,47 @@ # define png_isaligned(ptr, type) 0 #endif +/* Buffer alignment control. These #defines control how the buffers used during + * read are aligned and how big they are. + */ +#ifndef PNG_ROW_BUFFER_ALIGN_TYPE + /* The absolute minimum alignment for a row buffer is that required for + * png_uint_32 direct access. The #define is of a legal C type that can be + * used as the type in the definition of the first member of a C union; give + * a typedef name if in doubt. + */ +# define PNG_ROW_BUFFER_ALIGN_TYPE png_uint_32 +#endif /* !ROW_BUFFER_ALIGN_TYPE */ +#ifndef PNG_ROW_BUFFER_BYTE_ALIGN + /* This is the minimum size in bytes of the buffer used while processing + * parts of row. Except at the end of the row pixels will always be + * processed in blocks such that the block size is a multiple of this number + */ +# define PNG_ROW_BUFFER_BYTE_ALIGN\ + ((unsigned int)/*SAFE*/(sizeof (PNG_ROW_BUFFER_ALIGN_TYPE))) +#endif /* !ROW_BUFFER_BYTE_ALIGN */ +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +# define PNG_MAX_PIXEL_BYTES 16U /* 4x32-bit channels */ +#else /* !READ_USER_TRANSFORM */ +# define PNG_MAX_PIXEL_BYTES 8U /* 4x16-bit channels */ +#endif /* !READ_USER_TRANSFORM */ +/* PNG_ROW_BUFFER_SIZE is a compile time constant for the size of the row + * buffer. The minimum size of 2048 bytes is intended to allow the buffer to + * hold a complete 256 entry color map of 64-bit (8-byte) pixels. This is a + * requirement at some points of the colormap handling code. + * + * The maximum size is intended to allow (unsigned int) indexing of the buffer, + * it only affects systems with a 16-bit unsigned value where it limits the + * maximum to 4096 bytes. + */ +#define PNG_MIN_ROW_BUFFER_SIZE\ + (PNG_MAX_PIXEL_BYTES * PNG_ROW_BUFFER_BYTE_ALIGN * 8U) +#define PNG_MAX_ROW_BUFFER_SIZE ((UINT_MAX / 16U) + 1U) +#ifndef PNG_ROW_BUFFER_SIZE +# define PNG_ROW_BUFFER_SIZE\ + (PNG_MIN_ROW_BUFFER_SIZE < 2048U ? 2048U : PNG_MIN_ROW_BUFFER_SIZE) +#endif /* ROW_BUFFER_SIZE */ + /* End of memory model/platform independent support */ /* End of 1.5.0beta36 move from pngconf.h */ @@ -525,117 +615,19 @@ * are defined in png.h because they need to be visible to applications * that call png_set_unknown_chunk(). */ -/* #define PNG_HAVE_IHDR 0x01 (defined in png.h) */ -/* #define PNG_HAVE_PLTE 0x02 (defined in png.h) */ -#define PNG_HAVE_IDAT 0x04 -/* #define PNG_AFTER_IDAT 0x08 (defined in png.h) */ -#define PNG_HAVE_IEND 0x10 - /* 0x20 (unused) */ - /* 0x40 (unused) */ - /* 0x80 (unused) */ -#define PNG_HAVE_CHUNK_HEADER 0x100 -#define PNG_WROTE_tIME 0x200 -#define PNG_WROTE_INFO_BEFORE_PLTE 0x400 - /* 0x800 (unused) */ -#define PNG_HAVE_PNG_SIGNATURE 0x1000 -#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */ - /* 0x4000 (unused) */ -#define PNG_IS_READ_STRUCT 0x8000 /* Else is a write struct */ - -/* Flags for the transformations the PNG library does on the image data */ -#define PNG_BGR 0x0001 -#define PNG_INTERLACE 0x0002 -#define PNG_PACK 0x0004 -#define PNG_SHIFT 0x0008 -#define PNG_SWAP_BYTES 0x0010 -#define PNG_INVERT_MONO 0x0020 -#define PNG_QUANTIZE 0x0040 -#define PNG_COMPOSE 0x0080 /* Was PNG_BACKGROUND */ - /* 0x0100 unused */ -#define PNG_EXPAND_16 0x0200 /* Added to libpng 1.5.2 */ -#define PNG_16_TO_8 0x0400 /* Becomes 'chop' in 1.5.4 */ -#define PNG_RGBA 0x0800 -#define PNG_EXPAND 0x1000 -#define PNG_GAMMA 0x2000 -#define PNG_GRAY_TO_RGB 0x4000 -#define PNG_FILLER 0x8000 -#define PNG_PACKSWAP 0x10000 -#define PNG_SWAP_ALPHA 0x20000 -#define PNG_STRIP_ALPHA 0x40000 -#define PNG_INVERT_ALPHA 0x80000 -#define PNG_USER_TRANSFORM 0x100000 -#define PNG_RGB_TO_GRAY_ERR 0x200000 -#define PNG_RGB_TO_GRAY_WARN 0x400000 -#define PNG_RGB_TO_GRAY 0x600000 /* two bits, RGB_TO_GRAY_ERR|WARN */ -#define PNG_ENCODE_ALPHA 0x800000 /* Added to libpng-1.5.4 */ -#define PNG_ADD_ALPHA 0x1000000 /* Added to libpng-1.2.7 */ -#define PNG_EXPAND_tRNS 0x2000000 /* Added to libpng-1.2.9 */ -#define PNG_SCALE_16_TO_8 0x4000000 /* Added to libpng-1.5.4 */ - /* 0x8000000 unused */ - /* 0x10000000 unused */ - /* 0x20000000 unused */ - /* 0x40000000 unused */ -/* Flags for png_create_struct */ -#define PNG_STRUCT_PNG 0x0001 -#define PNG_STRUCT_INFO 0x0002 - -/* Scaling factor for filter heuristic weighting calculations */ -#define PNG_WEIGHT_FACTOR (1<<(PNG_WEIGHT_SHIFT)) -#define PNG_COST_FACTOR (1<<(PNG_COST_SHIFT)) - -/* Flags for the png_ptr->flags rather than declaring a byte for each one */ -#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY 0x0001 -#define PNG_FLAG_ZSTREAM_INITIALIZED 0x0002 /* Added to libpng-1.6.0 */ - /* 0x0004 unused */ -#define PNG_FLAG_ZSTREAM_ENDED 0x0008 /* Added to libpng-1.6.0 */ - /* 0x0010 unused */ - /* 0x0020 unused */ -#define PNG_FLAG_ROW_INIT 0x0040 -#define PNG_FLAG_FILLER_AFTER 0x0080 -#define PNG_FLAG_CRC_ANCILLARY_USE 0x0100 -#define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200 -#define PNG_FLAG_CRC_CRITICAL_USE 0x0400 -#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800 -#define PNG_FLAG_ASSUME_sRGB 0x1000 /* Added to libpng-1.5.4 */ -#define PNG_FLAG_OPTIMIZE_ALPHA 0x2000 /* Added to libpng-1.5.4 */ -#define PNG_FLAG_DETECT_UNINITIALIZED 0x4000 /* Added to libpng-1.5.4 */ -/* #define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000 */ -/* #define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000 */ -#define PNG_FLAG_LIBRARY_MISMATCH 0x20000 -#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000 -#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000 -#define PNG_FLAG_BENIGN_ERRORS_WARN 0x100000 /* Added to libpng-1.4.0 */ -#define PNG_FLAG_APP_WARNINGS_WARN 0x200000 /* Added to libpng-1.6.0 */ -#define PNG_FLAG_APP_ERRORS_WARN 0x400000 /* Added to libpng-1.6.0 */ -#define PNG_FLAG_BACKGROUND_IS_GRAY 0x800000 -#define PNG_FLAG_BACKGROUND_EXPAND 0x1000000 - /* 0x2000000 unused */ - /* 0x4000000 unused */ - /* 0x8000000 unused */ - /* 0x10000000 unused */ - /* 0x20000000 unused */ - /* 0x40000000 unused */ - -#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \ - PNG_FLAG_CRC_ANCILLARY_NOWARN) - -#define PNG_FLAG_CRC_CRITICAL_MASK (PNG_FLAG_CRC_CRITICAL_USE | \ - PNG_FLAG_CRC_CRITICAL_IGNORE) - -#define PNG_FLAG_CRC_MASK (PNG_FLAG_CRC_ANCILLARY_MASK | \ - PNG_FLAG_CRC_CRITICAL_MASK) - -/* Save typing and make code easier to understand */ - -#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \ - abs((int)((c1).green) - (int)((c2).green)) + \ - abs((int)((c1).blue) - (int)((c2).blue))) +/* #define PNG_HAVE_IHDR 0x01 (defined as (int) in png.h) */ +/* #define PNG_HAVE_PLTE 0x02 (defined as (int) in png.h) */ +#define PNG_HAVE_IDAT 0x04U +/* #define PNG_AFTER_IDAT 0x08 (defined as (int) in png.h) */ +#define PNG_HAVE_IEND 0x10U +#define PNG_HAVE_PNG_SIGNATURE 0x20U #if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\ defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) /* See below for the definitions of the tables used in these macros */ -#define PNG_sRGB_FROM_LINEAR(linear) ((png_byte)((png_sRGB_base[(linear)>>15] +\ - ((((linear)&0x7fff)*png_sRGB_delta[(linear)>>15])>>12)) >> 8)) +#define PNG_sRGB_FROM_LINEAR(pp, linear) png_check_byte(pp,\ + (png_sRGB_base[(linear)>>15] +\ + ((((linear)&0x7fffU)*png_sRGB_delta[(linear)>>15])>>12)) >> 8) /* Given a value 'linear' in the range 0..255*65535 calculate the 8-bit sRGB * encoded value with maximum error 0.646365. Note that the input is not a * 16-bit value; it has been multiplied by 255! */ @@ -649,14 +641,43 @@ * 65535 - this has a maximum error of .502. Over the range 0..65535*65535 it * only gives off-by-one errors and only for 0.5% (1 in 200) of the values. */ -#define PNG_DIV65535(v24) (((v24) + 32895) >> 16) -#define PNG_DIV257(v16) PNG_DIV65535((png_uint_32)(v16) * 255) +#define PNG_DIV65535(v24) (((v24) + 32895U) >> 16) +#define PNG_DIV257(v16) PNG_DIV65535((png_uint_32)(v16) * 255U) -/* Added to libpng-1.2.6 JB */ +/* Added to libpng-1.2.6 JB + * Modified in libpng-1.7.0 to avoid the intermediate calculation overflow + * when: + * + * pixel_bits == 4: any width over 0x3FFFFFFEU overflows + * pixel_bits == 2: any width over 0x7FFFFFFCU overflows + * + * In both these cases any width results in a rowbytes that fits in 32 bits. + * The problem arose in previous versions because the calculation used was + * simply ((width x pixel-bit-depth)+7)/8. At the cost of more calculations + * on pixel_depth this avoids the problem. + */ +#define PNG_SHIFTOF(pixel_bits/*<8*/) \ + ( (pixel_bits) == 1 ? 3 : \ + ( (pixel_bits) == 2 ? 2 : \ + ( (pixel_bits) == 4 ? 1 : \ + 0/*force bytes*/ ) ) ) +#define PNG_ADDOF(pixel_bits/*<8*/) ((1U<= 8 ? \ - ((png_size_t)(width) * (((png_size_t)(pixel_bits)) >> 3)) : \ - (( ((png_size_t)(width) * ((png_size_t)(pixel_bits))) + 7) >> 3) ) + ((pixel_bits) >= 8 ? \ + ((png_alloc_size_t)(width) * ((pixel_bits) >> 3)) : \ + (((png_alloc_size_t)(width) + PNG_ADDOF(pixel_bits)) >> \ + PNG_SHIFTOF(pixel_bits)) ) + +/* This macros, added in 1.7.0, makes it easy to deduce the number of channels + * and therefore the pixel depth from the color type. The PNG specification + * numbers are used in preference to the png.h constants to make it more clear + * why the macro works. + */ +#define PNG_COLOR_TYPE_CHANNELS(ct)\ + (((ct) & PNG_COLOR_MASK_PALETTE) ?\ + 1U : 1U+((ct) & 2U/*COLOR*/)+(((ct)>>2)&1U/*ALPHA*/)) +#define PNG_CHANNELS(ps) PNG_COLOR_TYPE_CHANNELS((ps).color_type) +#define PNG_PIXEL_DEPTH(ps) (PNG_CHANNELS(ps) * (ps).bit_depth) /* PNG_OUT_OF_RANGE returns true if value is outside the range * ideal-delta..ideal+delta. Each argument is evaluated twice. @@ -666,6 +687,18 @@ #define PNG_OUT_OF_RANGE(value, ideal, delta) \ ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) ) +/* Handling of bit-field masks. Because the expression: + * + * bit_field & ~mask + * + * has implementation defined behavior in ANSI C-90 for many (int) values of + * 'mask' and because some of these are defined in png.h and passed in (int) + * parameters use of '~' has been expunged in libpng 1.7 and replaced by this + * macro, which is well defined in ANSI C-90 (there is a similar, 16-bit, + * version in pngstruct.h for the colorspace flags.) + */ +#define PNG_BIC_MASK(flags) (0xFFFFFFFFU - (flags)) + /* Conversions between fixed and floating point, only defined if * required (to make sure the code doesn't accidentally use float * when it is supposedly disabled.) @@ -730,7 +763,7 @@ * zlib version number and because this affects handling of certain broken * PNG files the -I directives must match. * - * The most likely explanation is that you passed a -I in CFLAGS, this will + * The most likely explanation is that you passed a -I in CFLAGS. This will * not work; all the preprocessor directories and in particular all the -I * directives must be in CPPFLAGS. */ @@ -766,31 +799,152 @@ extern "C" { * * All of these functions must be declared with PNG_INTERNAL_FUNCTION. */ +/* Affirm handling */ +PNG_INTERNAL_FUNCTION(void, png_affirm,(png_const_structrp png_ptr, + param_deb(png_const_charp condition) unsigned int position), PNG_NORETURN); + +#if !PNG_RELEASE_BUILD +PNG_INTERNAL_FUNCTION(void, png_handled_affirm,(png_const_structrp png_ptr, + png_const_charp message, unsigned int position), PNG_EMPTY); + /* This is not marked PNG_NORETURN because in PNG_RELEASE_BUILD it will + * disappear and control will pass through it. + */ +#endif /* !RELEASE_BUILD */ + +/* Character/byte range checking. */ +/* GCC complains about assignments of an (int) expression to a (char) even when + * it can readily determine that the value is in range. This makes arithmetic + * on (char) or (png_byte) values tedious. The warning is not issued by + * default, but libpng coding rules require no warnings leading to excessive, + * ridiculous and dangerous expressions of the form: + * + * = (char)(expression & 0xff) + * + * They are dangerous because they hide the warning, which might actually be + * valid, and therefore merely enable introduction of undetected overflows when + * code is modified. + * + * The following macros exist to reliably detect any overflow in non-release + * builds. The theory here is that we really want to know about overflows, not + * merely hide a basically flawed compiler warning by throwing unnecessary casts + * into the code. The warnings disappear in RC builds so that the released + * (STABLE) version just assigns the value (with, possibly, a warning if someone + * turns on the -Wconversion GCC warning.) + * + * Doing it this way ensures that the code meets two very important aims: + * + * 1) Overflows are detected in pre-release tests; previously versions of libpng + * have been released that really did have overflows in the RGB calculations. + * 2) In release builds GCC specific operations, which may reduce the ability + * of other compilers and even GCC to optimize the code, are avoided. + * + * There is one important extra consequence for pre-release code; it is + * performing a lot of checks in pixel arithmetic that the release code won't + * perform. As a consequence a build time option, RANGE_CHECK, is provided + * to allow the checks to be turned off in pre-release when building for + * performance testing. This is a standard "_SUPPORTED" option except that it + * cannot be set in the system configuration (pnglibconf.h, pnglibconf.dfa). + * + * A separate macro PNG_BYTE() is provided to safely convert an unsigned value + * to the PNG byte range 0..255. This handles the fact that, technically, + * an ANSI-C (unsigned char), hence a (png_byte), may be able to store values + * outside this range. Note that if you are building on a system where this is + * true libpng is almost certainly going to produce errors; it has never been + * tested on such a system. For the moment pngconf.h ensures that this will + * not happen. + * + * PNG_UINT_16 does the same thing for a 16-bit value passed in an (int) or + * (png_uint_32) (where checking is not expected.) + */ +#if !PNG_RELEASE_BUILD +# ifndef PNG_NO_RANGE_CHECK /* Turn off even in pre-release */ +# define PNG_RANGE_CHECK_SUPPORTED +# endif +#endif + +#ifdef PNG_RANGE_CHECK_SUPPORTED +PNG_INTERNAL_FUNCTION(unsigned int, png_bit_affirm,(png_const_structrp png_ptr, + unsigned int position, unsigned int u, unsigned int bits), PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(char, png_char_affirm,(png_const_structrp png_ptr, + unsigned int position, int c), PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(png_byte, png_byte_affirm,(png_const_structrp png_ptr, + unsigned int position, int b), PNG_EMPTY); + +#if INT_MAX >= 65535 +PNG_INTERNAL_FUNCTION(png_uint_16, png_u16_affirm,(png_const_structrp png_ptr, + unsigned int position, int u), PNG_EMPTY); +# define png_check_u16(pp, u) (png_u16_affirm((pp), PNG_SRC_LINE, (u))) +#else + /* (int) cannot hold a (png_uint_16) so the above function just won't + * compile correctly, for the moment just do this: + */ +# define png_check_u16(pp, u) (u) +#endif + +# define png_check_bits(pp, u, bits)\ + (((1U<<(bits))-1) & png_bit_affirm((pp), PNG_SRC_LINE, (u), (bits))) +# define png_check_char(pp, c) (png_char_affirm((pp), PNG_SRC_LINE, (c))) +# define png_check_byte(pp, b) (png_byte_affirm((pp), PNG_SRC_LINE, (b))) +# define PNG_BYTE(b) ((png_byte)((b) & 0xFFU)) +# define PNG_UINT_16(u) ((png_uint_16)((u) & 0xFFFFU)) +#elif !(defined PNG_REMOVE_CASTS) /* && !RANGE_CHECK */ +# define png_check_bits(pp, u, bits) (((1U<<(bits))-1U) & (u)) +# define png_check_char(pp, c) ((char)(c)) +# define png_check_byte(pp, b) ((png_byte)(b)) +# define png_check_u16(pp, u) ((png_uint_16)(u)) +# define PNG_BYTE(b) ((png_byte)((b) & 0xFFU)) +# define PNG_UINT_16(u) ((png_uint_16)((u) & 0xFFFFU)) +#else /* !RANGE_CHECK */ + /* This is somewhat trust-me-it-works: if PNG_REMOVE_CASTS is defined then + * the casts, which might otherwise change the values, are completely + * removed. Use this to test your compiler to see if it makes *any* + * difference (code size or speed.) Currently NOT SUPPORTED. + * + * It also makes the PNG_BYTE and PNG_UINT_16 macros do nothing either + * NOTE: this seems safe at present but might lead to unexpected results + * if someone writes code to depend on the truncation. + */ +# define png_check_bits(pp, u, bits) (u) +# define png_check_char(pp, c) (c) +# define png_check_byte(pp, b) (b) +# define png_check_u16(pp, u) (u) +# define PNG_BYTE(b) (b) +# define PNG_UINT_16(u) (u) +#endif /* RANGE_CHECK */ + +/* Safe calculation of a rowbytes value; does a png_error if the system limits + * are exceeded. + */ +PNG_INTERNAL_FUNCTION(png_alloc_size_t,png_calc_rowbytes, + (png_const_structrp png_ptr, unsigned int pixel_depth, + png_uint_32 row_width),PNG_EMPTY); + +/* Common code to calculate the maximum number of pixels to transform or filter + * at one time; controlled by PNG_ROW_BUFFER_SIZE above: + */ +PNG_INTERNAL_FUNCTION(unsigned int,png_max_pixel_block, + (png_const_structrp png_ptr),PNG_EMPTY); + +/* Copy the row in row_buffer; this is the non-interlaced copy used in both the + * read and write code. 'x_in_dest' specifies whether the 'x' applies to + * the destination (sp->dp[x], x_in_dest tru) or the source (sp[x]->dp, + * x_in_dest false). + */ +PNG_INTERNAL_FUNCTION(void, png_copy_row,(png_const_structrp png_ptr, + png_bytep dp, png_const_bytep sp, png_uint_32 x/*in INPUT*/, + png_uint_32 width/*of INPUT*/, unsigned int pixel_depth, + int clear/*clear the final byte*/, int x_in_dest),PNG_EMPTY); /* Zlib support */ #define PNG_UNEXPECTED_ZLIB_RETURN (-7) -PNG_INTERNAL_FUNCTION(void, png_zstream_error,(png_structrp png_ptr, int ret), +PNG_INTERNAL_FUNCTION(void, png_zstream_error,(z_stream *zstream, int ret), PNG_EMPTY); /* Used by the zlib handling functions to ensure that z_stream::msg is always * set before they return. */ -#ifdef PNG_WRITE_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_free_buffer_list,(png_structrp png_ptr, - png_compression_bufferp *list),PNG_EMPTY); - /* Free the buffer list used by the compressed write code. */ -#endif - -#ifdef PNG_WRITE_FILTER_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_write_alloc_filter_row_buffers, - (png_structrp png_ptr, int filters),PNG_EMPTY); - /* Allocate pixel row buffers to cache filtered rows while testing candidate - * filters. - * TODO: avoid this, only one spare row buffer (at most) is required, this - * wastes a lot of memory for large images. - */ -#endif - #if defined(PNG_FLOATING_POINT_SUPPORTED) && \ !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \ (defined(PNG_gAMA_SUPPORTED) || defined(PNG_cHRM_SUPPORTED) || \ @@ -802,12 +956,6 @@ PNG_INTERNAL_FUNCTION(png_fixed_point,png_fixed,(png_const_structrp png_ptr, double fp, png_const_charp text),PNG_EMPTY); #endif -/* Check the user version string for compatibility, returns false if the version - * numbers aren't compatible. - */ -PNG_INTERNAL_FUNCTION(int,png_user_version_check,(png_structrp png_ptr, - png_const_charp user_png_ver),PNG_EMPTY); - /* Internal base allocator - no messages, NULL on failure to allocate. This * does, however, call the application provided allocator and that could call * png_error (although that would be a bug in the application implementation.) @@ -857,69 +1005,60 @@ PNG_INTERNAL_FUNCTION(voidpf,png_zalloc,(voidpf png_ptr, uInt items, uInt size), /* Function to free memory for zlib. PNGAPI is disallowed. */ PNG_INTERNAL_FUNCTION(void,png_zfree,(voidpf png_ptr, voidpf ptr),PNG_EMPTY); -/* Next four functions are used internally as callbacks. PNGCBAPI is required - * but not PNG_EXPORT. PNGAPI added at libpng version 1.2.3, changed to - * PNGCBAPI at 1.5.0 +/* The next three functions are used by png_init_io to set the default + * implementations for reading or writing to a stdio (png_FILE_p) stream. + * They can't be static because in 1.7 png_init_io needs to reference them. */ - +#ifdef PNG_STDIO_SUPPORTED +# ifdef PNG_READ_SUPPORTED PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_read_data,(png_structp png_ptr, png_bytep data, png_size_t length),PNG_EMPTY); +# endif /* READ */ -#ifdef PNG_PROGRESSIVE_READ_SUPPORTED -PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_push_fill_buffer,(png_structp png_ptr, - png_bytep buffer, png_size_t length),PNG_EMPTY); -#endif - +# ifdef PNG_WRITE_SUPPORTED PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_write_data,(png_structp png_ptr, png_bytep data, png_size_t length),PNG_EMPTY); -#ifdef PNG_WRITE_FLUSH_SUPPORTED -# ifdef PNG_STDIO_SUPPORTED +# ifdef PNG_WRITE_FLUSH_SUPPORTED PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_flush,(png_structp png_ptr), PNG_EMPTY); -# endif -#endif +# endif /* WRITE_FLUSH */ +# endif /* WRITE */ +#endif /* STDIO */ -/* Reset the CRC variable */ -PNG_INTERNAL_FUNCTION(void,png_reset_crc,(png_structrp png_ptr),PNG_EMPTY); +/* Reset the CRC variable. The CRC is initialized with the chunk tag (4 bytes). + * NOTE: at present png_struct::chunk_name MUST be set before this as well so + * that png_struct::current_crc is initialized correctly! + */ +PNG_INTERNAL_FUNCTION(void,png_reset_crc,(png_structrp png_ptr, + png_const_bytep chunk_tag), PNG_EMPTY); /* Write the "data" buffer to whatever output you are using */ PNG_INTERNAL_FUNCTION(void,png_write_data,(png_structrp png_ptr, - png_const_bytep data, png_size_t length),PNG_EMPTY); + png_const_voidp data, png_size_t length),PNG_EMPTY); /* Read and check the PNG file signature */ PNG_INTERNAL_FUNCTION(void,png_read_sig,(png_structrp png_ptr, png_inforp info_ptr),PNG_EMPTY); -/* Read the chunk header (length + type name) */ -PNG_INTERNAL_FUNCTION(png_uint_32,png_read_chunk_header,(png_structrp png_ptr), - PNG_EMPTY); - /* Read data from whatever input you are using into the "data" buffer */ -PNG_INTERNAL_FUNCTION(void,png_read_data,(png_structrp png_ptr, png_bytep data, +PNG_INTERNAL_FUNCTION(void,png_read_data,(png_structrp png_ptr, png_voidp data, png_size_t length),PNG_EMPTY); /* Read bytes into buf, and update png_ptr->crc */ -PNG_INTERNAL_FUNCTION(void,png_crc_read,(png_structrp png_ptr, png_bytep buf, +PNG_INTERNAL_FUNCTION(void,png_crc_read,(png_structrp png_ptr, png_voidp buf, png_uint_32 length),PNG_EMPTY); /* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */ PNG_INTERNAL_FUNCTION(int,png_crc_finish,(png_structrp png_ptr, png_uint_32 skip),PNG_EMPTY); -/* Read the CRC from the file and compare it to the libpng calculated CRC */ -PNG_INTERNAL_FUNCTION(int,png_crc_error,(png_structrp png_ptr),PNG_EMPTY); - /* Calculate the CRC over a section of data. Note that we are only * passing a maximum of 64K on systems that have this as a memory limit, * since this is the maximum buffer size we can specify. */ PNG_INTERNAL_FUNCTION(void,png_calculate_crc,(png_structrp png_ptr, - png_const_bytep ptr, png_size_t length),PNG_EMPTY); - -#ifdef PNG_WRITE_FLUSH_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_flush,(png_structrp png_ptr),PNG_EMPTY); -#endif + png_const_voidp ptr, png_size_t length),PNG_EMPTY); /* Write various chunks */ @@ -931,11 +1070,7 @@ PNG_INTERNAL_FUNCTION(void,png_write_IHDR,(png_structrp png_ptr, int compression_method, int filter_method, int interlace_method),PNG_EMPTY); PNG_INTERNAL_FUNCTION(void,png_write_PLTE,(png_structrp png_ptr, - png_const_colorp palette, png_uint_32 num_pal),PNG_EMPTY); - -PNG_INTERNAL_FUNCTION(void,png_compress_IDAT,(png_structrp png_ptr, - png_const_bytep row_data, png_alloc_size_t row_data_length, int flush), - PNG_EMPTY); + png_const_colorp palette, unsigned int num_pal),PNG_EMPTY); PNG_INTERNAL_FUNCTION(void,png_write_IEND,(png_structrp png_ptr),PNG_EMPTY); @@ -952,7 +1087,7 @@ PNG_INTERNAL_FUNCTION(void,png_write_sBIT,(png_structrp png_ptr, #ifdef PNG_WRITE_cHRM_SUPPORTED PNG_INTERNAL_FUNCTION(void,png_write_cHRM_fixed,(png_structrp png_ptr, const png_xy *xy), PNG_EMPTY); - /* The xy value must have been previously validated */ + /* The xy value must have been previously validated */ #endif #ifdef PNG_WRITE_sRGB_SUPPORTED @@ -962,7 +1097,7 @@ PNG_INTERNAL_FUNCTION(void,png_write_sRGB,(png_structrp png_ptr, #ifdef PNG_WRITE_iCCP_SUPPORTED PNG_INTERNAL_FUNCTION(void,png_write_iCCP,(png_structrp png_ptr, - png_const_charp name, png_const_bytep profile), PNG_EMPTY); + png_const_charp name, png_const_voidp profile), PNG_EMPTY); /* The profile must have been previously validated for correctness, the * length comes from the first four bytes. Only the base, deflate, * compression is supported. @@ -993,12 +1128,12 @@ PNG_INTERNAL_FUNCTION(void,png_write_hIST,(png_structrp png_ptr, /* Chunks that have keywords */ #ifdef PNG_WRITE_tEXt_SUPPORTED PNG_INTERNAL_FUNCTION(void,png_write_tEXt,(png_structrp png_ptr, - png_const_charp key, png_const_charp text, png_size_t text_len),PNG_EMPTY); + png_const_charp key, png_const_charp text, png_size_t text_len),PNG_EMPTY); #endif #ifdef PNG_WRITE_zTXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_write_zTXt,(png_structrp png_ptr, png_const_charp - key, png_const_charp text, png_size_t text_len, int compression),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_write_zTXt,(png_structrp png_ptr, + png_const_charp key, png_const_charp text, int compression),PNG_EMPTY); #endif #ifdef PNG_WRITE_iTXt_SUPPORTED @@ -1039,330 +1174,599 @@ PNG_INTERNAL_FUNCTION(void,png_write_sCAL_s,(png_structrp png_ptr, int unit, png_const_charp width, png_const_charp height),PNG_EMPTY); #endif -/* Called when finished processing a row of data */ -PNG_INTERNAL_FUNCTION(void,png_write_finish_row,(png_structrp png_ptr), - PNG_EMPTY); +#ifdef PNG_WRITE_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_start_IDAT,(png_structrp png_ptr), + PNG_EMPTY); + /* Do any required initialization before IDAT or row processing starts. */ -/* Internal use only. Called before first row of data */ -PNG_INTERNAL_FUNCTION(void,png_write_start_row,(png_structrp png_ptr), - PNG_EMPTY); - -/* Combine a row of data, dealing with alpha, etc. if requested. 'row' is an - * array of png_ptr->width pixels. If the image is not interlaced or this - * is the final pass this just does a memcpy, otherwise the "display" flag - * is used to determine whether to copy pixels that are not in the current pass. +/* Choose the best filter to use and filter the row data then write it out. If + * WRITE_FILTERING is not supported this just writes the data out with a zero + * (NONE) filter byte. * - * Because 'png_do_read_interlace' (below) replicates pixels this allows this - * function to achieve the documented 'blocky' appearance during interlaced read - * if display is 1 and the 'sparkle' appearance, where existing pixels in 'row' - * are not changed if they are not in the current pass, when display is 0. - * - * 'display' must be 0 or 1, otherwise the memcpy will be done regardless. - * - * The API always reads from the png_struct row buffer and always assumes that - * it is full width (png_do_read_interlace has already been called.) - * - * This function is only ever used to write to row buffers provided by the - * caller of the relevant libpng API and the row must have already been - * transformed by the read transformations. - * - * The PNG_USE_COMPILE_TIME_MASKS option causes generation of pre-computed - * bitmasks for use within the code, otherwise runtime generated masks are used. - * The default is compile time masks. + * This may be called multiple times per row, but calls must be in 'x' order; + * first a call with x 0 to mark the start of the row and, at the end, one with + * PNG_ROW_END set (this can be done in the same function call if the whole row + * is passed.) The following flags are used internally to control pass + * filtering and deflate: */ -#ifndef PNG_USE_COMPILE_TIME_MASKS -# define PNG_USE_COMPILE_TIME_MASKS 1 -#endif -PNG_INTERNAL_FUNCTION(void,png_combine_row,(png_const_structrp png_ptr, - png_bytep row, int display),PNG_EMPTY); +enum +{ + png_pass_last =0x1U, /* This is the last pass in the image */ + png_pass_last_row =0x2U, /* This is the last row in a pass */ + png_pass_first_row =0x4U, /* This is the first row in a pass */ + png_row_end =0x8U, /* This is the last block in the row */ + png_no_row_info =0x0U /* Placeholder */ -#ifdef PNG_READ_INTERLACING_SUPPORTED -/* Expand an interlaced row: the 'row_info' describes the pass data that has - * been read in and must correspond to the pixels in 'row', the pixels are - * expanded (moved apart) in 'row' to match the final layout, when doing this - * the pixels are *replicated* to the intervening space. This is essential for - * the correct operation of png_combine_row, above. - */ -PNG_INTERNAL_FUNCTION(void,png_do_read_interlace,(png_row_infop row_info, - png_bytep row, int pass, png_uint_32 transformations),PNG_EMPTY); -#endif - -/* GRR TO DO (2.0 or whenever): simplify other internal calling interfaces */ - -#ifdef PNG_WRITE_INTERLACING_SUPPORTED -/* Grab pixels out of a row for an interlaced pass */ -PNG_INTERNAL_FUNCTION(void,png_do_write_interlace,(png_row_infop row_info, - png_bytep row, int pass),PNG_EMPTY); -#endif - -/* Unfilter a row: check the filter value before calling this, there is no point - * calling it for PNG_FILTER_VALUE_NONE. - */ -PNG_INTERNAL_FUNCTION(void,png_read_filter_row,(png_structrp pp, png_row_infop - row_info, png_bytep row, png_const_bytep prev_row, int filter),PNG_EMPTY); - -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_neon,(png_row_infop row_info, - png_bytep row, png_const_bytep prev_row),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_neon,(png_row_infop - row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_neon,(png_row_infop - row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_neon,(png_row_infop - row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_neon,(png_row_infop - row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_neon,(png_row_infop - row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_neon,(png_row_infop - row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); - -/* Choose the best filter to use and filter the row data */ -PNG_INTERNAL_FUNCTION(void,png_write_find_filter,(png_structrp png_ptr, - png_row_infop row_info),PNG_EMPTY); - -#ifdef PNG_SEQUENTIAL_READ_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_read_IDAT_data,(png_structrp png_ptr, - png_bytep output, png_alloc_size_t avail_out),PNG_EMPTY); - /* Read 'avail_out' bytes of data from the IDAT stream. If the output buffer - * is NULL the function checks, instead, for the end of the stream. In this - * case a benign error will be issued if the stream end is not found or if - * extra data has to be consumed. + /* A useful macro; return true if this is the last block of the last row in + * the image. */ -PNG_INTERNAL_FUNCTION(void,png_read_finish_IDAT,(png_structrp png_ptr), +# define PNG_IDAT_END(f) (((f) & ~png_pass_first_row) == \ + (png_row_end+png_pass_last_row+png_pass_last)) +}; +PNG_INTERNAL_FUNCTION(void,png_write_png_data,(png_structrp png_ptr, + png_bytep prev_pixels, png_const_bytep unfiltered_row, png_uint_32 x, + unsigned int width/*pixels*/, unsigned int row_info_flags), PNG_EMPTY); - /* This cleans up when the IDAT LZ stream does not end when the last image - * byte is read; there is still some pending input. + +PNG_INTERNAL_FUNCTION(void,png_write_png_rows,(png_structrp png_ptr, + png_const_bytep *rows, png_uint_32 num_rows), PNG_EMPTY); + /* As above but rows[num_rows] of correctly (PNG) formated but unfiltered + * data are passed in. For an interlaced image the rows will be interlaced + * rows and therefore may be narrower than the image width. + * + * This function advances png_structp::pass and png_structp::row_number as + * required. */ -PNG_INTERNAL_FUNCTION(void,png_read_finish_row,(png_structrp png_ptr), +/* Release memory used by the deflate mechanism */ +PNG_INTERNAL_FUNCTION(void, png_deflate_destroy, (png_structp png_ptr), PNG_EMPTY); - /* Finish a row while reading, dealing with interlacing passes, etc. */ -#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ +#endif /* WRITE */ -/* Initialize the row buffers, etc. */ -PNG_INTERNAL_FUNCTION(void,png_read_start_row,(png_structrp png_ptr),PNG_EMPTY); +#ifdef PNG_TRANSFORM_MECH_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_transform_free,(png_const_structrp png_ptr, + png_transformp *list),PNG_EMPTY); + /* Free the entire transform list, from the given point on. the argument is + * set to NULL. + */ + +PNG_INTERNAL_FUNCTION(void,png_init_transform_control,( + png_transform_controlp out, png_structp png_ptr),PNG_EMPTY); + /* Initialize a transform control for running the transform list forward (the + * read case, and write initialization, but the write case is called within + * pngtrans.c by the above function.) + */ #ifdef PNG_READ_TRANSFORMS_SUPPORTED +PNG_INTERNAL_FUNCTION(unsigned int,png_run_this_transform_list_forwards, + (png_transform_controlp tc, png_transformp *start, png_transformp end), + PNG_EMPTY); + /* Used by the transform cache code to run a sub-list, from *start to the + * transform end. + */ +#endif /* READ_TRANSFORMS */ + +#ifdef PNG_READ_SUPPORTED +PNG_INTERNAL_FUNCTION(unsigned int,png_run_transform_list_forwards, + (png_structp png_ptr, png_transform_controlp tc),PNG_EMPTY); + /* Run the transform list in the forwards direction (from PNG format to + * memory format). The transform control must be initialized completely by + * the caller. This function takes account of transforms which delete + * themselves during the run; it must be used. + */ +#endif /* READ */ + +#ifdef PNG_WRITE_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_run_transform_list_backwards, + (png_structp png_ptr, png_transform_controlp tc),PNG_EMPTY); + /* Run the transform list in the backwards direction (from memory format to + * PNG format). The transform control must be initialized completely by + * the caller. This function takes account of transforms which delete + * themselves during the run; it must be used. + */ +#endif /* WRITE */ + +PNG_INTERNAL_FUNCTION(png_transformp,png_add_transform,(png_structrp png_ptr, + size_t size, png_transform_fn fn, unsigned int order),PNG_EMPTY); + /* Add a transform, using the information in 'order' to control the position + * of the transform in the list, returning a pointer to the transform. The + * top 8 bits of 'order' control the position in the list. If a transform + * does not already exist in the list with the given value a new transform + * will be created and 'fn' and 'order' set. If there is a transform with + * that value 'fn' must match and 'order' will be updated by combining the + * new value in with a bitwise or (|). It is up to the function (fn) or the + * caller of png_add_transform to determine whether the combination is valid. + * + * 'size' is used when creating a new transform, it may be larger than + * (sizeof png_transform) if required to accomodate extra data. + * + * Prior to 1.7.0 transforms were executed in an order hard-wired into the + * code that executed the transform functions. This was summarized in the + * read case by the following comment from pngrtran.c + * (png_init_read_transformations), note that this has been marked up to + * indicate which PNG formats the transforms in the list apply to: + * + * *: applies to most formats + * A: only formats with alpha + * L: only low-bit-depth (less than 8 bits per component/pixel) + * H: only high-bit-depth (16-bits per component) + * + > From the code of png_do_read_transformations the order is: + * + * GGRR For example column: . no action + * AGG r acts on read + * BB w acts on write + * A B acts on both read and write + > + > r.r. 1) PNG_EXPAND (including PNG_EXPAND_tRNS) + > .r.r 2) PNG_STRIP_ALPHA (if no compose) + > ..rr 3) PNG_RGB_TO_GRAY + > rr.. 4) PNG_GRAY_TO_RGB iff !PNG_FLAG_BACKGROUND_IS_GRAY + > rrrr 5) PNG_COMPOSE + > rrrr 6) PNG_GAMMA + > .r.r 7) PNG_STRIP_ALPHA (if compose) + > .r.r 8) PNG_ENCODE_ALPHA + > rrrr 9) PNG_SCALE_16_TO_8 + > rrrr 10) PNG_16_TO_8 + > ..rr 11) PNG_QUANTIZE (converts to palette) + > rrrr 12) PNG_EXPAND_16 + > rr.. 13) PNG_GRAY_TO_RGB iff PNG_FLAG_BACKGROUND_IS_GRAY + > BB.. 14) PNG_INVERT_MONO + > .B.B 15) PNG_INVERT_ALPHA + > BBBB 16) PNG_SHIFT + * + * Note that transforms from this point on are used in 1.7.0 on palette + * indices as well; a png_set_pack request (for example) packs the palette + * index values if the output will be palettized and the grayscale values + * if it will not be (if the output is low-bit-grayscale, not palette.) + * + > B... 17) PNG_PACK + > ..BB 18) PNG_BGR + > B... 19) PNG_PACKSWAP + > rwrw 20) PNG_FILLER (includes PNG_ADD_ALPHA) + > .B.B 21) PNG_SWAP_ALPHA + > BBBB 22) PNG_SWAP_BYTES + > BBBB 23) PNG_USER_TRANSFORM [must be last] + * + * Finally, outside the set of transforms prior to 1.7.0, the libpng + * interlace handling required the pixels to be replicated to match the pixel + * spacing in the image row; the first part the pre-1.7.0 interlace support, + * this is still the case when reading, but for writing the interlace is now + * a transform: + * + > BBBB 24) png_do_{read,write}_interlace (interlaced images only). + * + * First transforms are grouped according to basic function using the top 3 + * bits of the order code: + */ +# define PNG_TR_START 0x0000U /* initial ops on the PNG data */ +# define PNG_TR_ARITHMETIC 0x2000U /* arithmetic linear operations */ +# define PNG_TR_CHANNEL 0x4000U /* PNG conformant format changes */ +# define PNG_TR_QUANTIZE 0x6000U /* quantize and following operations */ +# define PNG_TR_ENCODING 0x8000U /* Row encoding transforms */ +# define PNG_TR_INTERLACE 0xA000U /* write interlace transform */ + /* + * In libpng 1.7.0 the check on palette index values is moved to the start + * (of read, end of write, which is where it was before) immediately after + * the MNG filter handling + */ +# define PNG_TR_MNG_INTRAPIXEL (PNG_TR_START + 0x0100U) + /* Perform intra-pixel differencing (write) or un-differencing on read. */ +# define PNG_TR_CHECK_PALETTE (PNG_TR_START + 0x0200U) + /* Done before at the start on read, at the end on write to give a + * consistent postion: + * + * PNG_RWTR_CHECK_PALETTE PI W11: happens in pngwrite.c last + */ +# define PNG_TR_START_CACHE (PNG_TR_START + 0x0300U) + /* Not used on a transform; this is just a marker for the point at which + * palette or low-bit-depth caching can start on read. (The previous + * operations cannot be cached). + */ +# define PNG_TR_INIT_ALPHA (PNG_TR_START + 0x0400U) + /* This just handles alpha/tRNS initialization issues to resolve the + * inter-dependencies with tRNS expansion and background composition; it + * doesn't do anything itself, just sets flags and pushes transforms. + */ + /* + * Prior to 1.7 the arithmetic operations interleaved with the RGB-to-gray + * and alpha strip byte level ops. This was done to reduce the amount of + * data processed, i.e. it was an optimization not a requirement. These + * operations were preceded by the 'expand' operations, which is the + * opposite; it was done to simplify the code and actually slows things down + * in the low bit depth gray case. The full list of operations after expand, + * in the 1.6 order, is: + * + * PNG_TR_STRIP_ALPHA png_do_strip_channel (sometimes) + * PNG_TR_RGB_TO_GRAY png_do_rgb_to_gray + * PNG_TR_GRAY_TO_RGB png_do_gray_to_rgb (sometimes) + * PNG_TR_COMPOSE png_do_compose + * PNG_TR_GAMMA png_do_gamma (if no RGB_TO_GRAY) + * PNG_TR_STRIP_ALPHA png_do_strip_channel (other times) + * PNG_TR_ENCODE_ALPHA png_do_encode_alpha + * + * In 1.7 the operations are moved round somewhat, including moving alpha and + * 16-to-8 bit reduction later. This leaves the following operations: + * + * PNG_TR_RGB_TO_GRAY png_do_rgb_to_gray + * PNG_TR_COMPOSE png_do_compose + * PNG_TR_GAMMA png_do_gamma (if no RGB_TO_GRAY) + * PNG_TR_ENCODE_ALPHA png_do_encode_alpha + * + * Prior to 1.7 some combinations of transforms would do gamma correction + * twice, the actual implementation in 1.7 is to use the following order and + * rely on the cache code to optimize gray 1,2,4,8 and (of course) palette. + */ +# define PNG_TR_COMPOSE (PNG_TR_ARITHMETIC + 0x0100U) + /* Handle background composition. This may need to push a gray-to-rgb + * transform if the background is RGB for gray input. This precedes RGB + * to gray convertion so that it can handle tRNS appropriately when the + * background is in the PNG encoding however, typically, the processing + * happens at PNG_TR_COMPOSE_ALPHA below. + * + * NOTE: this must be the first arithmetic transform because the code in + * png_init_background relies on png_transform_control::gamma being the + * original PNG gamma. + */ +# define PNG_TR_RGB_TO_GRAY (PNG_TR_ARITHMETIC + 0x0200U) /* to gray */ + /* Convert any RGB input (8/16 bit depth, RGB, RGBA) to linear gray + * 16-bit. This happens first because it cannot be cached; the input data + * has 24 or 48 bits of uncorrelated data so the transform has to happen + * pixel-by-pixel. Internally the transform may maintain an 8 or 16-bit + * gamma correction table (to 16-bit linear) to speed things up. + * + * NOTE: this transform must follow PNG_TR_COMPOSE with no intervening + * transforms; see the code in png_init_background (pngrtran.c) which + * relies on this during PNG_TC_INIT_FORMAT. + */ +# define PNG_TR_COMPOSE_ALPHA (PNG_TR_ARITHMETIC + 0x0300U) + /* Compose alpha composition and tRNS handling when the background is a + * screen color. Pushed by PNG_TR_COMPOSE as required. + */ +# define PNG_TR_GAMMA_ENCODE (PNG_TR_ARITHMETIC + 0x1F00U) /* last */ + /* Gamma encode the input. This encodes the gray or RGB channels to the + * required bit depth and either scales the alpha channel or encodes it as + * well, depending on the requested alpha encoding. + */ + /* + * The 'expand' operations come after the arithmetic ones in libpng 1.7, this + * forces the arithmetic stuff to do the expand, but since arithmetic is (in + * 1.7) normally done in 16-bit linear this avoids spurious expands. + */ +# define PNG_TR_EXPAND (PNG_TR_CHANNEL + 0x0100U) + /* Includes: + * + * PNG_TR_EXPAND_PALETTE palette images only, includes tRNS + * PNG_TR_EXPAND_LBP_GRAY grayscale low-bit depth only + * PNG_TR_EXPAND_tRNS non-palette images only + */ +# define PNG_TR_SCALE_16_TO_8 (PNG_TR_CHANNEL + 0x0200U) + /* Comes after the expand and before the chop version; note that it works on + * the pixel values directly, so it is a linear transform on a non-linear + * value. + */ + /* + * To handle transforms that affect the palette entries, not the palette + * indices in the row data, libpng 1.7 reorders some of the post-quantize + * transformations to put all the "PC" transforms ahead of all the "PI" + * transforms. The "PC" transforms that came after png_do_quantize in libpng + * 1.6 cannot be ordered to be before so they are included in the + * PNG_TR_QUANTIZE section. The PI transforms are all in PNG_TR_ENCODING, + * PNG_GRAY_TO_RGB is moved before PNG_TR_QUANTIZE to avoid the unpredictable + * behavior of png_set_quantize that otherwise arises. + * + * The transforms in the PNG_TR_QUANTIZE section are: + * + * PNG_TR_EXPAND_16 !P !W + * PNG_RWTR_INVERT_MONO !P W10: invert the gray channel + * PNG_RWTR_INVERT_ALPHA PC W8: invert the alpha channel + * PNG_RWTR_SHIFT PC W6: read: down, write: scale up + * PNG_RWTR_BGR !P W9 + * PNG_RWTR_FILLER !P W2: add on R, remove on W + * PNG_RWTR_SWAP_ALPHA !P W7 + * PNG_RWTR_SWAP_16 !P W5 + * + * The ones in PNG_TR_ENCODING are: + * + * PNG_RWTR_PACK PI W4: R: unpack bytes, W: pack + * PNG_RWTR_PIXEL_SWAP PI W3: Swap pixels in a byte + * PNG_RWTR_USER PI W1 + */ + +# define PNG_TR_CHANNEL_PREQ (PNG_TR_CHANNEL + 0x1F00U) + /* The channel swap transforms that must happen before PNG_TR_QUANTIZE: + * + * PNG_TR_STRIP_ALPHA + * PNG_TR_CHOP_16_TO_8 + * PNG_TR_GRAY_TO_RGB + */ +# define PNG_TR_CHANNEL_POSTQ (PNG_TR_QUANTIZE + 0x0100U) + /* The post-quantize channel swap transforms: + * + * PNG_TR_EXPAND_16 !P !W + * PNG_RWTR_BGR !P W9 + * PNG_RWTR_FILLER !P W2: (filler) add on R, remove on W + * PNG_RWTR_SWAP_ALPHA !P W7 + * PNG_RWTR_SWAP_16 !P W5 + * + * The 'CHANNEL' operation sets the transform_control channel_add flag for + * use below. + */ +# define PNG_TR_INVERT (PNG_TR_QUANTIZE + 0x0200U) + /* Invert MONO and ALPHA. If the channel_add flag is set in the transform + * control INVERT_ALPHA will not be done; the png_add_alpha/filler APIs + * happened after png_set_invert_alpha in earlier versions so the filler + * value had to include the invert. + * + * PNG_RWTR_INVERT_MONO !P W10: invert the gray channel + * PNG_RWTR_INVERT_ALPHA PC W8: invert the alpha channel + */ +# define PNG_TR_SHIFT (PNG_TR_QUANTIZE + 0x0300U) + /* The channel shift, except that if the channel_add flag has been set the + * alpha channel is not shifted. + * + * PNG_RWTR_SHIFT PC W6: read: down, write: scale up + */ +# define PNG_TR_PACK (PNG_TR_ENCODING + 0x0200U) + /* PNG_RWTR_PACK PI W4: R: unpack bytes, W: pack */ +# define PNG_TR_PIXEL_SWAP (PNG_TR_ENCODING + 0x0300U) + /* PNG_RWTR_PIXEL_SWAP PI W3: Swap pixels in a byte */ +# define PNG_TR_USER (PNG_TR_ENCODING + 0x1F00U) + /* The user transform; must be last before the interlace handling because it + * does unpredictable things to the format. + * + * PNG_RWTR_USER PI W1 + */ + +PNG_INTERNAL_FUNCTION(png_transformp,png_push_transform,(png_structrp png_ptr, + size_t size, png_transform_fn fn, png_transformp *transform, + png_transform_controlp tc),PNG_EMPTY); + /* As png_add_transform except that the new transform is inserted ahead of + * the given transform (*transform). The new transform is returned, but it + * will also invariably be in *transform. If 'tc' is not NULL the transform + * callback will also be called; it needs to be called if this function is + * called while transforms are being run. + * + * 'fn' must not be NULL. + * + * The transform is inserted with the same 'order' as the passed in + * *transform, that transform and following transforms are moved up ('order' + * is incremented) as required to make space. Consequently, unlike with + * png_add_transform, the transform will always be new. To detect loops + * (*transform)->fn must not be the same as the passed in 'fn'. + */ + +PNG_INTERNAL_FUNCTION(png_voidp,png_transform_cast_check, + (png_const_structp png_ptr, unsigned int src_line, png_transformp tr, + size_t size),PNG_EMPTY); + /* Given a pointer to a transform, 'tr' validate that the underlying derived + * class has size 'size' using the tr->size field and return the same + * pointer. If there is a size mismatch the function does an affirm using + * the given line number. + */ +#define png_transform_cast(type, pointer) png_voidcast(type*,\ + png_transform_cast_check(png_ptr, PNG_SRC_LINE, (pointer), sizeof (type))) + /* This takes a pointer to a transform and safely returns a pointer to a + * derived transform class (type); type must not have the pointer. It + * validates the 'size' field. Derived classes start with a png_transform + * as the first member called 'tr'. + */ +#endif /* TRANSFORM_MECH_SUPPORTED */ + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +/* Remove a transform from a list, moving the next transform down into + * *transform. + */ +PNG_INTERNAL_FUNCTION(void,png_remove_transform,(png_const_structp png_ptr, + png_transformp *transform),PNG_EMPTY); + +/* Initializer for read transforms that handles caching, palette update and + * palette expansion. + */ +PNG_INTERNAL_FUNCTION(unsigned int,png_read_init_transform_mech, + (png_structp png_ptr, png_transform_control *tc),PNG_EMPTY); + /* Optional call to update the users info structure */ PNG_INTERNAL_FUNCTION(void,png_read_transform_info,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); + png_inforp info_ptr),PNG_EMPTY); #endif +/* APIs which do a tranform on both read and write but where the implementation + * is separate for each; the read and write init functions are in pngrtran.c or + * pngwtran.c, the API is in pngtrans.c + */ +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_READ_EXPAND_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_init_read_pack,(png_transformp *transform, + png_transform_controlp tc),PNG_EMPTY); +#endif /* READ_PACK || READ_EXPAND */ +#ifdef PNG_WRITE_PACK_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_init_write_pack,(png_transformp *transform, + png_transform_controlp tc),PNG_EMPTY); +#endif /* WRITE_PACK */ + /* Shared transform functions, defined in pngtran.c */ #if defined(PNG_WRITE_FILLER_SUPPORTED) || \ defined(PNG_READ_STRIP_ALPHA_SUPPORTED) -PNG_INTERNAL_FUNCTION(void,png_do_strip_channel,(png_row_infop row_info, - png_bytep row, int at_start),PNG_EMPTY); -#endif - -#ifdef PNG_16BIT_SUPPORTED -#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) -PNG_INTERNAL_FUNCTION(void,png_do_swap,(png_row_infop row_info, - png_bytep row),PNG_EMPTY); -#endif -#endif - -#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ - defined(PNG_WRITE_PACKSWAP_SUPPORTED) -PNG_INTERNAL_FUNCTION(void,png_do_packswap,(png_row_infop row_info, - png_bytep row),PNG_EMPTY); -#endif +PNG_INTERNAL_FUNCTION(void,png_do_strip_channel,( + png_transform_controlp row_info, png_bytep row, int at_start),PNG_EMPTY); +#endif /* FILLER */ #if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) -PNG_INTERNAL_FUNCTION(void,png_do_invert,(png_row_infop row_info, +PNG_INTERNAL_FUNCTION(void,png_do_invert,(png_transform_controlp row_info, png_bytep row),PNG_EMPTY); -#endif +#endif /* INVERT */ -#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) -PNG_INTERNAL_FUNCTION(void,png_do_bgr,(png_row_infop row_info, +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) ||\ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_do_invert_alpha,(png_transform_controlp row_info, png_bytep row),PNG_EMPTY); -#endif +#endif /* INVERT_ALPHA */ + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_add_rgb_to_gray_byte_ops,(png_structrp png_ptr, + png_transform_controlp tc, unsigned int index, unsigned int order), + PNG_EMPTY); + /* This is an init-time utility to add appropriate byte ops to select a given + * channel from R/G/B. + */ +#endif /* READ_RGB_TO_GRAY */ + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) &&\ + defined(PNG_READ_BACKGROUND_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_push_gray_to_rgb_byte_ops,(png_transformp *tr, + png_transform_controlp tc), PNG_EMPTY); + /* This is an init-time utility to push appropriate byte ops to expand a + * grayscale PNG data set to RGB. It calls the function callback so 'tc' + * must be non-NULL. + */ +#endif /* GRAY_TO_RGB && READ_BACKGROUND */ + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_add_strip_alpha_byte_ops,(png_structrp png_ptr), + PNG_EMPTY); + /* Called from pngrtran.c to add the relevant byte op. */ +#endif /* READ_STRIP_ALPHA */ /* The following decodes the appropriate chunks, and does error correction, * then calls the appropriate callback for the chunk if it is valid. */ -/* Decode the IHDR chunk */ -PNG_INTERNAL_FUNCTION(void,png_handle_IHDR,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_handle_PLTE,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_handle_IEND,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#ifdef PNG_READ_SUPPORTED +PNG_INTERNAL_FUNCTION(png_bytep,png_read_buffer,(png_structrp png_ptr, + png_alloc_size_t new_size, int warn),PNG_EMPTY); + /* Manage the dynamically allocated read buffer */ -#ifdef PNG_READ_bKGD_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_bKGD,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +/* Shared READ IDAT handling: */ +PNG_INTERNAL_FUNCTION(void,png_read_start_IDAT,(png_structrp png_ptr), + PNG_EMPTY); + /* Initialize the row buffers, etc. */ + +typedef enum +{ + png_row_incomplete, + /* more IDAT data needed for row */ + png_row_process, + /* png_struct::row_buffer contains a complete row */ + png_row_repeat, + /* row not in this pass, but the existing row may be used */ + png_row_skip + /* row not in pass and no appropriate data; skip this row */ +} png_row_op; +PNG_INTERNAL_FUNCTION(png_row_op,png_read_process_IDAT,(png_structrp png_ptr, + png_bytep transformed_row, png_bytep display_row, int save_row), + PNG_EMPTY); + /* Process a block of IDAT data; the routine returns early if it has + * obtained a row. It is valid to call this routine with no input data; + * it will return png_row_incomplete if it needs input. + * + * transformed_row: The transformed pixels of the input are written here. + * For interlaced images only the pixels in the pass will + * be written, the other pixels will not be touched. + * + * display_row: The transformed pixels but replicated to that the entire + * buffer will have been initialized. For passes after the + * first the pixels written are determined by the 'block' + * algorithm; only those *following* pixels which are + * written by *later* passes are written (with a copy of the + * pixel from the pass.) + * + * save_row: A boolean which indicates that the row (unexpanded) + * should be saved in png_struct::transformed_row. This can + * be used in a later call to png_combine_row. + * + * During reading the row is built up until png_row_process is returned. At + * this point png_struct::row_buffer contains the original PNG row from the + * file and, if save_row was set, png_struct::transformed_row contains the + * row after the selected row transforms have been performed. For interlaced + * images both are the width of the interlace pass. + * + * When png_row_repeat is returned the same is true, except that the buffers + * still contain the contents of the preceding row (the one where this + * funciton returned png_row_pricess). + * + * The row buffers should not be accessed if png_row_skip is returned; this + * row is not modified in the current pass. + */ + +PNG_INTERNAL_FUNCTION(void,png_read_free_row_buffers,(png_structrp png_ptr), + PNG_EMPTY); + /* Free allocated row buffers; done as soon as possible to avoid carrying + * around all the memory for longer than necessary. + */ + +PNG_INTERNAL_FUNCTION(int,png_read_finish_IDAT,(png_structrp png_ptr), + PNG_EMPTY); + /* Complete reading of the IDAT chunks. This returns 0 if more data is to + * be read, 1 if the zlib stream has terminated. Call this routine with + * zstream.avail_in greater than zero unless there is no more input data. + * When zstream_avail_in is 0 on entry and the stream does not terminate + * an "IDAT truncated" error will be output. + * + * ENTRY: png_ptr->zstream.{next,avail}_in points to more IDAT data, if + * available, otherwise avail_in should be 0. + * RET 0: the LZ stream is still active, more IDAT date is required, if + * available, the routine *must* be called again. + * RET 1: the LZ stream has been closed and an error may have been output; + * png_ptr->zstream_error says whether it has. If not and there + * is more IDAT data available the caller should output an + * appropriate (too much IDAT) error message. + */ + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_cache_known_unknown,(png_structrp png_ptr, + png_const_bytep add, int keep),PNG_EMPTY); + /* Update the png_struct::known_unknown bit cache which stores whether each + * known chunk should be treated as unknown. + */ #endif -#ifdef PNG_READ_cHRM_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_cHRM,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif +typedef enum +{ + png_chunk_skip = 0, /* Skip this chunk */ + png_chunk_unknown, /* Pass the chunk to png_handle_unknown */ + png_chunk_process_all, /* Process the chunk all at once */ + png_chunk_process_part /* Process the chunk in parts (for IDAT) */ +} png_chunk_op; -#ifdef PNG_READ_gAMA_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_gAMA,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif +PNG_INTERNAL_FUNCTION(png_chunk_op,png_find_chunk_op,(png_structrp png_ptr), + PNG_EMPTY); + /* Given a chunk in png_struct::{chunk_name,chunk_length} validate the name + * and work out how it should be handled. This function checks the chunk + * location using png_struct::mode and will set the mode appropriately for + * the known critical chunks but otherwise makes no changes to the stream + * read state. + */ -#ifdef PNG_READ_hIST_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_hIST,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif +PNG_INTERNAL_FUNCTION(void,png_check_chunk_name,(png_const_structrp png_ptr, + const png_uint_32 chunk_name),PNG_EMPTY); -#ifdef PNG_READ_iCCP_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_iCCP,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif /* PNG_READ_iCCP_SUPPORTED */ - -#ifdef PNG_READ_iTXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_iTXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_oFFs_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_oFFs,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_pCAL_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_pCAL,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_pHYs_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_pHYs,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_sBIT_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_sBIT,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_sCAL_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_sCAL,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_sPLT_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_sPLT,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif /* PNG_READ_sPLT_SUPPORTED */ - -#ifdef PNG_READ_sRGB_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_sRGB,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_tEXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_tEXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_tIME_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_tIME,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_tRNS_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_tRNS,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_zTXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_zTXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -PNG_INTERNAL_FUNCTION(void,png_check_chunk_name,(png_structrp png_ptr, - png_uint_32 chunk_name),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_check_chunk_length,(png_const_structrp png_ptr, + const png_uint_32 chunk_length),PNG_EMPTY); +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED PNG_INTERNAL_FUNCTION(void,png_handle_unknown,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length, int keep),PNG_EMPTY); - /* This is the function that gets called for unknown chunks. The 'keep' - * argument is either non-zero for a known chunk that has been set to be - * handled as unknown or zero for an unknown chunk. By default the function - * just skips the chunk or errors out if it is critical. + png_inforp info_ptr, png_bytep chunk_data),PNG_EMPTY); + /* Handle an unknown chunk that needs to be processed. It is only valid + * to call this after png_find_chunk_op returns png_chunk_unknown. The + * data argument points to the png_struct::chunk_length bytes of the chunk + * data. */ +#endif /* READ_UNKNOWN_CHUNKS */ -#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) ||\ - defined(PNG_HANDLE_AS_UNKNOWN_SUPPORTED) -PNG_INTERNAL_FUNCTION(int,png_chunk_unknown_handling, - (png_const_structrp png_ptr, png_uint_32 chunk_name),PNG_EMPTY); - /* Exactly as the API png_handle_as_unknown() except that the argument is a - * 32-bit chunk name, not a string. +PNG_INTERNAL_FUNCTION(void,png_handle_chunk,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); + /* The chunk to handle is in png_struct::chunk_name,chunk_length. + * + * NOTE: at present it is only valid to call this after png_find_chunk_op + * has returned png_chunk_process_all and all the data is available for + * png_handle_chunk (via the libpng read callback.) */ -#endif /* READ_UNKNOWN_CHUNKS || HANDLE_AS_UNKNOWN */ +#endif /* READ */ -/* Handle the transformations for reading and writing */ -#ifdef PNG_READ_TRANSFORMS_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_do_read_transformations,(png_structrp png_ptr, - png_row_infop row_info),PNG_EMPTY); -#endif -#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_do_write_transformations,(png_structrp png_ptr, - png_row_infop row_info),PNG_EMPTY); -#endif - -#ifdef PNG_READ_TRANSFORMS_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_init_read_transformations,(png_structrp png_ptr), - PNG_EMPTY); -#endif - -#ifdef PNG_PROGRESSIVE_READ_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_push_read_chunk,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_read_sig,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_check_crc,(png_structrp png_ptr),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_crc_skip,(png_structrp png_ptr, - png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_crc_finish,(png_structrp png_ptr), - PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_save_buffer,(png_structrp png_ptr), - PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_restore_buffer,(png_structrp png_ptr, - png_bytep buffer, png_size_t buffer_length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_read_IDAT,(png_structrp png_ptr),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_process_IDAT_data,(png_structrp png_ptr, - png_bytep buffer, png_size_t buffer_length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_process_row,(png_structrp png_ptr), - PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_handle_unknown,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_have_info,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_have_end,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_have_row,(png_structrp png_ptr, - png_bytep row),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_read_end,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_process_some_data,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_read_push_finish_row,(png_structrp png_ptr), - PNG_EMPTY); -# ifdef PNG_READ_tEXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_push_handle_tEXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_read_tEXt,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -# endif -# ifdef PNG_READ_zTXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_push_handle_zTXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_read_zTXt,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -# endif -# ifdef PNG_READ_iTXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_push_handle_iTXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_read_iTXt,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -# endif - -#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ +PNG_INTERNAL_FUNCTION(void,png_init_row_info,(png_structrp png_ptr),PNG_EMPTY); + /* Set the png_struct::row_ members from the PNG file information, running + * transforms if required. + */ /* Added at libpng version 1.6.0 */ #ifdef PNG_GAMMA_SUPPORTED @@ -1375,13 +1779,13 @@ PNG_INTERNAL_FUNCTION(void,png_colorspace_set_gamma,(png_const_structrp png_ptr, PNG_INTERNAL_FUNCTION(void,png_colorspace_sync_info,(png_const_structrp png_ptr, png_inforp info_ptr), PNG_EMPTY); - /* Synchronize the info 'valid' flags with the colorspace */ + /* Synchronize the info 'valid' flags with the colorspace */ PNG_INTERNAL_FUNCTION(void,png_colorspace_sync,(png_const_structrp png_ptr, png_inforp info_ptr), PNG_EMPTY); - /* Copy the png_struct colorspace to the info_struct and call the above to - * synchronize the flags. Checks for NULL info_ptr and does nothing. - */ + /* Copy the png_struct colorspace to the info_struct and call the above to + * synchronize the flags. Checks for NULL info_ptr and does nothing. + */ #endif /* Added at libpng version 1.4.0 */ @@ -1410,7 +1814,7 @@ PNG_INTERNAL_FUNCTION(int,png_colorspace_set_sRGB,(png_const_structrp png_ptr, #ifdef PNG_iCCP_SUPPORTED PNG_INTERNAL_FUNCTION(int,png_colorspace_set_ICC,(png_const_structrp png_ptr, png_colorspacerp colorspace, png_const_charp name, - png_uint_32 profile_length, png_const_bytep profile, int color_type), + png_uint_32 profile_length, png_const_bytep profile, int is_color), PNG_EMPTY); /* The 'name' is used for information only */ @@ -1421,7 +1825,7 @@ PNG_INTERNAL_FUNCTION(int,png_icc_check_length,(png_const_structrp png_ptr, PNG_INTERNAL_FUNCTION(int,png_icc_check_header,(png_const_structrp png_ptr, png_colorspacerp colorspace, png_const_charp name, png_uint_32 profile_length, - png_const_bytep profile /* first 132 bytes only */, int color_type), + png_const_bytep profile /* first 132 bytes only */, int is_color), PNG_EMPTY); PNG_INTERNAL_FUNCTION(int,png_icc_check_tag_table,(png_const_structrp png_ptr, png_colorspacerp colorspace, png_const_charp name, @@ -1435,14 +1839,8 @@ PNG_INTERNAL_FUNCTION(void,png_icc_set_sRGB,( * be zero to indicate that it is not available. It is used, if provided, * as a fast check on the profile when checking to see if it is sRGB. */ -#endif +#endif /* sRGB */ #endif /* iCCP */ - -#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_colorspace_set_rgb_coefficients, - (png_structrp png_ptr), PNG_EMPTY); - /* Set the rgb_to_gray coefficients from the colorspace Y values */ -#endif /* READ_RGB_TO_GRAY */ #endif /* COLORSPACE */ /* Added at libpng version 1.4.0 */ @@ -1451,13 +1849,6 @@ PNG_INTERNAL_FUNCTION(void,png_check_IHDR,(png_const_structrp png_ptr, int color_type, int interlace_type, int compression_type, int filter_type),PNG_EMPTY); -/* Added at libpng version 1.5.10 */ -#if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \ - defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED) -PNG_INTERNAL_FUNCTION(void,png_do_check_palette_indexes, - (png_structrp png_ptr, png_row_infop row_info),PNG_EMPTY); -#endif - #if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED) PNG_INTERNAL_FUNCTION(void,png_fixed_error,(png_const_structrp png_ptr, png_const_charp name),PNG_NORETURN); @@ -1575,11 +1966,19 @@ PNG_INTERNAL_FUNCTION(void,png_chunk_report,(png_const_structrp png_ptr, * data set via an application call to a png_set_ API and png_app_error or * png_app_warning is used as appropriate. * + * With PNG_CHUNK_FATAL an error can be marked as unrecoverable, and the + * function will not return. + * * The 'error' parameter must have one of the following values: */ #define PNG_CHUNK_WARNING 0 /* never an error */ #define PNG_CHUNK_WRITE_ERROR 1 /* an error only on write */ #define PNG_CHUNK_ERROR 2 /* always an error */ +#define PNG_CHUNK_FATAL 3 /* an unrecoverable error */ + +#ifndef PNG_ERROR_TEXT_SUPPORTED +# define png_chunk_report(pp,e,v) png_chunk_report(pp,NULL,v) +#endif /* ASCII to FP interfaces, currently only implemented if sCAL * support is required. @@ -1715,56 +2114,51 @@ PNG_INTERNAL_FUNCTION(int,png_check_fp_string,(png_const_charp string, */ PNG_INTERNAL_FUNCTION(int,png_muldiv,(png_fixed_point_p res, png_fixed_point a, png_int_32 multiplied_by, png_int_32 divided_by),PNG_EMPTY); -#endif - -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED) -/* Same deal, but issue a warning on overflow and return 0. */ -PNG_INTERNAL_FUNCTION(png_fixed_point,png_muldiv_warn, - (png_const_structrp png_ptr, png_fixed_point a, png_int_32 multiplied_by, - png_int_32 divided_by),PNG_EMPTY); -#endif - -#ifdef PNG_GAMMA_SUPPORTED -/* Calculate a reciprocal - used for gamma values. This returns - * 0 if the argument is 0 in order to maintain an undefined value; - * there are no warnings. - */ -PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal,(png_fixed_point a), - PNG_EMPTY); - -#ifdef PNG_READ_GAMMA_SUPPORTED -/* The same but gives a reciprocal of the product of two fixed point - * values. Accuracy is suitable for gamma calculations but this is - * not exact - use png_muldiv for that. Only required at present on read. - */ -PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal2,(png_fixed_point a, - png_fixed_point b),PNG_EMPTY); -#endif - -/* Return true if the gamma value is significantly different from 1.0 */ -PNG_INTERNAL_FUNCTION(int,png_gamma_significant,(png_fixed_point gamma_value), - PNG_EMPTY); -#endif +#endif /* GAMMA || INCH_CONVERSIONS || READ_pHYs */ #ifdef PNG_READ_GAMMA_SUPPORTED /* Internal fixed point gamma correction. These APIs are called as * required to convert single values - they don't need to be fast, * they are not used when processing image pixel values. - * - * While the input is an 'unsigned' value it must actually be the - * correct bit value - 0..255 or 0..65535 as required. */ -PNG_INTERNAL_FUNCTION(png_uint_16,png_gamma_correct,(png_structrp png_ptr, - unsigned int value, png_fixed_point gamma_value),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(png_uint_16,png_gamma_16bit_correct,(unsigned int value, - png_fixed_point gamma_value),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(png_byte,png_gamma_8bit_correct,(unsigned int value, - png_fixed_point gamma_value),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_destroy_gamma_table,(png_structrp png_ptr), - PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_build_gamma_tables,(png_structrp png_ptr, - int bit_depth),PNG_EMPTY); -#endif +PNG_INTERNAL_FUNCTION(unsigned int,png_gamma_nxmbit_correct, + (unsigned int value, png_fixed_point gamma_val, unsigned int n/*input bits*/, + unsigned int m/*output bits */),PNG_EMPTY); + /* In this case the value must have 'n' bits and the output will have 'm' + * bits. + */ + +#if !PNG_RELEASE_BUILD +PNG_INTERNAL_FUNCTION(int,png_gamma_check,(png_const_structrp png_ptr, + png_const_transform_controlp tc),PNG_EMPTY); + /* Debugging only routine to repeat the test used above to determine if the + * gamma was insignificant. + */ +#endif /* !RELEASE_BUILD */ +#endif /* READ_GAMMA */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* Internal check function to saw if the gamma of the PNG data is far enough + * from the given screen gamma to require gamma correction (only needed for a + * bug work-round in the simplified API). + * TODO: it should be possible to remove the bug work-round in 1.7 + */ +PNG_INTERNAL_FUNCTION(int,png_need_gamma_correction,(png_const_structrp png_ptr, + png_fixed_point gamma, int sRGB_output),PNG_EMPTY); +#endif /* SIMPLIFIED_READ */ + +/* This is a utility macro to say whether a gamma value is close enough to sRGB. + * The test is now hardwired: + * + * API CHANGE: prior to 1.7 this would depend on the build-time + * PNG_GAMMA_THRESHOLD_FIXED setting, which would cause inconsistent results + * when the setting was changed. Since this setting can now be changed at + * run-time it seems more sensible to have a single fixed definition of 'sRGB'. + * + * The test is approximately +/- 1%, it allows any decimal value from 0.45 (the + * two digit rounded version of 1/2.2) to just under 0.46). + */ +#define PNG_GAMMA_IS_sRGB(g) ((g) >= 45000 && (g) < 46000) /* SIMPLIFIED READ/WRITE SUPPORT */ #if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\ @@ -1815,6 +2209,14 @@ PNG_INTERNAL_FUNCTION(int,png_safe_execute,(png_imagep image, PNG_INTERNAL_FUNCTION(int,png_image_error,(png_imagep image, png_const_charp error_message),PNG_EMPTY); +/* Safely initialize a stdio pointer - used by both the read and the write + * code. + */ +#ifdef PNG_STDIO_SUPPORTED +PNG_INTERNAL_FUNCTION(int,png_image_init_io,(png_imagep image, png_FILE_p file), + PNG_EMPTY); +#endif /* STDIO */ + #ifndef PNG_SIMPLIFIED_READ_SUPPORTED /* png_image_free is used by the write code but not exported */ PNG_INTERNAL_FUNCTION(void, png_image_free, (png_imagep image), PNG_EMPTY); @@ -1822,6 +2224,20 @@ PNG_INTERNAL_FUNCTION(void, png_image_free, (png_imagep image), PNG_EMPTY); #endif /* SIMPLIFIED READ/WRITE */ +#ifdef PNG_READ_SUPPORTED +PNG_INTERNAL_FUNCTION(png_int_32, png_read_setting, (png_structrp png_ptr, + png_uint_32 setting, png_uint_32 parameter, png_int_32 value), PNG_EMPTY); +#endif /* READ */ +#ifdef PNG_WRITE_SUPPORTED +PNG_INTERNAL_FUNCTION(png_int_32, png_write_setting, (png_structrp png_ptr, + png_uint_32 setting, png_uint_32 parameter, png_int_32 value), PNG_EMPTY); + /* Implementations of read and write settings, in pngrutil.c and pngwutil.c + * respectively. + */ +#endif /* WRITE */ + +/* Maintainer: Put new private prototypes here ^ */ + /* These are initialization functions for hardware specific PNG filter * optimizations; list these here then select the appropriate one at compile * time using the macro PNG_FILTER_OPTIMIZATIONS. If the macro is not defined @@ -1840,10 +2256,11 @@ PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_neon, (png_structp png_ptr, unsigned int bpp), PNG_EMPTY); #endif -/* Maintainer: Put new private prototypes here ^ */ - #include "pngdebug.h" +/* EXTENSION SPECIFIC FUNCTIONS */ +#include "arm/neon.h" + #ifdef __cplusplus } #endif diff --git a/ext/libpng17/pngread.c b/ext/libpng17/pngread.c index cec1144451..96a5a1b4b4 100644 --- a/ext/libpng17/pngread.c +++ b/ext/libpng17/pngread.c @@ -2,7 +2,7 @@ /* pngread.c - read a PNG file * * Last changed in libpng 1.7.0 [(PENDING RELEASE)] - * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -14,18 +14,13 @@ * read a PNG file or stream. */ -#ifdef _MSC_VER -#pragma warning (disable:4996) -#pragma warning (disable:4244) -#endif - #include "pngpriv.h" #if defined(PNG_SIMPLIFIED_READ_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) # include #endif +#define PNG_SRC_FILE PNG_SRC_FILE_pngread #ifdef PNG_READ_SUPPORTED - /* Create a PNG structure for reading, and allocate any memory needed. */ PNG_FUNCTION(png_structp,PNGAPI png_create_read_struct,(png_const_charp user_png_ver, png_voidp error_ptr, @@ -33,10 +28,10 @@ png_create_read_struct,(png_const_charp user_png_ver, png_voidp error_ptr, { #ifndef PNG_USER_MEM_SUPPORTED png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr, - error_fn, warn_fn, NULL, NULL, NULL); + error_fn, warn_fn, NULL, NULL, NULL); #else return png_create_read_struct_2(user_png_ver, error_ptr, error_fn, - warn_fn, NULL, NULL, NULL); + warn_fn, NULL, NULL, NULL); } /* Alternate create PNG structure for reading, and allocate any memory @@ -48,36 +43,51 @@ png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED) { png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr, - error_fn, warn_fn, mem_ptr, malloc_fn, free_fn); -#endif /* PNG_USER_MEM_SUPPORTED */ + error_fn, warn_fn, mem_ptr, malloc_fn, free_fn); +#endif /* USER_MEM */ if (png_ptr != NULL) { - png_ptr->mode = PNG_IS_READ_STRUCT; + png_ptr->read_struct = 1; + png_ptr->critical_crc = crc_error_quit; + png_ptr->ancillary_crc = crc_warn_discard; - /* Added in libpng-1.6.0; this can be used to detect a read structure if - * required (it will be zero in a write structure.) - */ -# ifdef PNG_SEQUENTIAL_READ_SUPPORTED - png_ptr->IDAT_read_size = PNG_IDAT_READ_SIZE; -# endif +# ifdef PNG_BENIGN_ERRORS_SUPPORTED +# if !PNG_RELEASE_BUILD + /* Always quit on error prior to release */ + png_ptr->benign_error_action = PNG_ERROR; + png_ptr->app_warning_action = PNG_WARN; + png_ptr->app_error_action = PNG_ERROR; +# else /* RELEASE_BUILD */ + /* Allow benign errors on read, subject to app control. */ + png_ptr->benign_error_action = PNG_WARN; +# ifdef PNG_BENIGN_READ_ERRORS_SUPPORTED + png_ptr->app_error_action = PNG_WARN; + png_ptr->app_warning_action = PNG_WARN; +# else /* !BENIGN_READ_ERRORS */ + /* libpng build without benign error support; the application + * author has to be assumed to be correct, so: + */ + png_ptr->app_warning_action = PNG_WARN; + png_ptr->app_error_action = PNG_ERROR; +# endif /* !BENIGN_READ_ERRORS */ +# endif /* RELEASE_BUILD */ -# ifdef PNG_BENIGN_READ_ERRORS_SUPPORTED - png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN; + /* This is always png_error unless explicitly changed: */ + png_ptr->IDAT_error_action = PNG_ERROR; +# endif /* BENIGN_ERRORS */ - /* In stable builds only warn if an application error can be completely - * handled. - */ -# if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC - png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN; -# endif -# endif +# ifdef PNG_SEQUENTIAL_READ_SUPPORTED + png_ptr->IDAT_size = PNG_IDAT_READ_SIZE; +# endif /* SEQUENTIAL_READ */ - /* TODO: delay this, it can be done in png_init_io (if the app doesn't - * do it itself) avoiding setting the default function if it is not - * required. - */ - png_set_read_fn(png_ptr, NULL, NULL); +# ifdef PNG_READ_GAMMA_SUPPORTED + /* Default gamma correction values: */ +# if 0 /*NYI*/ + png_ptr->gamma_accuracy = PNG_DEFAULT_GAMMA_ACCURACY; +# endif /*NYI*/ + png_ptr->gamma_threshold = PNG_GAMMA_THRESHOLD_FIXED; +# endif /* READ_GAMMA */ } return png_ptr; @@ -85,6 +95,64 @@ png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr, #ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the chunk header (length + type name). + * Put the type name into png_ptr->chunk_name, and return the length. + */ +static void +png_read_chunk_header(png_structrp png_ptr) +{ + png_byte buf[8]; + +#ifdef PNG_IO_STATE_SUPPORTED + png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_HDR; +#endif + + /* Read the length and the chunk name. + * This must be performed in a single I/O call. + */ + png_read_data(png_ptr, buf, 8); + + /* Put the chunk name into png_ptr->chunk_name. */ + png_ptr->chunk_length = png_get_uint_31(png_ptr, buf); + png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(buf+4); + + png_debug2(0, "Reading %lx chunk, length = %lu", + (unsigned long)png_ptr->chunk_name, + (unsigned long)png_ptr->chunk_length); + + /* Reset the crc and run it over the chunk name. */ + png_reset_crc(png_ptr, buf + 4); + +#ifdef PNG_IO_STATE_SUPPORTED + png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_DATA; +#endif +} + +static void +png_read_sequential_unknown(png_structrp png_ptr, png_inforp info_ptr) +{ +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED + /* Read the data for an unknown chunk. The read buffer is used: */ + png_bytep buffer = png_read_buffer(png_ptr, png_ptr->chunk_length, + PNG_CHUNK_ANCILLARY(png_ptr->chunk_name)); /* error if critical */ + + if (buffer != NULL) + { + if (png_ptr->chunk_length > 0U) + png_crc_read(png_ptr, buffer, png_ptr->chunk_length); + + png_crc_finish(png_ptr, 0); + png_handle_unknown(png_ptr, info_ptr, buffer); + } + + else /* out of memory on an ancillary chunk; skip the chunk */ +#else /* !READ_UNKNOWN_CHUNKS */ + /* or, no support for reading unknown chunks, so just skip it. */ + PNG_UNUSED(info_ptr) +#endif /* !READ_UNKNOWN_CHUNKS */ + png_crc_finish(png_ptr, png_ptr->chunk_length); +} + /* Read the information before the actual image data. This has been * changed in v0.90 to allow reading a file that already has the magic * bytes read from the stream. You can tell libpng how many bytes have @@ -96,196 +164,65 @@ png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr, void PNGAPI png_read_info(png_structrp png_ptr, png_inforp info_ptr) { -#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED - int keep; -#endif - png_debug(1, "in png_read_info"); if (png_ptr == NULL || info_ptr == NULL) return; - /* Read and check the PNG file signature. */ + /* Read and check the PNG file signature (this may do nothing if it has + * already been read.) + */ png_read_sig(png_ptr, info_ptr); - for (;;) + /* Loop reading chunks until an IDAT is encountered or we reach the end of + * the stream (IEND). + * + * Prior to 1.7.0 this function behaved very weirdly if called after the + * IDATs had been read; it would keep on reading chunks util it found + * another IDAT. This could cause it to read beyond IEND, damaging the + * state in the host stream. This is now caught by the check below. + */ + while ((png_ptr->mode & (PNG_HAVE_IEND|PNG_HAVE_IDAT)) == 0) { - png_uint_32 length = png_read_chunk_header(png_ptr); - png_uint_32 chunk_name = png_ptr->chunk_name; - - /* IDAT logic needs to happen here to simplify getting the two flags - * right. - */ - if (chunk_name == png_IDAT) + png_read_chunk_header(png_ptr); + switch (png_find_chunk_op(png_ptr)) { - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "Missing IHDR before IDAT"); - - else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && - !(png_ptr->mode & PNG_HAVE_PLTE)) - png_chunk_error(png_ptr, "Missing PLTE before IDAT"); - - else if (png_ptr->mode & PNG_AFTER_IDAT) - png_chunk_benign_error(png_ptr, "Too many IDATs found[s]"); - - png_ptr->mode |= PNG_HAVE_IDAT; - } - - else if (png_ptr->mode & PNG_HAVE_IDAT) - png_ptr->mode |= PNG_AFTER_IDAT; - - /* This should be a binary subdivision search or a hash for - * matching the chunk name rather than a linear search. - */ - if (chunk_name == png_IHDR) - png_handle_IHDR(png_ptr, info_ptr, length); - - else if (chunk_name == png_IEND) - png_handle_IEND(png_ptr, info_ptr, length); - -#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED - else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0) - { - png_handle_unknown(png_ptr, info_ptr, length, keep); - - if (chunk_name == png_PLTE) - png_ptr->mode |= PNG_HAVE_PLTE; - - else if (chunk_name == png_IDAT) - { - png_ptr->idat_size = 0; /* It has been consumed */ + default: + impossible("invalid chunk op"); + /* FALL THROUGH */ + case png_chunk_skip: + png_crc_finish(png_ptr, png_ptr->chunk_length); break; - } + + case png_chunk_unknown: + png_read_sequential_unknown(png_ptr, info_ptr); + break; + + case png_chunk_process_all: + png_handle_chunk(png_ptr, info_ptr); + break; + + case png_chunk_process_part: + debug(png_ptr->mode & PNG_HAVE_IDAT); + return; } -#endif - else if (chunk_name == png_PLTE) - png_handle_PLTE(png_ptr, info_ptr, length); - - else if (chunk_name == png_IDAT) - { - png_ptr->idat_size = length; - break; - } - -#ifdef PNG_READ_bKGD_SUPPORTED - else if (chunk_name == png_bKGD) - png_handle_bKGD(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_cHRM_SUPPORTED - else if (chunk_name == png_cHRM) - png_handle_cHRM(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_gAMA_SUPPORTED - else if (chunk_name == png_gAMA) - png_handle_gAMA(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_hIST_SUPPORTED - else if (chunk_name == png_hIST) - png_handle_hIST(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_oFFs_SUPPORTED - else if (chunk_name == png_oFFs) - png_handle_oFFs(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_pCAL_SUPPORTED - else if (chunk_name == png_pCAL) - png_handle_pCAL(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sCAL_SUPPORTED - else if (chunk_name == png_sCAL) - png_handle_sCAL(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_pHYs_SUPPORTED - else if (chunk_name == png_pHYs) - png_handle_pHYs(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sBIT_SUPPORTED - else if (chunk_name == png_sBIT) - png_handle_sBIT(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sRGB_SUPPORTED - else if (chunk_name == png_sRGB) - png_handle_sRGB(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_iCCP_SUPPORTED - else if (chunk_name == png_iCCP) - png_handle_iCCP(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sPLT_SUPPORTED - else if (chunk_name == png_sPLT) - png_handle_sPLT(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tEXt_SUPPORTED - else if (chunk_name == png_tEXt) - png_handle_tEXt(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tIME_SUPPORTED - else if (chunk_name == png_tIME) - png_handle_tIME(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tRNS_SUPPORTED - else if (chunk_name == png_tRNS) - png_handle_tRNS(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_zTXt_SUPPORTED - else if (chunk_name == png_zTXt) - png_handle_zTXt(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_iTXt_SUPPORTED - else if (chunk_name == png_iTXt) - png_handle_iTXt(png_ptr, info_ptr, length); -#endif - - else - png_handle_unknown(png_ptr, info_ptr, length, - PNG_HANDLE_CHUNK_AS_DEFAULT); } -} -#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ -/* Optional call to update the users info_ptr structure */ -void PNGAPI -png_read_update_info(png_structrp png_ptr, png_inforp info_ptr) -{ - png_debug(1, "in png_read_update_info"); + /* The loop was ended by IDAT or IEND, but if an IEND was seen the read code + * (png_handle_position in pngrutil.c) should have errored out, therefore: + */ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + affirm(png_ptr->chunk_name == png_IDAT && ((png_ptr->known_unknown)&1U)); +#else + debug(png_ptr->chunk_name == png_IDAT); + impossible("unknown IDAT"); +#endif - if (png_ptr != NULL) - { - if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0) - { - png_read_start_row(png_ptr); - -# ifdef PNG_READ_TRANSFORMS_SUPPORTED - png_read_transform_info(png_ptr, info_ptr); -# else - PNG_UNUSED(info_ptr) -# endif - } - - /* New in 1.6.0 this avoids the bug of doing the initializations twice */ - else - png_app_error(png_ptr, - "png_read_update_info/png_start_read_image: duplicate call"); - } + /* And the code cannot have left it unread; it must have called one of the + * handlers, so we are skipping IDAT. + */ } -#ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Initialize palette, background, etc, after transformations * are set, but before any reading takes place. This allows * the user to obtain a gamma-corrected palette, for example. @@ -298,318 +235,175 @@ png_start_read_image(png_structrp png_ptr) if (png_ptr != NULL) { - if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0) - png_read_start_row(png_ptr); + if (png_ptr->zowner != png_IDAT) + png_read_start_IDAT(png_ptr); - /* New in 1.6.0 this avoids the bug of doing the initializations twice */ + /* New in 1.6.0 this avoids the bug of doing the initializations twice, + * it could be a warning but in practice it indicates that the app may + * have made png_get_ calls on png_ptr assuming that it hadn't been + * 'started'. + */ else png_app_error(png_ptr, - "png_start_read_image/png_read_update_info: duplicate call"); + "png_start_read_image/png_read_update_info: duplicate call"); } } -#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ -#ifdef PNG_SEQUENTIAL_READ_SUPPORTED -#ifdef PNG_MNG_FEATURES_SUPPORTED -/* Undoes intrapixel differencing, - * NOTE: this is apparently only supported in the 'sequential' reader. - */ static void -png_do_read_intrapixel(png_row_infop row_info, png_bytep row) +png_read_IDAT(png_structrp png_ptr) { - png_debug(1, "in png_do_read_intrapixel"); + /* Read more input data, up to png_struct::IDAT_size, stop at the end of the + * IDAT stream. pngset.c checks png_struct::IDAT_size to ensure that it will + * fit in a uInt. + */ + const uInt buffer_size = (uInt)/*SAFE*/png_ptr->IDAT_size; + uInt IDAT_size = 0; + png_bytep buffer = + png_read_buffer(png_ptr, buffer_size, 0/*error*/); - if ( - (row_info->color_type & PNG_COLOR_MASK_COLOR)) + png_ptr->zstream.next_in = buffer; + + while (png_ptr->chunk_name == png_IDAT && IDAT_size < buffer_size) { - int bytes_per_pixel; - png_uint_32 row_width = row_info->width; + png_uint_32 l = png_ptr->chunk_length; - if (row_info->bit_depth == 8) + while (l == 0) /* end of this IDAT */ { - png_bytep rp; - png_uint_32 i; + png_crc_finish(png_ptr, 0); + png_read_chunk_header(png_ptr); - if (row_info->color_type == PNG_COLOR_TYPE_RGB) - bytes_per_pixel = 3; - - else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - bytes_per_pixel = 4; - - else - return; - - for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + if (png_ptr->chunk_name != png_IDAT) /* end of all IDAT */ { - *(rp) = (png_byte)((256 + *rp + *(rp + 1)) & 0xff); - *(rp+2) = (png_byte)((256 + *(rp + 2) + *(rp + 1)) & 0xff); + png_ptr->mode |= PNG_AFTER_IDAT; + goto done; } + + l = png_ptr->chunk_length; } - else if (row_info->bit_depth == 16) - { - png_bytep rp; - png_uint_32 i; - if (row_info->color_type == PNG_COLOR_TYPE_RGB) - bytes_per_pixel = 6; + /* Read from the IDAT chunk into the buffer, up to png_struct::IDAT_size: + */ + if (l > buffer_size - IDAT_size) /* SAFE: while check */ + l = buffer_size - IDAT_size; - else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - bytes_per_pixel = 8; - - else - return; - - for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) - { - png_uint_32 s0 = (*(rp ) << 8) | *(rp + 1); - png_uint_32 s1 = (*(rp + 2) << 8) | *(rp + 3); - png_uint_32 s2 = (*(rp + 4) << 8) | *(rp + 5); - png_uint_32 red = (s0 + s1 + 65536) & 0xffff; - png_uint_32 blue = (s2 + s1 + 65536) & 0xffff; - *(rp ) = (png_byte)((red >> 8) & 0xff); - *(rp + 1) = (png_byte)(red & 0xff); - *(rp + 4) = (png_byte)((blue >> 8) & 0xff); - *(rp + 5) = (png_byte)(blue & 0xff); - } - } + png_crc_read(png_ptr, buffer+IDAT_size, l); + IDAT_size += (uInt)/*SAFE*/l; + png_ptr->chunk_length -= l; } +done: + + /* IDAT_size may be zero if the compressed image stream is truncated; + * this is likely given a broken PNG. + */ + png_ptr->zstream.next_in = buffer; + png_ptr->zstream.avail_in = IDAT_size; } -#endif /* PNG_MNG_FEATURES_SUPPORTED */ void PNGAPI png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row) + /* It is valid to call this API with both 'row' and 'dsp_row' NULL, all + * the processing gets done. This is only useful for, either, performance + * testing (but it skips png_combine_row) or if there is a user transform + * or user row callback which actually uses the row data. + */ { - png_row_info row_info; - if (png_ptr == NULL) return; png_debug2(1, "in png_read_row (row %lu, pass %d)", (unsigned long)png_ptr->row_number, png_ptr->pass); - /* png_read_start_row sets the information (in particular iwidth) for this - * interlace pass. + /* Check the row number; if png_read_process_IDAT is called too many times + * if issues an affirm, but, while this is appropriate for the progressive + * reader, it is an app error if it happens here. + * + * Note that when the app does the interlace handling the last row will + * typically be before the last row in the image. */ - if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) - png_read_start_row(png_ptr); - - /* 1.5.6: row_info moved out of png_struct to a local here. */ - row_info.width = png_ptr->iwidth; /* NOTE: width of current interlaced row */ - row_info.color_type = png_ptr->color_type; - row_info.bit_depth = png_ptr->bit_depth; - row_info.channels = png_ptr->channels; - row_info.pixel_depth = png_ptr->pixel_depth; - row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width); - - if (png_ptr->row_number == 0 && png_ptr->pass == 0) + if (png_ptr->read_started && + (png_ptr->interlaced == PNG_INTERLACE_NONE ? + png_ptr->row_number == png_ptr->height-1U : ( +# ifdef PNG_READ_INTERLACING_SUPPORTED + png_ptr->do_interlace ? + png_ptr->pass == 6U && png_ptr->row_number == png_ptr->height-1U : +# endif /* READ_INTERLACING */ + png_ptr->pass == PNG_LAST_PASS(png_ptr->width, png_ptr->height) && + PNG_LAST_PASS_ROW(png_ptr->row_number, png_ptr->pass, + png_ptr->height) + ) + )) { - /* Check for transforms that have been set but were defined out */ -#if defined(PNG_WRITE_INVERT_SUPPORTED) && !defined(PNG_READ_INVERT_SUPPORTED) - if (png_ptr->transformations & PNG_INVERT_MONO) - png_warning(png_ptr, "PNG_READ_INVERT_SUPPORTED is not defined"); -#endif - -#if defined(PNG_WRITE_FILLER_SUPPORTED) && !defined(PNG_READ_FILLER_SUPPORTED) - if (png_ptr->transformations & PNG_FILLER) - png_warning(png_ptr, "PNG_READ_FILLER_SUPPORTED is not defined"); -#endif - -#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \ - !defined(PNG_READ_PACKSWAP_SUPPORTED) - if (png_ptr->transformations & PNG_PACKSWAP) - png_warning(png_ptr, "PNG_READ_PACKSWAP_SUPPORTED is not defined"); -#endif - -#if defined(PNG_WRITE_PACK_SUPPORTED) && !defined(PNG_READ_PACK_SUPPORTED) - if (png_ptr->transformations & PNG_PACK) - png_warning(png_ptr, "PNG_READ_PACK_SUPPORTED is not defined"); -#endif - -#if defined(PNG_WRITE_SHIFT_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) - if (png_ptr->transformations & PNG_SHIFT) - png_warning(png_ptr, "PNG_READ_SHIFT_SUPPORTED is not defined"); -#endif - -#if defined(PNG_WRITE_BGR_SUPPORTED) && !defined(PNG_READ_BGR_SUPPORTED) - if (png_ptr->transformations & PNG_BGR) - png_warning(png_ptr, "PNG_READ_BGR_SUPPORTED is not defined"); -#endif - -#if defined(PNG_WRITE_SWAP_SUPPORTED) && !defined(PNG_READ_SWAP_SUPPORTED) - if (png_ptr->transformations & PNG_SWAP_BYTES) - png_warning(png_ptr, "PNG_READ_SWAP_SUPPORTED is not defined"); -#endif + png_app_error(png_ptr, "Too many calls to png_read_row"); + return; } -#ifdef PNG_READ_INTERLACING_SUPPORTED - /* If interlaced and we do not need a new row, combine row and return. - * Notice that the pixels we have from previous rows have been transformed - * already; we can only combine like with like (transformed or - * untransformed) and, because of the libpng API for interlaced images, this - * means we must transform before de-interlacing. + /* Check this right at the start; functions like png_read_process_IDAT + * regard this condition as an internal error: */ - if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + if (png_ptr->zowner != png_IDAT) + png_read_start_IDAT(png_ptr); + + /* So reading has started: */ + png_ptr->read_started = 1; + + for (;;) { - switch (png_ptr->pass) + if (png_ptr->zstream.avail_in == 0) + png_read_IDAT(png_ptr); + + /* So... zstream.next_in may still be 0, but this may be enough for the + * next row if zlib is storing enough output state (it only need be enough + * for one byte, because png_read_process_IDAT keeps the next filter byte, + * so on the last row of the image only one byte might be required.) + * + * png_read_process_IDAT handles the case where the input has ended; mode + * has PNG_AFTER_IDAT set, by either doing png_error or using 0 bytes for + * the data (after issuing a warning.) + */ + switch (png_read_process_IDAT(png_ptr, row, dsp_row, 0/*no save*/)) { - case 0: - if (png_ptr->row_number & 0x07) - { - if (dsp_row != NULL) - png_combine_row(png_ptr, dsp_row, 1/*display*/); - png_read_finish_row(png_ptr); - return; - } - break; + case png_row_incomplete: + /* more IDAT data needed for row */ + debug(png_ptr->zstream.avail_in == 0); + continue; - case 1: - if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) - { - if (dsp_row != NULL) - png_combine_row(png_ptr, dsp_row, 1/*display*/); - - png_read_finish_row(png_ptr); - return; - } - break; - - case 2: - if ((png_ptr->row_number & 0x07) != 4) - { - if (dsp_row != NULL && (png_ptr->row_number & 4)) - png_combine_row(png_ptr, dsp_row, 1/*display*/); - - png_read_finish_row(png_ptr); - return; - } - break; - - case 3: - if ((png_ptr->row_number & 3) || png_ptr->width < 3) - { - if (dsp_row != NULL) - png_combine_row(png_ptr, dsp_row, 1/*display*/); - - png_read_finish_row(png_ptr); - return; - } - break; - - case 4: - if ((png_ptr->row_number & 3) != 2) - { - if (dsp_row != NULL && (png_ptr->row_number & 2)) - png_combine_row(png_ptr, dsp_row, 1/*display*/); - - png_read_finish_row(png_ptr); - return; - } - break; - - case 5: - if ((png_ptr->row_number & 1) || png_ptr->width < 2) - { - if (dsp_row != NULL) - png_combine_row(png_ptr, dsp_row, 1/*display*/); - - png_read_finish_row(png_ptr); - return; - } - break; + case png_row_repeat: + /* row not in this pass, but the existing row in row_buffer or (if + * transforms are happening) png_struct::transformed_row is + * available from a previous row. + */ + /* FALL THROUGH */ + case png_row_skip: + /* row not in pass and no appropriate data; skip this row, nothing + * more need be done, except the read_row_fn and then only if libpng + * is doing the interlace handling (this is the historical + * behavior!) + */ +# ifdef PNG_READ_INTERLACING_SUPPORTED + if (!png_ptr->do_interlace) continue; +# else /* !do_interlace */ + continue; +# endif /* !do_interlace */ + /* FALL THROUGH */ + case png_row_process: + /* png_read_process_IDAT has done everything we need, the only extra + * required is to call the application row callback. + */ + if (png_ptr->read_row_fn != NULL) + png_ptr->read_row_fn(png_ptr, png_ptr->row_number, + png_ptr->pass); + /* And return now because the next row has been processed; so there + * is exactly one read_row_fn callback for each call to + * png_read_process_IDAT. + */ + return; default: - case 6: - if (!(png_ptr->row_number & 1)) - { - png_read_finish_row(png_ptr); - return; - } - break; + impossible("not reached"); } } -#endif - - if (!(png_ptr->mode & PNG_HAVE_IDAT)) - png_error(png_ptr, "Invalid attempt to read row data"); - - /* Fill the row with IDAT data: */ - png_read_IDAT_data(png_ptr, png_ptr->row_buf, row_info.rowbytes + 1); - - if (png_ptr->row_buf[0] > PNG_FILTER_VALUE_NONE) - { - if (png_ptr->row_buf[0] < PNG_FILTER_VALUE_LAST) - png_read_filter_row(png_ptr, &row_info, png_ptr->row_buf + 1, - png_ptr->prev_row + 1, png_ptr->row_buf[0]); - else - png_error(png_ptr, "bad adaptive filter value"); - } - - /* libpng 1.5.6: the following line was copying png_ptr->rowbytes before - * 1.5.6, while the buffer really is this big in current versions of libpng - * it may not be in the future, so this was changed just to copy the - * interlaced count: - */ - memcpy(png_ptr->prev_row, png_ptr->row_buf, row_info.rowbytes + 1); - -#ifdef PNG_MNG_FEATURES_SUPPORTED - if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && - (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) - { - /* Intrapixel differencing */ - png_do_read_intrapixel(&row_info, png_ptr->row_buf + 1); - } -#endif - -#ifdef PNG_READ_TRANSFORMS_SUPPORTED - if (png_ptr->transformations) - png_do_read_transformations(png_ptr, &row_info); -#endif - - /* The transformed pixel depth should match the depth now in row_info. */ - if (png_ptr->transformed_pixel_depth == 0) - { - png_ptr->transformed_pixel_depth = row_info.pixel_depth; - if (row_info.pixel_depth > png_ptr->maximum_pixel_depth) - png_error(png_ptr, "sequential row overflow"); - } - - else if (png_ptr->transformed_pixel_depth != row_info.pixel_depth) - png_error(png_ptr, "internal sequential row size calculation error"); - -#ifdef PNG_READ_INTERLACING_SUPPORTED - /* Blow up interlaced rows to full size */ - if (png_ptr->interlaced && - (png_ptr->transformations & PNG_INTERLACE)) - { - if (png_ptr->pass < 6) - png_do_read_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass, - png_ptr->transformations); - - if (dsp_row != NULL) - png_combine_row(png_ptr, dsp_row, 1/*display*/); - - if (row != NULL) - png_combine_row(png_ptr, row, 0/*row*/); - } - - else -#endif - { - if (row != NULL) - png_combine_row(png_ptr, row, -1/*ignored*/); - - if (dsp_row != NULL) - png_combine_row(png_ptr, dsp_row, -1/*ignored*/); - } - png_read_finish_row(png_ptr); - - if (png_ptr->read_row_fn != NULL) - (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); - } -#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ -#ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Read one or more rows of image data. If the image is interlaced, * and png_set_interlace_handling() has been called, the rows need to * contain the contents of the rows from the previous pass. If the @@ -674,14 +468,14 @@ png_read_rows(png_structrp png_ptr, png_bytepp row, dp++; } } -#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ +#endif /* SEQUENTIAL_READ */ -#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +#ifdef PNG_READ_IMAGE_SUPPORTED /* Read the entire image. If the image has an alpha channel or a tRNS * chunk, and you have called png_handle_alpha()[*], you will need to * initialize the image to the current image that PNG will be overlaying. * We set the num_rows again here, in case it was incorrectly set in - * png_read_start_row() by a call to png_read_update_info() or + * png_read_start_IDAT() by a call to png_read_update_info() or * png_start_read_image() if png_set_interlace_handling() wasn't called * prior to either of these functions like it should have been. You can * only call this function once. If you desire to have an image for @@ -692,54 +486,31 @@ png_read_rows(png_structrp png_ptr, png_bytepp row, void PNGAPI png_read_image(png_structrp png_ptr, png_bytepp image) { - png_uint_32 i, image_height; + png_uint_32 image_height; int pass, j; - png_bytepp rp; png_debug(1, "in png_read_image"); if (png_ptr == NULL) return; -#ifdef PNG_READ_INTERLACING_SUPPORTED - if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) - { + if (png_ptr->zowner != png_IDAT) pass = png_set_interlace_handling(png_ptr); - /* And make sure transforms are initialized. */ - png_start_read_image(png_ptr); - } + else { - if (png_ptr->interlaced && !(png_ptr->transformations & PNG_INTERLACE)) - { - /* Caller called png_start_read_image or png_read_update_info without - * first turning on the PNG_INTERLACE transform. We can fix this here, - * but the caller should do it! - */ - png_warning(png_ptr, "Interlace handling should be turned on when " - "using png_read_image"); - /* Make sure this is set correctly */ - png_ptr->num_rows = png_ptr->height; - } + if (png_ptr->interlaced == 0) + pass = 1; - /* Obtain the pass number, which also turns on the PNG_INTERLACE flag in - * the above error case. - */ - pass = png_set_interlace_handling(png_ptr); + else + pass = PNG_INTERLACE_ADAM7_PASSES; } -#else - if (png_ptr->interlaced) - png_error(png_ptr, - "Cannot read interlaced image -- interlace handler disabled"); - pass = 1; -#endif - - image_height=png_ptr->height; - - for (j = 0; j < pass; j++) + for (j = 0, image_height = png_ptr->height; j < pass; ++j) { - rp = image; + png_bytepp rp = image; + png_uint_32 i; + for (i = 0; i < image_height; i++) { png_read_row(png_ptr, *rp, NULL); @@ -747,7 +518,7 @@ png_read_image(png_structrp png_ptr, png_bytepp image) } } } -#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ +#endif /* READ_IMAGE */ #ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Read the end of the PNG file. Will not read past the end of the @@ -757,162 +528,147 @@ png_read_image(png_structrp png_ptr, png_bytepp image) void PNGAPI png_read_end(png_structrp png_ptr, png_inforp info_ptr) { -#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED - int keep; -#endif - png_debug(1, "in png_read_end"); if (png_ptr == NULL) return; - /* If png_read_end is called in the middle of reading the rows there may - * still be pending IDAT data and an owned zstream. Deal with this here. + /* When this routine is entered it is possible that an IDAT chunk still + * remains to be read. There are three conditions: + * + * 1) The app decided to handle IDAT as unknown, libpng will have consumed + * the first IDAT in png_read_info, the rest will be consumed as normal + * chunks by calls to png_handle_chunk below. + * + * 2) The app did not start to read an image, so png_read_start_IDAT was + * not called and png_struct::zowner is not png_IDAT. The first IDAT + * must still be skipped then the code below will skip the remainder. + * + * 3) The app did start to read the image. png_struct::zowner is png_IDAT + * and we need to close down the IDAT reading code. There may also be + * pending IDAT chunks, these are passed to png_read_finish_IDAT here so + * that error detection happens. If the app didn't read all the rows + * libpng will issue an 'extra compressed data' error, we could supress + * that by warning that not all the rows have been read and setting + * png_struct::zstream_error if necessary. */ -#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED - if (!png_chunk_unknown_handling(png_ptr, png_IDAT)) -#endif - png_read_finish_IDAT(png_ptr); - -#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED - /* Report invalid palette index; added at libng-1.5.10 */ - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && - png_ptr->num_palette_max > png_ptr->num_palette) - png_benign_error(png_ptr, "Read palette index exceeding num_palette"); -#endif - - do +# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + if (!(png_ptr->known_unknown & 1U)) +# endif { - png_uint_32 length = png_read_chunk_header(png_ptr); - png_uint_32 chunk_name = png_ptr->chunk_name; - - if (chunk_name == png_IEND) - png_handle_IEND(png_ptr, info_ptr, length); - - else if (chunk_name == png_IHDR) - png_handle_IHDR(png_ptr, info_ptr, length); - - else if (info_ptr == NULL) - png_crc_finish(png_ptr, length); - -#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED - else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0) + if (png_ptr->zowner == png_IDAT) { - if (chunk_name == png_IDAT) - { - if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) - png_benign_error(png_ptr, "Too many IDATs found(a1)"); - } - png_handle_unknown(png_ptr, info_ptr, length, keep); - if (chunk_name == png_PLTE) - png_ptr->mode |= PNG_HAVE_PLTE; - } -#endif - - else if (chunk_name == png_IDAT) - { - /* Zero length IDATs are legal after the last IDAT has been - * read, but not after other chunks have been read. + /* Normal case: read to the end of the IDAT chunks. In about + * 5/PNG_IDAT_READ_SIZE cases (typically that's 1:820) zlib will have + * returned all the image data but not read up to the end of the + * Adler32 because the end of the stream had not been read. Make sure + * it gets read here: */ - if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) - png_benign_error(png_ptr, "Too many IDATs found(a2)"); + if (png_ptr->zstream.avail_in == 0) + png_read_IDAT(png_ptr); - png_crc_finish(png_ptr, length); + while (!png_read_finish_IDAT(png_ptr)) { + /* This will adjust zstream.next/avail_in appropriately and if + * necessary read the next chunk. After this avail_in may still + * be zero, but if it is then PNG_AFTER_IDAT should be set. + */ + debug(png_ptr->zstream.avail_in == 0); + png_read_IDAT(png_ptr); + debug(png_ptr->zstream.avail_in > 0 || + (png_ptr->mode & PNG_AFTER_IDAT) != 0); + } + + debug(png_ptr->zstream.avail_in == 0 && png_ptr->zowner == 0); + + /* If this is still an IDAT then it hasn't been finished; at least + * the CRC has not been read. If there is data left in it then + * an error may need to be output. Note that the code below handles + * any additional chunks. + */ + if (png_ptr->chunk_name == png_IDAT) + { + if (png_ptr->chunk_length > 0 && !png_ptr->zstream_error) + { + png_chunk_benign_error(png_ptr, "too much IDAT data (read)"); + png_ptr->zstream_error = 1; + } + + png_crc_finish(png_ptr, png_ptr->chunk_length); + png_read_chunk_header(png_ptr); + } } - else if (chunk_name == png_PLTE) - png_handle_PLTE(png_ptr, info_ptr, length); -#ifdef PNG_READ_bKGD_SUPPORTED - else if (chunk_name == png_bKGD) - png_handle_bKGD(png_ptr, info_ptr, length); -#endif + else if (png_ptr->chunk_name == png_IDAT) + { + /* This IDAT has not been processed, the remainder will be finished + * in the loop. This is the case where IDAT is being skipped because + * the rows weren't read, this is OK, but warn anyway. + */ + png_crc_finish(png_ptr, png_ptr->chunk_length); + png_app_warning(png_ptr, "image reading skipped"); + png_ptr->zstream_error = 1; /* Prevent 'too much IDAT' errors */ + png_read_chunk_header(png_ptr); + } -#ifdef PNG_READ_cHRM_SUPPORTED - else if (chunk_name == png_cHRM) - png_handle_cHRM(png_ptr, info_ptr, length); -#endif + else /* This might work, if the signature was read, but just in case: */ + png_app_error(png_ptr, "Missing call to png_read_info"); + } -#ifdef PNG_READ_gAMA_SUPPORTED - else if (chunk_name == png_gAMA) - png_handle_gAMA(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_hIST_SUPPORTED - else if (chunk_name == png_hIST) - png_handle_hIST(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_oFFs_SUPPORTED - else if (chunk_name == png_oFFs) - png_handle_oFFs(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_pCAL_SUPPORTED - else if (chunk_name == png_pCAL) - png_handle_pCAL(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sCAL_SUPPORTED - else if (chunk_name == png_sCAL) - png_handle_sCAL(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_pHYs_SUPPORTED - else if (chunk_name == png_pHYs) - png_handle_pHYs(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sBIT_SUPPORTED - else if (chunk_name == png_sBIT) - png_handle_sBIT(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sRGB_SUPPORTED - else if (chunk_name == png_sRGB) - png_handle_sRGB(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_iCCP_SUPPORTED - else if (chunk_name == png_iCCP) - png_handle_iCCP(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sPLT_SUPPORTED - else if (chunk_name == png_sPLT) - png_handle_sPLT(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tEXt_SUPPORTED - else if (chunk_name == png_tEXt) - png_handle_tEXt(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tIME_SUPPORTED - else if (chunk_name == png_tIME) - png_handle_tIME(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tRNS_SUPPORTED - else if (chunk_name == png_tRNS) - png_handle_tRNS(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_zTXt_SUPPORTED - else if (chunk_name == png_zTXt) - png_handle_zTXt(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_iTXt_SUPPORTED - else if (chunk_name == png_iTXt) - png_handle_iTXt(png_ptr, info_ptr, length); -#endif +# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else + { + /* IDAT is unknown, the chunk that terminated the loop must be an IDAT + * and it has been processed. Get a new chunk header. + */ + if (png_ptr->chunk_name == png_IDAT) + png_read_chunk_header(png_ptr); else - png_handle_unknown(png_ptr, info_ptr, length, - PNG_HANDLE_CHUNK_AS_DEFAULT); - } while (!(png_ptr->mode & PNG_HAVE_IEND)); + png_app_error(png_ptr, + "Missing call to png_read_info with unknown IDAT"); + } +# endif + + if ((png_ptr->mode & PNG_HAVE_IEND) == 0) for (;;) + { + switch (png_find_chunk_op(png_ptr)) + { + default: + impossible("invalid chunk op"); + /* FALL THROUGH */ + case png_chunk_skip: + png_crc_finish(png_ptr, png_ptr->chunk_length); + break; + + case png_chunk_unknown: + png_read_sequential_unknown(png_ptr, info_ptr); + break; + + case png_chunk_process_all: + png_handle_chunk(png_ptr, info_ptr); + break; + + case png_chunk_process_part: + debug(png_ptr->chunk_name == png_IDAT); + debug(!(png_ptr->mode & PNG_AFTER_IDAT)); + if (png_ptr->chunk_length > 0 && !png_ptr->zstream_error) + { + png_chunk_benign_error(png_ptr, "too many IDAT chunks"); + png_ptr->zstream_error = 1; + } + + /* Skip it: */ + png_crc_finish(png_ptr, png_ptr->chunk_length); + return; + } + + if ((png_ptr->mode & PNG_HAVE_IEND) != 0) + break; + + png_read_chunk_header(png_ptr); + } } -#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ +#endif /* SEQUENTIAL_READ */ /* Free all memory used in the read struct */ static void @@ -920,43 +676,50 @@ png_read_destroy(png_structrp png_ptr) { png_debug(1, "in png_read_destroy"); -#ifdef PNG_READ_GAMMA_SUPPORTED - png_destroy_gamma_table(png_ptr); -#endif + png_read_free_row_buffers(png_ptr); - png_free(png_ptr, png_ptr->big_row_buf); - png_free(png_ptr, png_ptr->big_prev_row); png_free(png_ptr, png_ptr->read_buffer); + png_ptr->read_buffer = NULL; -#ifdef PNG_READ_QUANTIZE_SUPPORTED - png_free(png_ptr, png_ptr->palette_lookup); - png_free(png_ptr, png_ptr->quantize_index); -#endif + if (png_ptr->palette != NULL) + { + png_free(png_ptr, png_ptr->palette); + png_ptr->num_palette = 0; + png_ptr->palette = NULL; + } - if (png_ptr->free_me & PNG_FREE_PLTE) - png_zfree(png_ptr, png_ptr->palette); - png_ptr->free_me &= ~PNG_FREE_PLTE; - -#if defined(PNG_tRNS_SUPPORTED) || \ - defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) - if (png_ptr->free_me & PNG_FREE_TRNS) +#ifdef PNG_READ_tRNS_SUPPORTED + if (png_ptr->trans_alpha != NULL) + { png_free(png_ptr, png_ptr->trans_alpha); - png_ptr->free_me &= ~PNG_FREE_TRNS; + png_ptr->num_trans = 0; + png_ptr->trans_alpha = NULL; + } #endif - inflateEnd(&png_ptr->zstream); + if (png_ptr->zstream.state != NULL) + { + int ret = inflateEnd(&png_ptr->zstream); + + if (ret != Z_OK) + { + png_zstream_error(&png_ptr->zstream, ret); + png_warning(png_ptr, png_ptr->zstream.msg); + } + } + +#ifdef PNG_TRANSFORM_MECH_SUPPORTED + png_transform_free(png_ptr, &png_ptr->transform_list); +#endif #ifdef PNG_PROGRESSIVE_READ_SUPPORTED png_free(png_ptr, png_ptr->save_buffer); -#endif - -#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) && \ - defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) - png_free(png_ptr, png_ptr->unknown_chunk.data); + png_ptr->save_buffer = NULL; #endif #ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list = NULL; #endif /* NOTE: the 'setjmp' buffer may still be allocated and the memory and error @@ -1002,12 +765,21 @@ png_set_read_status_fn(png_structrp png_ptr, png_read_status_ptr read_row_fn) } -#ifdef PNG_SEQUENTIAL_READ_SUPPORTED -#ifdef PNG_INFO_IMAGE_SUPPORTED +#ifdef PNG_READ_PNG_SUPPORTED +#ifdef __GNUC__ +/* This exists solely to work round a warning from GNU C. */ +static int /* PRIVATE */ +png_gt(size_t a, size_t b) +{ + return a > b; +} +#else +# define png_gt(a,b) ((a) > (b)) +#endif + void PNGAPI -png_read_png(png_structrp png_ptr, png_inforp info_ptr, - int transforms, - voidp params) +png_read_png(png_structrp png_ptr, png_inforp info_ptr, int transforms, + voidp params) { if (png_ptr == NULL || info_ptr == NULL) return; @@ -1016,7 +788,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr, * PNG file before the first IDAT (image data chunk). */ png_read_info(png_ptr, info_ptr); - if (info_ptr->height > PNG_UINT_32_MAX/(sizeof (png_bytep))) + if (png_gt(info_ptr->height, PNG_SIZE_MAX/(sizeof (png_bytep)))) png_error(png_ptr, "Image is too high to process with png_read_png()"); /* -------------- image transformations start here ------------------- */ @@ -1028,7 +800,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr, /* Tell libpng to strip 16-bit/color files down to 8 bits per color. */ - if (transforms & PNG_TRANSFORM_SCALE_16) + if ((transforms & PNG_TRANSFORM_SCALE_16) != 0) /* Added at libpng-1.5.4. "strip_16" produces the same result that it * did in earlier versions, while "scale_16" is now more accurate. */ @@ -1042,7 +814,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr, * latter by doing SCALE first. This is ok and allows apps not to check for * which is supported to get the right answer. */ - if (transforms & PNG_TRANSFORM_STRIP_16) + if ((transforms & PNG_TRANSFORM_STRIP_16) != 0) #ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED png_set_strip_16(png_ptr); #else @@ -1052,7 +824,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr, /* Strip alpha bytes from the input data without combining with * the background (not recommended). */ - if (transforms & PNG_TRANSFORM_STRIP_ALPHA) + if ((transforms & PNG_TRANSFORM_STRIP_ALPHA) != 0) #ifdef PNG_READ_STRIP_ALPHA_SUPPORTED png_set_strip_alpha(png_ptr); #else @@ -1062,7 +834,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr, /* Extract multiple pixels with bit depths of 1, 2, or 4 from a single * byte into separate bytes (useful for paletted and grayscale images). */ - if (transforms & PNG_TRANSFORM_PACKING) + if ((transforms & PNG_TRANSFORM_PACKING) != 0) #ifdef PNG_READ_PACK_SUPPORTED png_set_packing(png_ptr); #else @@ -1072,7 +844,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr, /* Change the order of packed pixels to least significant bit first * (not useful if you are using png_set_packing). */ - if (transforms & PNG_TRANSFORM_PACKSWAP) + if ((transforms & PNG_TRANSFORM_PACKSWAP) != 0) #ifdef PNG_READ_PACKSWAP_SUPPORTED png_set_packswap(png_ptr); #else @@ -1084,19 +856,17 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr, * Expand paletted or RGB images with transparency to full alpha * channels so the data will be available as RGBA quartets. */ - if (transforms & PNG_TRANSFORM_EXPAND) + if ((transforms & PNG_TRANSFORM_EXPAND) != 0) #ifdef PNG_READ_EXPAND_SUPPORTED png_set_expand(png_ptr); #else png_app_error(png_ptr, "PNG_TRANSFORM_EXPAND not supported"); #endif - /* We don't handle background color or gamma transformation or quantizing. - */ + /* We don't handle background color, gamma transformation, or quantizing. */ - /* Invert monochrome files to have 0 as white and 1 as black - */ - if (transforms & PNG_TRANSFORM_INVERT_MONO) + /* Invert monochrome files to have 0 as white and 1 as black */ + if ((transforms & PNG_TRANSFORM_INVERT_MONO) != 0) #ifdef PNG_READ_INVERT_SUPPORTED png_set_invert_mono(png_ptr); #else @@ -1107,16 +877,16 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr, * [0,65535] to the original [0,7] or [0,31], or whatever range the * colors were originally in: */ - if (transforms & PNG_TRANSFORM_SHIFT) + if ((transforms & PNG_TRANSFORM_SHIFT) != 0) #ifdef PNG_READ_SHIFT_SUPPORTED - if (info_ptr->valid & PNG_INFO_sBIT) + if ((info_ptr->valid & PNG_INFO_sBIT) != 0) png_set_shift(png_ptr, &info_ptr->sig_bit); #else png_app_error(png_ptr, "PNG_TRANSFORM_SHIFT not supported"); #endif /* Flip the RGB pixels to BGR (or RGBA to BGRA) */ - if (transforms & PNG_TRANSFORM_BGR) + if ((transforms & PNG_TRANSFORM_BGR) != 0) #ifdef PNG_READ_BGR_SUPPORTED png_set_bgr(png_ptr); #else @@ -1124,7 +894,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr, #endif /* Swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */ - if (transforms & PNG_TRANSFORM_SWAP_ALPHA) + if ((transforms & PNG_TRANSFORM_SWAP_ALPHA) != 0) #ifdef PNG_READ_SWAP_ALPHA_SUPPORTED png_set_swap_alpha(png_ptr); #else @@ -1132,7 +902,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr, #endif /* Swap bytes of 16-bit files to least significant byte first */ - if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) + if ((transforms & PNG_TRANSFORM_SWAP_ENDIAN) != 0) #ifdef PNG_READ_SWAP_SUPPORTED png_set_swap(png_ptr); #else @@ -1141,7 +911,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr, /* Added at libpng-1.2.41 */ /* Invert the alpha channel from opacity to transparency */ - if (transforms & PNG_TRANSFORM_INVERT_ALPHA) + if ((transforms & PNG_TRANSFORM_INVERT_ALPHA) != 0) #ifdef PNG_READ_INVERT_ALPHA_SUPPORTED png_set_invert_alpha(png_ptr); #else @@ -1150,7 +920,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr, /* Added at libpng-1.2.41 */ /* Expand grayscale image to RGB */ - if (transforms & PNG_TRANSFORM_GRAY_TO_RGB) + if ((transforms & PNG_TRANSFORM_GRAY_TO_RGB) != 0) #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED png_set_gray_to_rgb(png_ptr); #else @@ -1158,7 +928,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr, #endif /* Added at libpng-1.5.4 */ - if (transforms & PNG_TRANSFORM_EXPAND_16) + if ((transforms & PNG_TRANSFORM_EXPAND_16) != 0) #ifdef PNG_READ_EXPAND_16_SUPPORTED png_set_expand_16(png_ptr); #else @@ -1184,6 +954,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr, if (info_ptr->row_pointers == NULL) { png_uint_32 iptr; + png_alloc_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr); info_ptr->row_pointers = png_voidcast(png_bytepp, png_malloc(png_ptr, info_ptr->height * (sizeof (png_bytep)))); @@ -1195,7 +966,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr, for (iptr = 0; iptr < info_ptr->height; iptr++) info_ptr->row_pointers[iptr] = png_voidcast(png_bytep, - png_malloc(png_ptr, info_ptr->rowbytes)); + png_malloc(png_ptr, rowbytes)); } png_read_image(png_ptr, info_ptr->row_pointers); @@ -1206,8 +977,7 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr, PNG_UNUSED(params) } -#endif /* PNG_INFO_IMAGE_SUPPORTED */ -#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ +#endif /* READ_PNG */ #ifdef PNG_SIMPLIFIED_READ_SUPPORTED /* SIMPLIFIED READ @@ -1223,9 +993,10 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr, # define P_LINEAR 2 /* 16-bit linear: not encoded, NOT pre-multiplied! */ # define P_FILE 3 /* 8-bit encoded to file gamma, not sRGB or linear */ # define P_LINEAR8 4 /* 8-bit linear: only from a file value */ +# define P_FILE8 5 /* 8-bit encoded to file gamma but not significant bits */ /* Color-map processing: after libpng has run on the PNG image further - * processing may be needed to conver the data to color-map indicies. + * processing may be needed to convert the data to color-map indices. */ #define PNG_CMAP_NONE 0 #define PNG_CMAP_GA 1 /* Process GA data to a color-map with alpha */ @@ -1245,7 +1016,7 @@ typedef struct /* Arguments: */ png_imagep image; png_voidp buffer; - png_int_32 row_stride; + ptrdiff_t row_stride; png_voidp colormap; png_const_colorp background; /* Local variables: */ @@ -1253,8 +1024,9 @@ typedef struct png_voidp first_row; ptrdiff_t row_bytes; /* step between rows */ int file_encoding; /* E_ values above */ - png_fixed_point gamma_to_linear; /* For P_FILE, reciprocal of gamma */ + png_fixed_point file_to_sRGB; /* Cached correction factor */ int colormap_processing; /* PNG_CMAP_ values above */ + png_byte sBIT[4]; /* Significant bits for channels */ } png_image_read_control; /* Do all the *safe* initialization - 'safe' means that png_error won't be @@ -1283,7 +1055,7 @@ png_image_read_init(png_imagep image) if (info_ptr != NULL) { png_controlp control = png_voidcast(png_controlp, - png_malloc_warn(png_ptr, (sizeof *control))); + png_malloc_warn(png_ptr, (sizeof *control))); if (control != NULL) { @@ -1316,10 +1088,10 @@ png_image_format(png_structrp png_ptr) { png_uint_32 format = 0; - if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0) format |= PNG_FORMAT_FLAG_COLOR; - if (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) + if ((png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0) format |= PNG_FORMAT_FLAG_ALPHA; /* Use png_ptr here, not info_ptr, because by examination png_handle_tRNS @@ -1333,30 +1105,19 @@ png_image_format(png_structrp png_ptr) if (png_ptr->bit_depth == 16) format |= PNG_FORMAT_FLAG_LINEAR; - if (png_ptr->color_type & PNG_COLOR_MASK_PALETTE) + if ((png_ptr->color_type & PNG_COLOR_MASK_PALETTE) != 0) format |= PNG_FORMAT_FLAG_COLORMAP; return format; } -/* Is the given gamma significantly different from sRGB? The test is the same - * one used in pngrtran.c when deciding whether to do gamma correction. The - * arithmetic optimizes the division by using the fact that the inverse of the - * file sRGB gamma is 2.2 +/* Is the given gamma significantly different from sRGB? */ static int png_gamma_not_sRGB(png_fixed_point g) { - if (g < PNG_FP_1) - { - /* An uninitialized gamma is assumed to be sRGB for the simplified API. */ - if (g == 0) - return 0; - - return png_gamma_significant((g * 11 + 2)/5 /* i.e. *2.2, rounded */); - } - - return 1; + /* An uninitialized gamma is assumed to be sRGB for the simplified API. */ + return g != 0 && !PNG_GAMMA_IS_sRGB(g); } /* Do the main body of a 'png_image_begin_read' function; read the PNG file @@ -1370,7 +1131,9 @@ png_image_read_header(png_voidp argument) png_structrp png_ptr = image->opaque->png_ptr; png_inforp info_ptr = image->opaque->info_ptr; +#ifdef PNG_BENIGN_ERRORS_SUPPORTED png_set_benign_errors(png_ptr, 1/*warn*/); +#endif png_read_info(png_ptr, info_ptr); /* Do this the fast way; just read directly out of png_struct. */ @@ -1425,6 +1188,47 @@ png_image_read_header(png_voidp argument) return 1; } +static void +png_image_get_sBIT(png_image_read_control *display) + /* Utility to cache the sBIT values. This uses the information from the + * png_struct not png_info because it may be needed after the sBIT + * information in png_info has been invalidated. + */ +{ + if (display->sBIT[0] == 0) + { + const png_const_structrp png_ptr = display->image->opaque->png_ptr; + const unsigned int color_type = png_ptr->color_type; + const png_byte bit_depth = + (color_type & PNG_COLOR_MASK_PALETTE) ? 8U : png_ptr->bit_depth; + + memset(display->sBIT, bit_depth, sizeof display->sBIT); + + if (color_type & PNG_COLOR_MASK_COLOR) + { + if (png_ptr->sig_bit.red > 0 && png_ptr->sig_bit.red < bit_depth) + display->sBIT[0] = png_ptr->sig_bit.red; + if (png_ptr->sig_bit.green > 0 && png_ptr->sig_bit.green < bit_depth) + display->sBIT[1] = png_ptr->sig_bit.green; + if (png_ptr->sig_bit.blue > 0 && png_ptr->sig_bit.blue < bit_depth) + display->sBIT[2] = png_ptr->sig_bit.blue; + } + + else + { + if (png_ptr->sig_bit.gray > 0 && png_ptr->sig_bit.gray < bit_depth) + display->sBIT[2] = display->sBIT[1] = display->sBIT[0] = + png_ptr->sig_bit.gray; + } + + if (color_type & PNG_COLOR_MASK_ALPHA) + { + if (png_ptr->sig_bit.alpha > 0 && png_ptr->sig_bit.alpha < bit_depth) + display->sBIT[3] = png_ptr->sig_bit.alpha; + } + } +} + #ifdef PNG_STDIO_SUPPORTED int PNGAPI png_image_begin_read_from_stdio(png_imagep image, FILE* file) @@ -1433,25 +1237,19 @@ png_image_begin_read_from_stdio(png_imagep image, FILE* file) { if (file != NULL) { - if (png_image_read_init(image)) - { - /* This is slightly evil, but png_init_io doesn't do anything other - * than this and we haven't changed the standard IO functions so - * this saves a 'safe' function. - */ - image->opaque->png_ptr->io_ptr = file; + if (png_image_read_init(image) != 0 && + png_image_init_io(image, file) != 0) return png_safe_execute(image, png_image_read_header, image); - } } else return png_image_error(image, - "png_image_begin_read_from_stdio: invalid argument"); + "png_image_begin_read_from_stdio: invalid argument"); } else if (image != NULL) return png_image_error(image, - "png_image_begin_read_from_stdio: incorrect PNG_IMAGE_VERSION"); + "png_image_begin_read_from_stdio: incorrect PNG_IMAGE_VERSION"); return 0; } @@ -1467,9 +1265,9 @@ png_image_begin_read_from_file(png_imagep image, const char *file_name) if (fp != NULL) { - if (png_image_read_init(image)) + if (png_image_read_init(image) != 0 && + png_image_init_io(image, fp) != 0) { - image->opaque->png_ptr->io_ptr = fp; image->opaque->owned_file = 1; return png_safe_execute(image, png_image_read_header, image); } @@ -1484,16 +1282,16 @@ png_image_begin_read_from_file(png_imagep image, const char *file_name) else return png_image_error(image, - "png_image_begin_read_from_file: invalid argument"); + "png_image_begin_read_from_file: invalid argument"); } else if (image != NULL) return png_image_error(image, - "png_image_begin_read_from_file: incorrect PNG_IMAGE_VERSION"); + "png_image_begin_read_from_file: incorrect PNG_IMAGE_VERSION"); return 0; } -#endif /* PNG_STDIO_SUPPORTED */ +#endif /* STDIO */ static void PNGCBAPI png_image_memory_read(png_structp png_ptr, png_bytep out, png_size_t need) @@ -1525,14 +1323,26 @@ png_image_memory_read(png_structp png_ptr, png_bytep out, png_size_t need) } } -int PNGAPI png_image_begin_read_from_memory(png_imagep image, - png_const_voidp memory, png_size_t size) +static int +image_init_memory_io(png_voidp param) + /* Set the read function and pointer for a memory read, the io pointer is + * just the imagep so it is passed in directly. + */ +{ + png_imagep image = png_voidcast(png_imagep, param); + png_set_read_fn(image->opaque->png_ptr, image, png_image_memory_read); + return 1; +} + +int PNGAPI +png_image_begin_read_from_memory(png_imagep image, png_const_voidp memory, + png_size_t size) { if (image != NULL && image->version == PNG_IMAGE_VERSION) { if (memory != NULL && size > 0) { - if (png_image_read_init(image)) + if (png_image_read_init(image) != 0) { /* Now set the IO functions to read from the memory buffer and * store it into io_ptr. Again do this in-place to avoid calling a @@ -1540,21 +1350,20 @@ int PNGAPI png_image_begin_read_from_memory(png_imagep image, */ image->opaque->memory = png_voidcast(png_const_bytep, memory); image->opaque->size = size; - image->opaque->png_ptr->io_ptr = image; - image->opaque->png_ptr->read_data_fn = png_image_memory_read; - return png_safe_execute(image, png_image_read_header, image); + return png_safe_execute(image, image_init_memory_io, image) && + png_safe_execute(image, png_image_read_header, image); } } else return png_image_error(image, - "png_image_begin_read_from_memory: invalid argument"); + "png_image_begin_read_from_memory: invalid argument"); } else if (image != NULL) return png_image_error(image, - "png_image_begin_read_from_memory: incorrect PNG_IMAGE_VERSION"); + "png_image_begin_read_from_memory: incorrect PNG_IMAGE_VERSION"); return 0; } @@ -1600,18 +1409,18 @@ png_image_skip_unused_chunks(png_structrp png_ptr) * IHDR, PLTE, tRNS, IDAT, and IEND chunks. */ png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_NEVER, - NULL, -1); + NULL, -1); /* But do not ignore image data handling chunks */ png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_AS_DEFAULT, - chunks_to_process, (sizeof chunks_to_process)/5); - } + chunks_to_process, (int)/*SAFE*/(sizeof chunks_to_process)/5); + } } # define PNG_SKIP_CHUNKS(p) png_image_skip_unused_chunks(p) #else # define PNG_SKIP_CHUNKS(p) ((void)0) -#endif /* PNG_HANDLE_AS_UNKNOWN_SUPPORTED */ +#endif /* HANDLE_AS_UNKNOWN */ /* The following macro gives the exact rounded answer for all values in the * range 0..255 (it actually divides by 51.2, but the rounding still generates @@ -1623,14 +1432,15 @@ png_image_skip_unused_chunks(png_structrp png_ptr) static void set_file_encoding(png_image_read_control *display) { - png_fixed_point g = display->image->opaque->png_ptr->colorspace.gamma; - if (png_gamma_significant(g)) + /* First test for an encoding close to linear: */ + if (png_need_gamma_correction(display->image->opaque->png_ptr, + 0/*PNG gamma*/, 0/*not sRGB*/)) { + png_fixed_point g = display->image->opaque->png_ptr->colorspace.gamma; + + /* Now look for one close to sRGB: */ if (png_gamma_not_sRGB(g)) - { display->file_encoding = P_FILE; - display->gamma_to_linear = png_reciprocal(g); - } else display->file_encoding = P_sRGB; @@ -1640,11 +1450,138 @@ set_file_encoding(png_image_read_control *display) display->file_encoding = P_LINEAR8; } -static unsigned int -decode_gamma(png_image_read_control *display, png_uint_32 value, int encoding) +/* For colormap entries we end up doing the gamma correction here and the + * following routines are provided to separate out the code. In all cases the + * input value is in the range 0..255 and is encoded P_FILE with the gamma value + * stored in the png_struct colorspace. + */ +static void +init_correct(png_const_structrp png_ptr, png_fixed_point *correct) { + /* Record the convertion necessary to get from the encoding values to + * sRGB. If this overflows just store FP_1. + * + * NOTE: this code used to store, and use, a convertion factor to + * linear then use the sRGB encoding tables to get back to sRGB, but + * this smashes the low values; the ones which fall in the linear part + * of the sRGB transfer function. + * + * The new version of this code assumes an encoding which is neither + * linear nor sRGB is a power law transform of the sRGB curve, not + * linear values. This is somewhat at odds with a precise reading of + * the PNG spec, but given that we are trying to produce sRGB values + * here it is most likely to be correct. + */ + affirm(png_ptr->colorspace.gamma > 0); + + if (!png_muldiv(correct, PNG_GAMMA_sRGB_INVERSE, PNG_FP_1, + png_ptr->colorspace.gamma)) + *correct = PNG_FP_1; +} + +static png_uint_32 +update_for_sBIT(png_uint_32 value, unsigned int significant_bits, + unsigned int bit_depth) + /* Return a bit_depth value adjusted for the number of significant bits in + * the value. + */ +{ + if (significant_bits < bit_depth) + { + value >>= bit_depth - significant_bits; + /* Now scale back to bit_depth, taking care not to overflow when 'value' + * is (1<image->opaque->png_ptr; + + debug(value <= 255U && significant_bits <= 8U && significant_bits > 0U); + + if (display->file_to_sRGB == 0) + init_correct(png_ptr, &display->file_to_sRGB); + + /* Now simply apply this correction factor and scale back to 8 bits. */ + if (display->file_to_sRGB != PNG_FP_1) + value = png_gamma_nxmbit_correct(value >> (8U-significant_bits), + display->file_to_sRGB, significant_bits, 8U); + + else if (significant_bits < 8U) + value = update_for_sBIT(value, significant_bits, 8U); + + return value; +} + +static png_uint_32 +convert_to_linear(png_image_read_control *display, png_uint_32 value, + unsigned int significant_bits) +{ + /* Converts an 8-bit value from P_FILE to 16-bit P_LINEAR */ + png_const_structrp png_ptr = display->image->opaque->png_ptr; + + debug(value <= 255U && significant_bits <= 8U && significant_bits > 0U); + + if (display->file_to_sRGB == 0) + init_correct(png_ptr, &display->file_to_sRGB); + + /* Use this correction to get a 16-bit sRGB value: */ + if (display->file_to_sRGB != PNG_FP_1) + value = png_gamma_nxmbit_correct(value >> (8U-significant_bits), + display->file_to_sRGB, significant_bits, 16U); + + else + { + value *= 257U; + + if (significant_bits < 8U) + value = update_for_sBIT(value, significant_bits, 16U); + } + + /* Now convert this back to linear, using the correct transfer function. */ + if (value <= 2650U /* 65535 * 0.04045 */) + { + /* We want to divide a 12-bit number by 12.92, do this by scaling to 32 + * bits then dividing by 2^24, with rounding: + */ + value = (value * 1298546U + 649273U) >> 24; + } + + else + { + /* Calculate for v in the range 0.04045..1.0 calculate: + * + * ((v + 0.055)/1.055)^2.4 + * + * the gamma correction function needs a 16-bit value: + */ + value *= 62119U; + value += 223904831U+32768U; /* cannot overflow; test with 65535 */ + value = png_gamma_nxmbit_correct(value >> 16, 240000, 16U, 16U); + } + + return value; +} + +static unsigned int +decode_gamma(png_image_read_control *display, png_uint_32 value, + unsigned int significant_bits, int encoding) +{ + int do_sBIT = 0; + if (encoding == P_FILE) /* double check */ - encoding = display->file_encoding; + encoding = display->file_encoding, do_sBIT = 1; if (encoding == P_NOTSET) /* must be the file encoding */ { @@ -1655,23 +1592,31 @@ decode_gamma(png_image_read_control *display, png_uint_32 value, int encoding) switch (encoding) { case P_FILE: - value = png_gamma_16bit_correct(value*257, display->gamma_to_linear); + /* This is a file value, so the sBIT, if any, needs to be used. */ + value = convert_to_linear(display, value, significant_bits); break; case P_sRGB: + if (do_sBIT) + value = update_for_sBIT(value, significant_bits, 8U); + value = png_sRGB_table[value]; break; case P_LINEAR: + if (do_sBIT) + value = update_for_sBIT(value, significant_bits, 16U); break; case P_LINEAR8: value *= 257; + if (do_sBIT) + value = update_for_sBIT(value, significant_bits, 16U); break; default: - png_error(display->image->opaque->png_ptr, - "unexpected encoding (internal error)"); + png_impossiblepp(display->image->opaque->png_ptr, + "unexpected encoding"); break; } @@ -1680,16 +1625,18 @@ decode_gamma(png_image_read_control *display, png_uint_32 value, int encoding) static png_uint_32 png_colormap_compose(png_image_read_control *display, - png_uint_32 foreground, int foreground_encoding, png_uint_32 alpha, - png_uint_32 background, int encoding) + png_uint_32 foreground, unsigned int foreground_significant_bits, + int foreground_encoding, png_uint_32 alpha, + png_uint_32 background, int encoding) { /* The file value is composed on the background, the background has the given * encoding and so does the result, the file is encoded with P_FILE and the * file and alpha are 8-bit values. The (output) encoding will always be * P_LINEAR or P_sRGB. */ - png_uint_32 f = decode_gamma(display, foreground, foreground_encoding); - png_uint_32 b = decode_gamma(display, background, encoding); + png_uint_32 f = decode_gamma(display, foreground, + foreground_significant_bits, foreground_encoding); + png_uint_32 b = decode_gamma(display, background, 0U/*UNUSED*/, encoding); /* The alpha is always an 8-bit value (it comes from the palette), the value * scaled by 255 is what PNG_sRGB_FROM_LINEAR requires. @@ -1707,7 +1654,7 @@ png_colormap_compose(png_image_read_control *display, } else /* P_sRGB */ - f = PNG_sRGB_FROM_LINEAR(f); + f = PNG_sRGB_FROM_LINEAR(display->image->opaque->png_ptr, f); return f; } @@ -1717,17 +1664,25 @@ png_colormap_compose(png_image_read_control *display, */ static void png_create_colormap_entry(png_image_read_control *display, - png_uint_32 ip, png_uint_32 red, png_uint_32 green, png_uint_32 blue, - png_uint_32 alpha, int encoding) + png_uint_32 ip, png_uint_32 red, png_uint_32 green, png_uint_32 blue, + png_uint_32 alpha, int encoding) { png_imagep image = display->image; - const int output_encoding = (image->format & PNG_FORMAT_FLAG_LINEAR) ? +# define png_ptr image->opaque->png_ptr /* for error messages */ + const int output_encoding = (image->format & PNG_FORMAT_FLAG_LINEAR) != 0 ? P_LINEAR : P_sRGB; const int convert_to_Y = (image->format & PNG_FORMAT_FLAG_COLOR) == 0 && - (red != green || green != blue); + (red != green || green != blue); + int use_sBIT = encoding == P_FILE; - if (ip > 255) - png_error(image->opaque->png_ptr, "color-map index out of range"); + affirm(ip <= 255); + implies(encoding != P_LINEAR, red <= 255U && green <= 255U && blue <= 255U + && display->sBIT[0] <= 8U && display->sBIT[1] <= 8U + && display->sBIT[2] <= 8U && display->sBIT[3] <= 8U); + + /* This is a hack for the grayscale colormap below. */ + if (encoding == P_FILE8) + encoding = P_FILE; /* Update the cache with whether the file gamma is significantly different * from sRGB. @@ -1737,67 +1692,112 @@ png_create_colormap_entry(png_image_read_control *display, if (display->file_encoding == P_NOTSET) set_file_encoding(display); - /* Note that the cached value may be P_FILE too, but if it is then the - * gamma_to_linear member has been set. - */ + /* Note that the cached value may be P_FILE too. */ encoding = display->file_encoding; + if (use_sBIT) + png_image_get_sBIT(display); } if (encoding == P_FILE) { - png_fixed_point g = display->gamma_to_linear; - - red = png_gamma_16bit_correct(red*257, g); - green = png_gamma_16bit_correct(green*257, g); - blue = png_gamma_16bit_correct(blue*257, g); - - if (convert_to_Y || output_encoding == P_LINEAR) + if (convert_to_Y != 0 || output_encoding == P_LINEAR) { - alpha *= 257; + red = convert_to_linear(display, red, + use_sBIT ? display->sBIT[0] : 8U); + green = convert_to_linear(display, green, + use_sBIT ? display->sBIT[1] : 8U); + blue = convert_to_linear(display, blue, + use_sBIT ? display->sBIT[2] : 8U); + alpha *= 257U; + if (use_sBIT) + alpha = update_for_sBIT(alpha, display->sBIT[3], 16U); + encoding = P_LINEAR; + use_sBIT = 0; } else { - red = PNG_sRGB_FROM_LINEAR(red * 255); - green = PNG_sRGB_FROM_LINEAR(green * 255); - blue = PNG_sRGB_FROM_LINEAR(blue * 255); + red = convert_to_sRGB(display, red, + use_sBIT ? display->sBIT[0] : 8U); + green = convert_to_sRGB(display, green, + use_sBIT ? display->sBIT[1] : 8U); + blue = convert_to_sRGB(display, blue, + use_sBIT ? display->sBIT[2] : 8U); + if (use_sBIT) + alpha = update_for_sBIT(alpha, display->sBIT[3], 8U); encoding = P_sRGB; + use_sBIT = 0; } } else if (encoding == P_LINEAR8) { - /* This encoding occurs quite frequently in test cases because PngSuite - * includes a gAMA 1.0 chunk with most images. + /* This encoding corresponds to a colormap with linear RGB entries, this + * is not a very sensible encoding but it does happen with the PNGSuite + * test images. */ red *= 257; green *= 257; blue *= 257; alpha *= 257; + if (use_sBIT) + { + red = update_for_sBIT(red, display->sBIT[0], 16U); + green = update_for_sBIT(green, display->sBIT[1], 16U); + blue = update_for_sBIT(blue, display->sBIT[2], 16U); + alpha = update_for_sBIT(alpha, display->sBIT[3], 16U); + use_sBIT = 0; + } encoding = P_LINEAR; } - else if (encoding == P_sRGB && (convert_to_Y || output_encoding == P_LINEAR)) + else if (encoding == P_sRGB && + (convert_to_Y != 0 || output_encoding == P_LINEAR)) { /* The values are 8-bit sRGB values, but must be converted to 16-bit * linear. */ - red = png_sRGB_table[red]; - green = png_sRGB_table[green]; - blue = png_sRGB_table[blue]; - alpha *= 257; + if (use_sBIT) + { + + red = convert_to_linear(display, red, display->sBIT[0]); + green = convert_to_linear(display, green, display->sBIT[1]); + blue = convert_to_linear(display, blue, display->sBIT[2]); + alpha = update_for_sBIT(alpha * 257U, display->sBIT[3], 16U); + use_sBIT = 0; + } + + else + { + red = png_sRGB_table[red]; + green = png_sRGB_table[green]; + blue = png_sRGB_table[blue]; + alpha *= 257; + } + encoding = P_LINEAR; } + else if (encoding == P_sRGB && use_sBIT) + { + debug(output_encoding == P_sRGB); /* P_LINEAR handled above */ + red = update_for_sBIT(red, display->sBIT[0], 8U); + green = update_for_sBIT(green, display->sBIT[1], 8U); + blue = update_for_sBIT(blue, display->sBIT[2], 8U); + alpha = update_for_sBIT(alpha, display->sBIT[3], 8U); + use_sBIT = 0; + } + + debug(!use_sBIT); /* it should have been handled above */ + /* This is set if the color isn't gray but the output is. */ if (encoding == P_LINEAR) { if (convert_to_Y != 0) { /* NOTE: these values are copied from png_do_rgb_to_gray */ - png_uint_32 y = (png_uint_32)6968 * red + (png_uint_32)23434 * green + - (png_uint_32)2366 * blue; + png_uint_32 y = 6968 * red + 23434 * green + 2366 * blue; if (output_encoding == P_LINEAR) y = (y + 16384) >> 15; @@ -1807,7 +1807,8 @@ png_create_colormap_entry(png_image_read_control *display, /* y is scaled by 32768, we need it scaled by 255: */ y = (y + 128) >> 8; y *= 255; - y = PNG_sRGB_FROM_LINEAR((y + 64) >> 7); + y = PNG_sRGB_FROM_LINEAR(png_ptr, (y + 64) >> 7); + alpha = PNG_DIV257(alpha); encoding = P_sRGB; } @@ -1816,30 +1817,30 @@ png_create_colormap_entry(png_image_read_control *display, else if (output_encoding == P_sRGB) { - red = PNG_sRGB_FROM_LINEAR(red * 255); - green = PNG_sRGB_FROM_LINEAR(green * 255); - blue = PNG_sRGB_FROM_LINEAR(blue * 255); + red = PNG_sRGB_FROM_LINEAR(png_ptr, red * 255); + green = PNG_sRGB_FROM_LINEAR(png_ptr, green * 255); + blue = PNG_sRGB_FROM_LINEAR(png_ptr, blue * 255); alpha = PNG_DIV257(alpha); encoding = P_sRGB; } } if (encoding != output_encoding) - png_error(image->opaque->png_ptr, "bad encoding (internal error)"); + png_impossiblepp(png_ptr, "bad encoding"); /* Store the value. */ { -# ifdef PNG_FORMAT_AFIRST_SUPPORTED - const int afirst = (image->format & PNG_FORMAT_FLAG_AFIRST) != 0 && - (image->format & PNG_FORMAT_FLAG_ALPHA) != 0; -# else -# define afirst 0 -# endif -# ifdef PNG_FORMAT_BGR_SUPPORTED - const int bgr = (image->format & PNG_FORMAT_FLAG_BGR) ? 2 : 0; -# else -# define bgr 0 -# endif +# ifdef PNG_FORMAT_AFIRST_SUPPORTED + const int afirst = (image->format & PNG_FORMAT_FLAG_AFIRST) != 0 && + (image->format & PNG_FORMAT_FLAG_ALPHA) != 0; +# else +# define afirst 0 +# endif +# ifdef PNG_FORMAT_BGR_SUPPORTED + const int bgr = (image->format & PNG_FORMAT_FLAG_BGR) != 0 ? 2 : 0; +# else +# define bgr 0 +# endif if (output_encoding == P_LINEAR) { @@ -1854,7 +1855,7 @@ png_create_colormap_entry(png_image_read_control *display, switch (PNG_IMAGE_SAMPLE_CHANNELS(image->format)) { case 4: - entry[afirst ? 0 : 3] = (png_uint_16)alpha; + entry[afirst ? 0 : 3] = png_check_u16(png_ptr, alpha); /* FALL THROUGH */ case 3: @@ -1870,13 +1871,13 @@ png_create_colormap_entry(png_image_read_control *display, else red = green = blue = 0; } - entry[afirst + (2 ^ bgr)] = (png_uint_16)blue; - entry[afirst + 1] = (png_uint_16)green; - entry[afirst + bgr] = (png_uint_16)red; + entry[afirst + (2 ^ bgr)] = png_check_u16(png_ptr, blue); + entry[afirst + 1] = png_check_u16(png_ptr, green); + entry[afirst + bgr] = png_check_u16(png_ptr, red); break; case 2: - entry[1 ^ afirst] = (png_uint_16)alpha; + entry[1 ^ afirst] = png_check_u16(png_ptr, alpha); /* FALL THROUGH */ case 1: @@ -1888,7 +1889,7 @@ png_create_colormap_entry(png_image_read_control *display, else green = 0; } - entry[afirst] = (png_uint_16)green; + entry[afirst] = png_check_u16(png_ptr, green); break; default: @@ -1902,20 +1903,24 @@ png_create_colormap_entry(png_image_read_control *display, entry += ip * PNG_IMAGE_SAMPLE_CHANNELS(image->format); + png_affirmpp(png_ptr, output_encoding == P_sRGB); + switch (PNG_IMAGE_SAMPLE_CHANNELS(image->format)) { case 4: - entry[afirst ? 0 : 3] = (png_byte)alpha; + entry[afirst ? 0 : 3] = png_check_byte(png_ptr, alpha); + case 3: - entry[afirst + (2 ^ bgr)] = (png_byte)blue; - entry[afirst + 1] = (png_byte)green; - entry[afirst + bgr] = (png_byte)red; + entry[afirst + (2 ^ bgr)] = png_check_byte(png_ptr, blue); + entry[afirst + 1] = png_check_byte(png_ptr, green); + entry[afirst + bgr] = png_check_byte(png_ptr, red); break; case 2: - entry[1 ^ afirst] = (png_byte)alpha; + entry[1 ^ afirst] = png_check_byte(png_ptr, alpha); + case 1: - entry[afirst] = (png_byte)green; + entry[afirst] = png_check_byte(png_ptr, green); break; default: @@ -1923,13 +1928,15 @@ png_create_colormap_entry(png_image_read_control *display, } } -# ifdef afirst -# undef afirst -# endif -# ifdef bgr -# undef bgr -# endif +# ifdef afirst +# undef afirst +# endif +# ifdef bgr +# undef bgr +# endif } + +# undef png_ptr } static int @@ -1938,7 +1945,7 @@ make_gray_file_colormap(png_image_read_control *display) unsigned int i; for (i=0; i<256; ++i) - png_create_colormap_entry(display, i, i, i, i, 255, P_FILE); + png_create_colormap_entry(display, i, i, i, i, 255, P_FILE8); return i; } @@ -2002,7 +2009,7 @@ make_ga_colormap(png_image_read_control *display) for (g=0; g<6; ++g) png_create_colormap_entry(display, i++, g*51, g*51, g*51, a*51, - P_sRGB); + P_sRGB); } return i; @@ -2026,7 +2033,7 @@ make_rgb_colormap(png_image_read_control *display) for (b=0; b<6; ++b) png_create_colormap_entry(display, i++, r*51, g*51, b*51, 255, - P_sRGB); + P_sRGB); } } @@ -2037,7 +2044,8 @@ make_rgb_colormap(png_image_read_control *display) /* Return a palette index to the above palette given three 8-bit sRGB values. */ #define PNG_RGB_INDEX(r,g,b) \ - ((png_byte)(6 * (6 * PNG_DIV51(r) + PNG_DIV51(g)) + PNG_DIV51(b))) + (png_check_byte(image->opaque->png_ptr,\ + 6 * (6 * PNG_DIV51(r) + PNG_DIV51(g)) + PNG_DIV51(b))) static int png_image_read_colormap(png_voidp argument) @@ -2048,7 +2056,7 @@ png_image_read_colormap(png_voidp argument) const png_structrp png_ptr = image->opaque->png_ptr; const png_uint_32 output_format = image->format; - const int output_encoding = (output_format & PNG_FORMAT_FLAG_LINEAR) ? + const int output_encoding = (output_format & PNG_FORMAT_FLAG_LINEAR) != 0 ? P_LINEAR : P_sRGB; unsigned int cmap_entries; @@ -2078,7 +2086,7 @@ png_image_read_colormap(png_voidp argument) else if (display->background == NULL /* no way to remove it */) png_error(png_ptr, - "a background color must be supplied to remove alpha/transparency"); + "background color must be supplied to remove alpha/transparency"); /* Get a copy of the background color (this avoids repeating the checks * below.) The encoding is 8-bit sRGB or 16-bit linear, depending on the @@ -2087,7 +2095,7 @@ png_image_read_colormap(png_voidp argument) else { back_g = display->background->green; - if (output_format & PNG_FORMAT_FLAG_COLOR) + if ((output_format & PNG_FORMAT_FLAG_COLOR) != 0) { back_r = display->background->red; back_b = display->background->blue; @@ -2122,7 +2130,12 @@ png_image_read_colormap(png_voidp argument) else png_ptr->colorspace.gamma = PNG_GAMMA_sRGB_INVERSE; - png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; + /* Make sure libpng doesn't ignore the setting: */ + if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) + png_ptr->colorspace.flags = PNG_COLORSPACE_HAVE_GAMMA; + + else + png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; } /* Decide what to do based on the PNG color type of the input data. The @@ -2173,7 +2186,7 @@ png_image_read_colormap(png_voidp argument) */ if (i != trans) png_create_colormap_entry(display, i, val, val, val, 255, - P_FILE/*8-bit with file gamma*/); + P_FILE/*8-bit with file gamma*/); /* Else this entry is transparent. The colors don't matter if * there is an alpha channel (back_alpha == 0), but it does no @@ -2184,15 +2197,22 @@ png_image_read_colormap(png_voidp argument) * supplied background color when it is used. */ else + { +#ifdef __COVERITY__ + /* Coverity says back_r|g|b might be 16-bit values */ + png_affirmpp(png_ptr, back_r < 256 && back_g < 256 && + back_b < 256); +#endif png_create_colormap_entry(display, i, back_r, back_g, back_b, - back_alpha, output_encoding); + back_alpha, output_encoding); + } } /* We need libpng to preserve the original encoding. */ data_encoding = P_FILE; /* The rows from libpng, while technically gray values, are now also - * color-map indicies; however, they may need to be expanded to 1 + * color-map indices; however, they may need to be expanded to 1 * byte per pixel. This is what png_set_packing does (i.e., it * unpacks the bit values into bytes.) */ @@ -2229,7 +2249,7 @@ png_image_read_colormap(png_voidp argument) { unsigned int back_alpha; - if (output_format & PNG_FORMAT_FLAG_ALPHA) + if ((output_format & PNG_FORMAT_FLAG_ALPHA) != 0) back_alpha = 0; else @@ -2244,42 +2264,46 @@ png_image_read_colormap(png_voidp argument) if (output_encoding == P_LINEAR) { - gray = PNG_sRGB_FROM_LINEAR(gray * 255); + gray = PNG_sRGB_FROM_LINEAR(png_ptr, gray * 255); /* And make sure the corresponding palette entry * matches. */ png_create_colormap_entry(display, gray, back_g, back_g, - back_g, 65535, P_LINEAR); + back_g, 65535, P_LINEAR); } /* The background passed to libpng, however, must be the * sRGB value. */ c.index = 0; /*unused*/ - c.gray = c.red = c.green = c.blue = (png_uint_16)gray; + c.gray = c.red = c.green = c.blue = + png_check_u16(png_ptr, gray); /* NOTE: does this work without expanding tRNS to alpha? * It should be the color->gray case below apparently * doesn't. */ png_set_background_fixed(png_ptr, &c, - PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, - 0/*gamma: not used*/); + PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, + 0/*gamma: not used*/); output_processing = PNG_CMAP_NONE; break; } - - back_alpha = output_encoding == P_LINEAR ? 65535 : 255; + /* Coverity claims that output_encoding cannot be 2 (P_LINEAR) + * here. + */ + affirm(output_encoding != P_LINEAR); + back_alpha = 255U; } /* output_processing means that the libpng-processed row will be * 8-bit GA and it has to be processing to single byte color-map * values. Entry 254 is replaced by either a completely * transparent entry or by the background color at full - * precision (and the background color is not a simple gray leve - * in this case.) + * precision (and the background color is not a simple gray + * level in this case.) */ expand_tRNS = 1; output_processing = PNG_CMAP_TRANS; @@ -2288,8 +2312,13 @@ png_image_read_colormap(png_voidp argument) /* And set (overwrite) color-map entry 254 to the actual * background color at full precision. */ +#ifdef __COVERITY__ + /* Coverity says back_r|g|b might be 16-bit values */ + png_affirmpp(png_ptr, back_r < 256 && back_g < 256 && + back_b < 256); +#endif png_create_colormap_entry(display, 254, back_r, back_g, back_b, - back_alpha, output_encoding); + back_alpha, output_encoding); } else @@ -2310,7 +2339,7 @@ png_image_read_colormap(png_voidp argument) */ data_encoding = P_sRGB; - if (output_format & PNG_FORMAT_FLAG_ALPHA) + if ((output_format & PNG_FORMAT_FLAG_ALPHA) != 0) { if (PNG_GA_COLORMAP_ENTRIES > image->colormap_entries) png_error(png_ptr, "gray+alpha color-map: too few entries"); @@ -2353,22 +2382,22 @@ png_image_read_colormap(png_voidp argument) if (output_encoding == P_LINEAR) { - gray = PNG_sRGB_FROM_LINEAR(gray * 255); + gray = PNG_sRGB_FROM_LINEAR(png_ptr, gray * 255); /* And make sure the corresponding palette entry matches. */ png_create_colormap_entry(display, gray, back_g, back_g, - back_g, 65535, P_LINEAR); + back_g, 65535, P_LINEAR); } /* The background passed to libpng, however, must be the sRGB * value. */ c.index = 0; /*unused*/ - c.gray = c.red = c.green = c.blue = (png_uint_16)gray; + c.gray = c.red = c.green = c.blue = png_check_u16(png_ptr, gray); png_set_background_fixed(png_ptr, &c, - PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, - 0/*gamma: not used*/); + PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, + 0/*gamma: not used*/); output_processing = PNG_CMAP_NONE; } @@ -2388,15 +2417,18 @@ png_image_read_colormap(png_voidp argument) { png_uint_32 gray = (i * 256 + 115) / 231; png_create_colormap_entry(display, i++, gray, gray, gray, - 255, P_sRGB); + 255, P_sRGB); } /* NOTE: this preserves the full precision of the application * background color. + * + * Coverity claims that output_encoding cannot be 2 (P_LINEAR) */ + affirm(output_encoding != P_LINEAR); background_index = i; png_create_colormap_entry(display, i++, back_r, back_g, back_b, - output_encoding == P_LINEAR ? 65535U : 255U, output_encoding); + 255U, output_encoding); /* For non-opaque input composite on the sRGB background - this * requires inverting the encoding for each component. The input @@ -2434,9 +2466,10 @@ png_image_read_colormap(png_voidp argument) png_uint_32 gray = png_sRGB_table[g*51] * alpha; png_create_colormap_entry(display, i++, - PNG_sRGB_FROM_LINEAR(gray + back_rx), - PNG_sRGB_FROM_LINEAR(gray + back_gx), - PNG_sRGB_FROM_LINEAR(gray + back_bx), 255, P_sRGB); + PNG_sRGB_FROM_LINEAR(png_ptr, gray + back_rx), + PNG_sRGB_FROM_LINEAR(png_ptr, gray + back_gx), + PNG_sRGB_FROM_LINEAR(png_ptr, gray + back_bx), + 255, P_sRGB); } } @@ -2462,7 +2495,7 @@ png_image_read_colormap(png_voidp argument) * png_set_tRNS_to_alpha before png_set_background_fixed. */ png_set_rgb_to_gray_fixed(png_ptr, PNG_ERROR_ACTION_NONE, -1, - -1); + -1); data_encoding = P_sRGB; /* The output will now be one or two 8-bit gray or gray+alpha @@ -2505,7 +2538,7 @@ png_image_read_colormap(png_voidp argument) */ if ((png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA || png_ptr->num_trans > 0) && - png_gamma_not_sRGB(png_ptr->colorspace.gamma)) + png_gamma_not_sRGB(png_ptr->colorspace.gamma) != 0) { cmap_entries = make_gray_file_colormap(display); data_encoding = P_FILE; @@ -2537,8 +2570,8 @@ png_image_read_colormap(png_voidp argument) if (output_encoding == P_sRGB) gray = png_sRGB_table[gray]; /* now P_LINEAR */ - gray = PNG_DIV257(png_gamma_16bit_correct(gray, - png_ptr->colorspace.gamma)); /* now P_FILE */ + gray = png_gamma_nxmbit_correct(gray, + png_ptr->colorspace.gamma, 16U, 8U); /* And make sure the corresponding palette entry contains * exactly the required sRGB value. @@ -2549,7 +2582,7 @@ png_image_read_colormap(png_voidp argument) else if (output_encoding == P_LINEAR) { - gray = PNG_sRGB_FROM_LINEAR(gray * 255); + gray = PNG_sRGB_FROM_LINEAR(png_ptr, gray * 255); /* And make sure the corresponding palette entry matches. */ @@ -2561,7 +2594,8 @@ png_image_read_colormap(png_voidp argument) * output (normally sRGB) value. */ c.index = 0; /*unused*/ - c.gray = c.red = c.green = c.blue = (png_uint_16)gray; + c.gray = c.red = c.green = c.blue = + png_check_u16(png_ptr, gray); /* NOTE: the following is apparently a bug in libpng. Without * it the transparent color recognition in @@ -2569,8 +2603,8 @@ png_image_read_colormap(png_voidp argument) */ expand_tRNS = 1; png_set_background_fixed(png_ptr, &c, - PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, - 0/*gamma: not used*/); + PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, + 0/*gamma: not used*/); } output_processing = PNG_CMAP_NONE; @@ -2593,7 +2627,7 @@ png_image_read_colormap(png_voidp argument) /* Is there alpha in the output too? If so all four channels are * processed into a special RGB cube with alpha support. */ - if (output_format & PNG_FORMAT_FLAG_ALPHA) + if ((output_format & PNG_FORMAT_FLAG_ALPHA) != 0) { png_uint_32 r; @@ -2604,7 +2638,7 @@ png_image_read_colormap(png_voidp argument) /* Add a transparent entry. */ png_create_colormap_entry(display, cmap_entries, 255, 255, - 255, 0, P_sRGB); + 255, 0, P_sRGB); /* This is stored as the background index for the processing * algorithm. @@ -2625,7 +2659,7 @@ png_image_read_colormap(png_voidp argument) */ for (b=0; b<256; b = (b << 1) | 0x7f) png_create_colormap_entry(display, cmap_entries++, - r, g, b, 128, P_sRGB); + r, g, b, 128, P_sRGB); } } @@ -2652,13 +2686,13 @@ png_image_read_colormap(png_voidp argument) cmap_entries = make_rgb_colormap(display); png_create_colormap_entry(display, cmap_entries, back_r, - back_g, back_b, 0/*unused*/, output_encoding); + back_g, back_b, 0/*unused*/, output_encoding); if (output_encoding == P_LINEAR) { - r = PNG_sRGB_FROM_LINEAR(back_r * 255); - g = PNG_sRGB_FROM_LINEAR(back_g * 255); - b = PNG_sRGB_FROM_LINEAR(back_b * 255); + r = PNG_sRGB_FROM_LINEAR(png_ptr, back_r * 255); + g = PNG_sRGB_FROM_LINEAR(png_ptr, back_g * 255); + b = PNG_sRGB_FROM_LINEAR(png_ptr, back_b * 255); } else @@ -2674,9 +2708,9 @@ png_image_read_colormap(png_voidp argument) * index. */ if (memcmp((png_const_bytep)display->colormap + - sample_size * cmap_entries, - (png_const_bytep)display->colormap + - sample_size * PNG_RGB_INDEX(r,g,b), + sample_size * cmap_entries, + (png_const_bytep)display->colormap + + sample_size * PNG_RGB_INDEX(r,g,b), sample_size) != 0) { /* The background color must be added. */ @@ -2694,12 +2728,12 @@ png_image_read_colormap(png_voidp argument) */ for (b=0; b<256; b = (b << 1) | 0x7f) png_create_colormap_entry(display, cmap_entries++, - png_colormap_compose(display, r, P_sRGB, 128, - back_r, output_encoding), - png_colormap_compose(display, g, P_sRGB, 128, - back_g, output_encoding), - png_colormap_compose(display, b, P_sRGB, 128, - back_b, output_encoding), + png_colormap_compose(display, r, 8U, P_sRGB, + 128U, back_r, output_encoding), + png_colormap_compose(display, g, 8U, P_sRGB, + 128U, back_g, output_encoding), + png_colormap_compose(display, b, 8U, P_sRGB, + 128U, back_b, output_encoding), 0/*unused*/, output_encoding); } } @@ -2713,13 +2747,13 @@ png_image_read_colormap(png_voidp argument) png_color_16 c; c.index = 0; /*unused*/ - c.red = (png_uint_16)back_r; - c.gray = c.green = (png_uint_16)back_g; - c.blue = (png_uint_16)back_b; + c.red = png_check_u16(png_ptr, back_r); + c.gray = c.green = png_check_u16(png_ptr, back_g); + c.blue = png_check_u16(png_ptr, back_b); png_set_background_fixed(png_ptr, &c, - PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, - 0/*gamma: not used*/); + PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, + 0/*gamma: not used*/); output_processing = PNG_CMAP_RGB; } @@ -2757,7 +2791,7 @@ png_image_read_colormap(png_voidp argument) num_trans = 0; output_processing = PNG_CMAP_NONE; - data_encoding = P_FILE; /* Don't change from color-map indicies */ + data_encoding = P_FILE; /* Don't change from color-map indices */ cmap_entries = png_ptr->num_palette; if (cmap_entries > 256) cmap_entries = 256; @@ -2767,25 +2801,39 @@ png_image_read_colormap(png_voidp argument) for (i=0; i < cmap_entries; ++i) { - if (do_background && i < num_trans && trans[i] < 255) + if (do_background != 0 && i < num_trans && trans[i] < 255) { if (trans[i] == 0) png_create_colormap_entry(display, i, back_r, back_g, - back_b, 0, output_encoding); + back_b, 0, output_encoding); else { + unsigned int alpha; + /* Must compose the PNG file color in the color-map entry * on the sRGB color in 'back'. */ + png_image_get_sBIT(display); + alpha = update_for_sBIT(trans[i], display->sBIT[3], 8U); + + /* Do the sBIT handling here because it only applies to the + * values from the colormap, not the background. Passing + * output_encoding to png_create_colormap_entry prevents + * this being duplicated. + */ png_create_colormap_entry(display, i, - png_colormap_compose(display, colormap[i].red, P_FILE, - trans[i], back_r, output_encoding), - png_colormap_compose(display, colormap[i].green, P_FILE, - trans[i], back_g, output_encoding), - png_colormap_compose(display, colormap[i].blue, P_FILE, - trans[i], back_b, output_encoding), - output_encoding == P_LINEAR ? trans[i] * 257U : + png_colormap_compose(display, colormap[i].red, + display->sBIT[0], P_FILE, alpha, back_r, + output_encoding), + png_colormap_compose(display, colormap[i].green, + display->sBIT[1], P_FILE, alpha, back_g, + output_encoding), + png_colormap_compose(display, colormap[i].blue, + display->sBIT[2], P_FILE, alpha, back_b, + output_encoding), + output_encoding == P_LINEAR ? + update_for_sBIT(alpha*257U, display->sBIT[3], 16U) : trans[i], output_encoding); } @@ -2797,7 +2845,7 @@ png_image_read_colormap(png_voidp argument) i < num_trans ? trans[i] : 255U, P_FILE/*8-bit*/); } - /* The PNG data may have indicies packed in fewer than 8 bits, it + /* The PNG data may have indices packed in fewer than 8 bits, it * must be expanded if so. */ if (png_ptr->bit_depth < 8) @@ -2808,18 +2856,17 @@ png_image_read_colormap(png_voidp argument) default: png_error(png_ptr, "invalid PNG color type"); /*NOT REACHED*/ - break; } /* Now deal with the output processing */ - if (expand_tRNS && png_ptr->num_trans > 0 && - (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) == 0) + if (expand_tRNS != 0 && png_ptr->num_trans > 0 && + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) == 0) png_set_tRNS_to_alpha(png_ptr); switch (data_encoding) { default: - png_error(png_ptr, "bad data option (internal error)"); + impossible("bad data option"); break; case P_sRGB: @@ -2833,8 +2880,7 @@ png_image_read_colormap(png_voidp argument) break; } - if (cmap_entries > 256 || cmap_entries > image->colormap_entries) - png_error(png_ptr, "color map overflow (BAD internal error)"); + affirm(cmap_entries <= 256 && cmap_entries <= image->colormap_entries); image->colormap_entries = cmap_entries; @@ -2868,10 +2914,10 @@ png_image_read_colormap(png_voidp argument) break; default: - png_error(png_ptr, "bad processing option (internal error)"); + impossible("bad processing option"); bad_background: - png_error(png_ptr, "bad background index (internal error)"); + impossible("bad background index"); } display->colormap_processing = output_processing; @@ -2884,7 +2930,7 @@ static int png_image_read_and_map(png_voidp argument) { png_image_read_control *display = png_voidcast(png_image_read_control*, - argument); + argument); png_imagep image = display->image; png_structrp png_ptr = image->opaque->png_ptr; int passes; @@ -2980,7 +3026,7 @@ png_image_read_and_map(png_voidp argument) entry = 226 + 6 * PNG_DIV51(alpha) + PNG_DIV51(gray); } - *outrow = (png_byte)entry; + *outrow = png_check_byte(png_ptr, entry); } break; @@ -2997,7 +3043,7 @@ png_image_read_and_map(png_voidp argument) *outrow = gray; else - *outrow = (png_byte)(PNG_CMAP_TRANS_BACKGROUND+1); + *outrow = PNG_CMAP_TRANS_BACKGROUND+1; } break; @@ -3021,7 +3067,7 @@ png_image_read_and_map(png_voidp argument) if (alpha >= 196) *outrow = PNG_RGB_INDEX(inrow[0], inrow[1], - inrow[2]); + inrow[2]); else if (alpha < 64) *outrow = PNG_CMAP_RGB_ALPHA_BACKGROUND; @@ -3052,7 +3098,7 @@ png_image_read_and_map(png_voidp argument) if (inrow[0] & 0x80) back_i += 1; /* blue */ if (inrow[0] & 0x40) back_i += 1; - *outrow = (png_byte)back_i; + *outrow = png_check_byte(png_ptr, back_i); } inrow += 4; @@ -3073,12 +3119,12 @@ static int png_image_read_colormapped(png_voidp argument) { png_image_read_control *display = png_voidcast(png_image_read_control*, - argument); + argument); png_imagep image = display->image; png_controlp control = image->opaque; png_structrp png_ptr = control->png_ptr; png_inforp info_ptr = control->info_ptr; - + int color_type, bit_depth; int passes = 0; /* As a flag */ PNG_SKIP_CHUNKS(png_ptr); @@ -3092,6 +3138,12 @@ png_image_read_colormapped(png_voidp argument) png_read_update_info(png_ptr, info_ptr); + /* Avoid the 'easy access' functions below because this allows them to be + * disabled; there are not useful with the simplified API. + */ + color_type = PNG_COLOR_TYPE_FROM_FORMAT(info_ptr->format); + bit_depth = info_ptr->bit_depth; + /* The expected output can be deduced from the colormap_processing option. */ switch (display->colormap_processing) { @@ -3099,9 +3151,8 @@ png_image_read_colormapped(png_voidp argument) /* Output must be one channel and one byte per pixel, the output * encoding can be anything. */ - if ((info_ptr->color_type == PNG_COLOR_TYPE_PALETTE || - info_ptr->color_type == PNG_COLOR_TYPE_GRAY) && - info_ptr->bit_depth == 8) + if ((color_type == PNG_COLOR_TYPE_PALETTE || + color_type == PNG_COLOR_TYPE_GRAY) && bit_depth == 8) break; goto bad_output; @@ -3112,30 +3163,30 @@ png_image_read_colormapped(png_voidp argument) * can be checked with an exact number because it should have been set * to this number above! */ - if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && - info_ptr->bit_depth == 8 && - png_ptr->screen_gamma == PNG_GAMMA_sRGB && - image->colormap_entries == 256) + if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA && bit_depth == 8 && + !png_need_gamma_correction(png_ptr, png_memory_gamma(png_ptr), + 1/*sRGB*/) && + image->colormap_entries == 256) break; goto bad_output; case PNG_CMAP_RGB: /* Output must be 8-bit sRGB encoded RGB */ - if (info_ptr->color_type == PNG_COLOR_TYPE_RGB && - info_ptr->bit_depth == 8 && - png_ptr->screen_gamma == PNG_GAMMA_sRGB && - image->colormap_entries == 216) + if (color_type == PNG_COLOR_TYPE_RGB && bit_depth == 8 && + !png_need_gamma_correction(png_ptr, png_memory_gamma(png_ptr), + 1/*sRGB*/) && + image->colormap_entries == 216) break; goto bad_output; case PNG_CMAP_RGB_ALPHA: /* Output must be 8-bit sRGB encoded RGBA */ - if (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA && - info_ptr->bit_depth == 8 && - png_ptr->screen_gamma == PNG_GAMMA_sRGB && - image->colormap_entries == 244 /* 216 + 1 + 27 */) + if (color_type == PNG_COLOR_TYPE_RGB_ALPHA && bit_depth == 8 && + !png_need_gamma_correction(png_ptr, png_memory_gamma(png_ptr), + 1/*sRGB*/) && + image->colormap_entries == 244 /* 216 + 1 + 27 */) break; /* goto bad_output; */ @@ -3143,7 +3194,7 @@ png_image_read_colormapped(png_voidp argument) default: bad_output: - png_error(png_ptr, "bad color-map processing (internal error)"); + impossible("bad color-map processing"); } /* Now read the rows. Do this here if it is possible to read directly into @@ -3206,7 +3257,7 @@ static int png_image_read_composite(png_voidp argument) { png_image_read_control *display = png_voidcast(png_image_read_control*, - argument); + argument); png_imagep image = display->image; png_structrp png_ptr = image->opaque->png_ptr; int passes; @@ -3229,7 +3280,8 @@ png_image_read_composite(png_voidp argument) png_uint_32 height = image->height; png_uint_32 width = image->width; ptrdiff_t step_row = display->row_bytes; - unsigned int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1; + unsigned int channels = + (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ? 3 : 1; int pass; for (pass = 0; pass < passes; ++pass) @@ -3299,10 +3351,11 @@ png_image_read_composite(png_voidp argument) * therefore appropriate for the sRGB to linear * conversion table. */ - component = PNG_sRGB_FROM_LINEAR(component); + component = + PNG_sRGB_FROM_LINEAR(png_ptr, component); } - outrow[c] = (png_byte)component; + outrow[c] = png_check_byte(png_ptr, component); } } @@ -3318,21 +3371,21 @@ png_image_read_composite(png_voidp argument) /* The do_local_background case; called when all the following transforms are to * be done: * - * PNG_RGB_TO_GRAY - * PNG_COMPOSITE - * PNG_GAMMA + * PNG_READ_RGB_TO_GRAY + * PNG_READ_COMPOSITE + * PNG_READ_GAMMA * - * This is a work-round for the fact that both the PNG_RGB_TO_GRAY and + * This is a work-around for the fact that both the PNG_READ_RGB_TO_GRAY and * PNG_COMPOSITE code performs gamma correction, so we get double gamma - * correction. The fix-up is to prevent the PNG_COMPOSITE operation happening - * inside libpng, so this routine sees an 8 or 16-bit gray+alpha row and handles - * the removal or pre-multiplication of the alpha channel. + * correction. The fix-up is to prevent the PNG_COMPOSITE operation from + * happening inside libpng, so this routine sees an 8 or 16-bit gray+alpha + * row and handles the removal or pre-multiplication of the alpha channel. */ static int png_image_read_background(png_voidp argument) { png_image_read_control *display = png_voidcast(png_image_read_control*, - argument); + argument); png_imagep image = display->image; png_structrp png_ptr = image->opaque->png_ptr; png_inforp info_ptr = image->opaque->info_ptr; @@ -3345,14 +3398,9 @@ png_image_read_background(png_voidp argument) * left to the png_image_read_background function. The rows libpng produce * might be 8 or 16-bit but should always have two channels; gray plus alpha. */ - if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == 0) - png_error(png_ptr, "lost rgb to gray"); - - if ((png_ptr->transformations & PNG_COMPOSE) != 0) - png_error(png_ptr, "unexpected compose"); - - if (png_get_channels(png_ptr, info_ptr) != 2) - png_error(png_ptr, "lost/gained channels"); + affirm(PNG_COLOR_TYPE_FROM_FORMAT(info_ptr->format) == + PNG_COLOR_TYPE_GRAY_ALPHA); + debug(png_get_channels(png_ptr, info_ptr) == 2); /* Expect the 8-bit case to always remove the alpha channel */ if ((image->format & PNG_FORMAT_FLAG_LINEAR) == 0 && @@ -3396,8 +3444,7 @@ png_image_read_background(png_voidp argument) for (pass = 0; pass < passes; ++pass) { - png_bytep row = png_voidcast(png_bytep, - display->first_row); + png_bytep row = png_voidcast(png_bytep, display->first_row); unsigned int startx, stepx, stepy; png_uint_32 y; @@ -3425,7 +3472,7 @@ png_image_read_background(png_voidp argument) for (; ylocal_row); + display->local_row); png_bytep outrow = first_row + y * step_row; png_const_bytep end_row = outrow + width; @@ -3451,10 +3498,11 @@ png_image_read_background(png_voidp argument) component = png_sRGB_table[component] * alpha; component += png_sRGB_table[outrow[0]] * (255-alpha); - component = PNG_sRGB_FROM_LINEAR(component); + component = + PNG_sRGB_FROM_LINEAR(png_ptr, component); } - outrow[0] = (png_byte)component; + outrow[0] = png_check_byte(png_ptr, component); } inrow += 2; /* gray and alpha channel */ @@ -3470,7 +3518,7 @@ png_image_read_background(png_voidp argument) for (; ylocal_row); + display->local_row); png_bytep outrow = first_row + y * step_row; png_const_bytep end_row = outrow + width; @@ -3491,10 +3539,11 @@ png_image_read_background(png_voidp argument) { component = png_sRGB_table[component] * alpha; component += background * (255-alpha); - component = PNG_sRGB_FROM_LINEAR(component); + component = + PNG_sRGB_FROM_LINEAR(png_ptr, component); } - outrow[0] = (png_byte)component; + outrow[0] = png_check_byte(png_ptr, component); } else @@ -3517,7 +3566,7 @@ png_image_read_background(png_voidp argument) */ { png_uint_16p first_row = png_voidcast(png_uint_16p, - display->first_row); + display->first_row); /* The division by two is safe because the caller passed in a * stride which was multiplied by 2 (below) to get row_bytes. */ @@ -3526,10 +3575,11 @@ png_image_read_background(png_voidp argument) unsigned int outchannels = 1+preserve_alpha; int swap_alpha = 0; -# ifdef PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED - if (preserve_alpha && (image->format & PNG_FORMAT_FLAG_AFIRST)) - swap_alpha = 1; -# endif +#ifdef PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED + if (preserve_alpha != 0 && + (image->format & PNG_FORMAT_FLAG_AFIRST) != 0) + swap_alpha = 1; +#endif for (pass = 0; pass < passes; ++pass) { @@ -3566,7 +3616,7 @@ png_image_read_background(png_voidp argument) /* Read the row, which is packed: */ png_read_row(png_ptr, png_voidcast(png_bytep, - display->local_row), NULL); + display->local_row), NULL); inrow = png_voidcast(png_const_uint_16p, display->local_row); /* Now do the pre-multiplication on each pixel in this row. @@ -3590,7 +3640,8 @@ png_image_read_background(png_voidp argument) else component = 0; - outrow[swap_alpha] = (png_uint_16)component; + outrow[swap_alpha] = + png_check_u16(png_ptr, component); if (preserve_alpha != 0) outrow[1 ^ swap_alpha] = alpha; @@ -3610,7 +3661,7 @@ static int png_image_read_direct(png_voidp argument) { png_image_read_control *display = png_voidcast(png_image_read_control*, - argument); + argument); png_imagep image = display->image; png_structrp png_ptr = image->opaque->png_ptr; png_inforp info_ptr = image->opaque->info_ptr; @@ -3630,16 +3681,16 @@ png_image_read_direct(png_voidp argument) /* Now check the format to see if it was modified. */ { png_uint_32 base_format = png_image_format(png_ptr) & - ~PNG_FORMAT_FLAG_COLORMAP /* removed by png_set_expand */; + PNG_BIC_MASK(PNG_FORMAT_FLAG_COLORMAP) /* removed by png_set_expand */; png_uint_32 change = format ^ base_format; png_fixed_point output_gamma; int mode; /* alpha mode */ /* Do this first so that we have a record if rgb to gray is happening. */ - if (change & PNG_FORMAT_FLAG_COLOR) + if ((change & PNG_FORMAT_FLAG_COLOR) != 0) { /* gray<->color transformation required. */ - if (format & PNG_FORMAT_FLAG_COLOR) + if ((format & PNG_FORMAT_FLAG_COLOR) != 0) png_set_gray_to_rgb(png_ptr); else @@ -3657,14 +3708,14 @@ png_image_read_direct(png_voidp argument) * enormous change) 'do_local_background' is used to indicate that * the problem exists. */ - if (base_format & PNG_FORMAT_FLAG_ALPHA) + if ((base_format & PNG_FORMAT_FLAG_ALPHA) != 0) do_local_background = 1/*maybe*/; png_set_rgb_to_gray_fixed(png_ptr, PNG_ERROR_ACTION_NONE, - PNG_RGB_TO_GRAY_DEFAULT, PNG_RGB_TO_GRAY_DEFAULT); + PNG_RGB_TO_GRAY_DEFAULT, PNG_RGB_TO_GRAY_DEFAULT); } - change &= ~PNG_FORMAT_FLAG_COLOR; + change &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR); } /* Set the gamma appropriately, linear for 16-bit input, sRGB otherwise. @@ -3672,8 +3723,8 @@ png_image_read_direct(png_voidp argument) { png_fixed_point input_gamma_default; - if ((base_format & PNG_FORMAT_FLAG_LINEAR) && - (image->flags & PNG_IMAGE_FLAG_16BIT_sRGB) == 0) + if ((base_format & PNG_FORMAT_FLAG_LINEAR) != 0 && + (image->flags & PNG_IMAGE_FLAG_16BIT_sRGB) == 0) input_gamma_default = PNG_GAMMA_LINEAR; else input_gamma_default = PNG_DEFAULT_sRGB; @@ -3689,7 +3740,7 @@ png_image_read_direct(png_voidp argument) /* If there *is* an alpha channel in the input it must be multiplied * out; use PNG_ALPHA_STANDARD, otherwise just use PNG_ALPHA_PNG. */ - if (base_format & PNG_FORMAT_FLAG_ALPHA) + if ((base_format & PNG_FORMAT_FLAG_ALPHA) != 0) mode = PNG_ALPHA_STANDARD; /* associated alpha */ else @@ -3712,15 +3763,12 @@ png_image_read_direct(png_voidp argument) */ if (do_local_background != 0) { - png_fixed_point gtest; - - /* This is 'png_gamma_threshold' from pngrtran.c; the test used for - * gamma correction, the screen gamma hasn't been set on png_struct - * yet; it's set below. png_struct::gamma, however, is set to the - * final value. + /* This is intended to be a safe check to see if libpng will perform + * gamma work in pngrtran.c; if it will *not* be performed the + * do_local_background flag is cancelled. */ - if (png_muldiv(>est, output_gamma, png_ptr->colorspace.gamma, - PNG_FP_1) && !png_gamma_significant(gtest)) + if (!png_need_gamma_correction(png_ptr, 0/*PNG gamma*/, + output_gamma != PNG_GAMMA_LINEAR)) do_local_background = 0; else if (mode == PNG_ALPHA_STANDARD) @@ -3733,25 +3781,25 @@ png_image_read_direct(png_voidp argument) } /* If the bit-depth changes then handle that here. */ - if (change & PNG_FORMAT_FLAG_LINEAR) + if ((change & PNG_FORMAT_FLAG_LINEAR) != 0) { - if (linear /*16-bit output*/) + if (linear != 0 /*16-bit output*/) png_set_expand_16(png_ptr); else /* 8-bit output */ png_set_scale_16(png_ptr); - change &= ~PNG_FORMAT_FLAG_LINEAR; + change &= PNG_BIC_MASK(PNG_FORMAT_FLAG_LINEAR); } /* Now the background/alpha channel changes. */ - if (change & PNG_FORMAT_FLAG_ALPHA) + if ((change & PNG_FORMAT_FLAG_ALPHA) != 0) { /* Removing an alpha channel requires composition for the 8-bit * formats; for the 16-bit it is already done, above, by the * pre-multiplication and the channel just needs to be stripped. */ - if (base_format & PNG_FORMAT_FLAG_ALPHA) + if ((base_format & PNG_FORMAT_FLAG_ALPHA) != 0) { /* If RGB->gray is happening the alpha channel must be left and the * operation completed locally. @@ -3783,8 +3831,8 @@ png_image_read_direct(png_voidp argument) * pixels. */ png_set_background_fixed(png_ptr, &c, - PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, - 0/*gamma: not used*/); + PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, + 0/*gamma: not used*/); } else /* compose on row: implemented below. */ @@ -3815,22 +3863,22 @@ png_image_read_direct(png_voidp argument) else filler = 255; -# ifdef PNG_FORMAT_AFIRST_SUPPORTED - if (format & PNG_FORMAT_FLAG_AFIRST) - { - where = PNG_FILLER_BEFORE; - change &= ~PNG_FORMAT_FLAG_AFIRST; - } +#ifdef PNG_FORMAT_AFIRST_SUPPORTED + if ((format & PNG_FORMAT_FLAG_AFIRST) != 0) + { + where = PNG_FILLER_BEFORE; + change &= PNG_BIC_MASK(PNG_FORMAT_FLAG_AFIRST); + } - else -# endif + else +#endif where = PNG_FILLER_AFTER; png_set_add_alpha(png_ptr, filler, where); } /* This stops the (irrelevant) call to swap_alpha below. */ - change &= ~PNG_FORMAT_FLAG_ALPHA; + change &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA); } /* Now set the alpha mode correctly; this is always done, even if there is @@ -3839,45 +3887,45 @@ png_image_read_direct(png_voidp argument) */ png_set_alpha_mode_fixed(png_ptr, mode, output_gamma); -# ifdef PNG_FORMAT_BGR_SUPPORTED - if (change & PNG_FORMAT_FLAG_BGR) +# ifdef PNG_FORMAT_BGR_SUPPORTED + if ((change & PNG_FORMAT_FLAG_BGR) != 0) + { + /* Check only the output format; PNG is never BGR; don't do this if + * the output is gray, but fix up the 'format' value in that case. + */ + if ((format & PNG_FORMAT_FLAG_COLOR) != 0) + png_set_bgr(png_ptr); + + else + format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_BGR); + + change &= PNG_BIC_MASK(PNG_FORMAT_FLAG_BGR); + } +# endif + +# ifdef PNG_FORMAT_AFIRST_SUPPORTED + if ((change & PNG_FORMAT_FLAG_AFIRST) != 0) + { + /* Only relevant if there is an alpha channel - it's particularly + * important to handle this correctly because do_local_compose may + * be set above and then libpng will keep the alpha channel for this + * code to remove. + */ + if ((format & PNG_FORMAT_FLAG_ALPHA) != 0) { - /* Check only the output format; PNG is never BGR; don't do this if - * the output is gray, but fix up the 'format' value in that case. + /* Disable this if doing a local background, + * TODO: remove this when local background is no longer required. */ - if (format & PNG_FORMAT_FLAG_COLOR) - png_set_bgr(png_ptr); - - else - format &= ~PNG_FORMAT_FLAG_BGR; - - change &= ~PNG_FORMAT_FLAG_BGR; + if (do_local_background != 2) + png_set_swap_alpha(png_ptr); } -# endif -# ifdef PNG_FORMAT_AFIRST_SUPPORTED - if (change & PNG_FORMAT_FLAG_AFIRST) - { - /* Only relevant if there is an alpha channel - it's particularly - * important to handle this correctly because do_local_compose may - * be set above and then libpng will keep the alpha channel for this - * code to remove. - */ - if (format & PNG_FORMAT_FLAG_ALPHA) - { - /* Disable this if doing a local background, - * TODO: remove this when local background is no longer required. - */ - if (do_local_background != 2) - png_set_swap_alpha(png_ptr); - } + else + format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_AFIRST); - else - format &= ~PNG_FORMAT_FLAG_AFIRST; - - change &= ~PNG_FORMAT_FLAG_AFIRST; - } -# endif + change &= PNG_BIC_MASK(PNG_FORMAT_FLAG_AFIRST); + } +# endif /* If the *output* is 16-bit then we need to check for a byte-swap on this * architecture. @@ -3886,7 +3934,7 @@ png_image_read_direct(png_voidp argument) { PNG_CONST png_uint_16 le = 0x0001; - if (*(png_const_bytep)&le) + if ((*(png_const_bytep) & le) != 0) png_set_swap(png_ptr); } @@ -3909,55 +3957,47 @@ png_image_read_direct(png_voidp argument) png_read_update_info(png_ptr, info_ptr); { - png_uint_32 info_format = 0; + png_uint_32 out_format = png_memory_format(png_ptr); - if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) - info_format |= PNG_FORMAT_FLAG_COLOR; + /* Swapping is expected for the 16-bit format: */ + out_format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_SWAPPED); - if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + /* The remaining upper bits should never be set: */ + affirm(!(out_format & ~0x3FU)); + + if ((out_format & PNG_FORMAT_FLAG_ALPHA) != 0) { /* do_local_compose removes this channel below. */ - if (do_local_compose == 0) - { - /* do_local_background does the same if required. */ - if (do_local_background != 2 || - (format & PNG_FORMAT_FLAG_ALPHA) != 0) - info_format |= PNG_FORMAT_FLAG_ALPHA; - } + if (do_local_compose != 0 || + /* do_local_background does the same if required. */ + (do_local_background == 2 && + (format & PNG_FORMAT_FLAG_ALPHA) == 0)) + out_format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA); } - else if (do_local_compose != 0) /* internal error */ - png_error(png_ptr, "png_image_read: alpha channel lost"); + else + affirm(do_local_compose == 0 /* else alpha channel lost */); - if (info_ptr->bit_depth == 16) - info_format |= PNG_FORMAT_FLAG_LINEAR; + switch (png_memory_channel_depth(png_ptr)) + { + case 16: affirm((out_format & PNG_FORMAT_FLAG_LINEAR) != 0); break; + case 8: affirm((out_format & PNG_FORMAT_FLAG_LINEAR) == 0); break; + default: impossible("unexpected bit depth"); break; + } -# ifdef PNG_FORMAT_BGR_SUPPORTED - if (png_ptr->transformations & PNG_BGR) - info_format |= PNG_FORMAT_FLAG_BGR; -# endif +# ifdef PNG_FORMAT_AFIRST_SUPPORTED + if (do_local_background == 2) + { + /* do_local_background should be handling the swap: */ + affirm(!(out_format & PNG_FORMAT_FLAG_AFIRST)); -# ifdef PNG_FORMAT_AFIRST_SUPPORTED - if (do_local_background == 2) - { - if (format & PNG_FORMAT_FLAG_AFIRST) - info_format |= PNG_FORMAT_FLAG_AFIRST; - } - - if ((png_ptr->transformations & PNG_SWAP_ALPHA) != 0 || - ((png_ptr->transformations & PNG_ADD_ALPHA) != 0 && - (png_ptr->flags & PNG_FLAG_FILLER_AFTER) == 0)) - { - if (do_local_background == 2) - png_error(png_ptr, "unexpected alpha swap transformation"); - - info_format |= PNG_FORMAT_FLAG_AFIRST; - } -# endif + if ((format & PNG_FORMAT_FLAG_AFIRST) != 0) + out_format |= PNG_FORMAT_FLAG_AFIRST; + } +# endif /* This is actually an internal error. */ - if (info_format != format) - png_error(png_ptr, "png_read_image: invalid transformations"); + affirm(out_format == format /* else unimplemented transformations */); } /* Now read the rows. If do_local_compose is set then it is necessary to use @@ -4034,70 +4074,112 @@ png_image_read_direct(png_voidp argument) int PNGAPI png_image_finish_read(png_imagep image, png_const_colorp background, - void *buffer, png_int_32 row_stride, void *colormap) + void *buffer, ptrdiff_t row_stride, void *colormap) { if (image != NULL && image->version == PNG_IMAGE_VERSION) { - png_uint_32 check; + /* Check for row_stride overflow. This check is not performed on the + * original PNG format because it may not occur in the output PNG format + * and libpng deals with the issues of reading the original. + */ + const unsigned int channels = PNG_IMAGE_PIXEL_CHANNELS(image->format); - if (row_stride == 0) - row_stride = PNG_IMAGE_ROW_STRIDE(*image); - - if (row_stride < 0) - check = -row_stride; - - else - check = row_stride; - - if (image->opaque != NULL && buffer != NULL && - check >= PNG_IMAGE_ROW_STRIDE(*image)) + /* The test is slightly evil: it assumes that a signed pointer difference + * (ptrdiff_t) can hold a maximum value of half, rounded down, of the + * maximum of a (size_t). This is almost certain to be true. + */ + if (image->width <= (PNG_SIZE_MAX >> 1)/channels) /* no overflow */ { - if ((image->format & PNG_FORMAT_FLAG_COLORMAP) == 0 || - (image->colormap_entries > 0 && colormap != NULL)) + png_alloc_size_t check; + const png_alloc_size_t png_row_stride = + (png_alloc_size_t)/*SAFE*/image->width * channels; + + if (row_stride == 0) + row_stride = (ptrdiff_t)png_row_stride; + + if (row_stride < 0) + check = -row_stride; + + else + check = row_stride; + + if (image->opaque != NULL && buffer != NULL && check >= png_row_stride) { - int result; - png_image_read_control display; - - memset(&display, 0, (sizeof display)); - display.image = image; - display.buffer = buffer; - display.row_stride = row_stride; - display.colormap = colormap; - display.background = background; - display.local_row = NULL; - - /* Choose the correct 'end' routine; for the color-map case all the - * setup has already been done. + /* Now check for overflow of the image buffer calculation; this + * limits the whole image size to PNG_SIZE_MAX bytes. + * + * The PNG_IMAGE_BUFFER_SIZE macro is: + * + * (PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)*height*(row_stride)) + * + * We have no way of guaranteeing that the application used the + * correct type for 'row_stride' if it used the macro, so this is + * technically not completely safe, but this is the case throughout + * libpng; the app is responsible for making sure the calcualtion of + * buffer sizes does not overflow. */ - if (image->format & PNG_FORMAT_FLAG_COLORMAP) - result = - png_safe_execute(image, png_image_read_colormap, &display) && - png_safe_execute(image, png_image_read_colormapped, &display); + if (image->height <= PNG_SIZE_MAX / + PNG_IMAGE_PIXEL_COMPONENT_SIZE(image->format) / check) + { + if ((image->format & PNG_FORMAT_FLAG_COLORMAP) == 0 || + (image->colormap_entries > 0 && colormap != NULL)) + { + int result; + png_image_read_control display; + + memset(&display, 0, (sizeof display)); + display.image = image; + display.buffer = buffer; + display.row_stride = row_stride; + display.colormap = colormap; + display.background = background; + display.local_row = NULL; + + /* Choose the correct 'end' routine; for the color-map case + * all the setup has already been done. + */ + if ((image->format & PNG_FORMAT_FLAG_COLORMAP) != 0) + result = + png_safe_execute(image, + png_image_read_colormap, &display) && + png_safe_execute(image, + png_image_read_colormapped, &display); + + else + result = + png_safe_execute(image, + png_image_read_direct, &display); + + png_image_free(image); + return result; + } + + else + return png_image_error(image, + "png_image_finish_read[color-map]: no color-map"); + } else - result = - png_safe_execute(image, png_image_read_direct, &display); - - png_image_free(image); - return result; + return png_image_error(image, + "png_image_finish_read: image too large"); } else return png_image_error(image, - "png_image_finish_read[color-map]: no color-map"); + "png_image_finish_read: invalid argument"); } else return png_image_error(image, - "png_image_finish_read: invalid argument"); + "png_image_finish_read: row_stride too large"); } else if (image != NULL) return png_image_error(image, - "png_image_finish_read: damaged PNG_IMAGE_VERSION"); + "png_image_finish_read: damaged PNG_IMAGE_VERSION"); return 0; } -#endif /* PNG_SIMPLIFIED_READ_SUPPORTED */ -#endif /* PNG_READ_SUPPORTED */ +#endif /* SIMPLIFIED_READ */ +#endif /* READ */ diff --git a/ext/libpng17/pngrio.c b/ext/libpng17/pngrio.c index d75ac5b4dd..b77f827405 100644 --- a/ext/libpng17/pngrio.c +++ b/ext/libpng17/pngrio.c @@ -1,8 +1,8 @@ /* pngrio.c - functions for data input * - * Last changed in libpng 1.6.9 [February 6, 2014] - * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * Last changed in libpng 1.7.0 [(PENDING RELEASE)] + * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -19,6 +19,7 @@ */ #include "pngpriv.h" +#define PNG_SRC_FILE PNG_SRC_FILE_pngrio #ifdef PNG_READ_SUPPORTED @@ -26,18 +27,23 @@ * reads from a file pointer. Note that this routine sometimes gets called * with very small lengths, so you should implement some kind of simple * buffering if you are using unbuffered reads. This should never be asked - * to read more then 64K on a 16 bit machine. + * to read more than 64K on a 16 bit machine. */ void /* PRIVATE */ -png_read_data(png_structrp png_ptr, png_bytep data, png_size_t length) +png_read_data(png_structrp png_ptr, png_voidp data, png_size_t length) { png_debug1(4, "reading %d bytes", (int)length); - if (png_ptr->read_data_fn != NULL) - (*(png_ptr->read_data_fn))(png_ptr, data, length); + /* This was guaranteed by prior versions of libpng, so app callbacks may + * assume it even though it isn't documented to be the case. + */ + debug(length > 0U); + + if (png_ptr->rw_data_fn != NULL) + png_ptr->rw_data_fn(png_ptr, png_voidcast(png_bytep,data), length); else - png_error(png_ptr, "Call to NULL read function"); + png_app_error(png_ptr, "No read function"); } #ifdef PNG_STDIO_SUPPORTED @@ -62,7 +68,7 @@ png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) if (check != length) png_error(png_ptr, "Read Error"); } -#endif +#endif /* STDIO */ /* This function allows the application to supply a new input function * for libpng if standard C streams aren't being used. @@ -85,36 +91,24 @@ png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) */ void PNGAPI png_set_read_fn(png_structrp png_ptr, png_voidp io_ptr, - png_rw_ptr read_data_fn) + png_rw_ptr read_data_fn) { if (png_ptr == NULL) return; - png_ptr->io_ptr = io_ptr; - -#ifdef PNG_STDIO_SUPPORTED - if (read_data_fn != NULL) - png_ptr->read_data_fn = read_data_fn; - - else - png_ptr->read_data_fn = png_default_read_data; -#else - png_ptr->read_data_fn = read_data_fn; -#endif - -#ifdef PNG_WRITE_SUPPORTED - /* It is an error to write to a read device */ - if (png_ptr->write_data_fn != NULL) + if (!png_ptr->read_struct) { - png_ptr->write_data_fn = NULL; - png_warning(png_ptr, - "Can't set both read_data_fn and write_data_fn in the" - " same structure"); + png_app_error(png_ptr, "cannot set a read function on a write struct"); + return; } -#endif -#ifdef PNG_WRITE_FLUSH_SUPPORTED - png_ptr->output_flush_fn = NULL; -#endif + if (read_data_fn == NULL) + { + png_app_error(png_ptr, "API change: png_set_read_fn requires a function"); + return; + } + + png_ptr->io_ptr = io_ptr; + png_ptr->rw_data_fn = read_data_fn; } -#endif /* PNG_READ_SUPPORTED */ +#endif /* READ */ diff --git a/ext/libpng17/pngrtran.c b/ext/libpng17/pngrtran.c index 331b59ee62..c5fb837bee 100644 --- a/ext/libpng17/pngrtran.c +++ b/ext/libpng17/pngrtran.c @@ -1,8 +1,11 @@ +#ifdef _MSC_VER +#pragma warning (disable:4146) +#endif /* pngrtran.c - transforms the data in a row for PNG readers * * Last changed in libpng 1.7.0 [(PENDING RELEASE)] - * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -17,238 +20,3555 @@ */ #include "pngpriv.h" +#define PNG_SRC_FILE PNG_SRC_FILE_pngrtran -#ifdef PNG_READ_SUPPORTED - -/* Set the action on getting a CRC error for an ancillary or critical chunk. */ -void PNGAPI -png_set_crc_action(png_structrp png_ptr, int crit_action, int ancil_action) +#ifdef PNG_READ_QUANTIZE_SUPPORTED +typedef struct { - png_debug(1, "in png_set_crc_action"); + png_transform tr; + png_byte map[256U]; /* Map of palette values */ + png_byte lut[1U << /* LUT for RGB values */ + (PNG_QUANTIZE_RED_BITS+PNG_QUANTIZE_GREEN_BITS+PNG_QUANTIZE_BLUE_BITS)]; +} png_transform_quantize; - if (png_ptr == NULL) - return; +#define PNG_QUANTIZE_MAP 1U /* map is present and not a 1:1 mapping */ +#define PNG_QUANTIZE_LUT 2U /* lut has been built */ - /* Tell libpng how we react to CRC errors in critical chunks */ - switch (crit_action) +static void +do_quantize_rgb(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_quantize *tr = png_transform_cast(png_transform_quantize, + *transform); + unsigned int channels = PNG_TC_CHANNELS(*tc); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - channels/*safety*/; + png_bytep dp = png_voidcast(png_bytep, tc->dp); + + affirm(tc->bit_depth == 8 && (channels == 3 || channels == 4) && + !(tc->format & PNG_FORMAT_FLAG_SWAPPED) && + (tr->tr.args & PNG_QUANTIZE_LUT) != 0); + + tc->sp = dp; + tc->format |= PNG_FORMAT_FLAG_COLORMAP; + + while (sp <= ep) { - case PNG_CRC_NO_CHANGE: /* Leave setting as is */ - break; + unsigned int r = sp[0]; + unsigned int g = sp[1]; + unsigned int b = sp[2]; - case PNG_CRC_WARN_USE: /* Warn/use data */ - png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; - png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE; - break; + /* This looks real messy, but the compiler will reduce + * it down to a reasonable formula. For example, with + * 5 bits per color, we get: + * p = (((r >> 3) & 0x1f) << 10) | + * (((g >> 3) & 0x1f) << 5) | + * ((b >> 3) & 0x1f); + */ + *dp++ = tr->lut[(((r >> (8 - PNG_QUANTIZE_RED_BITS)) & + ((1 << PNG_QUANTIZE_RED_BITS) - 1)) << + (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) | + (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) & + ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) << + (PNG_QUANTIZE_BLUE_BITS)) | + ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) & + ((1 << PNG_QUANTIZE_BLUE_BITS) - 1))]; - case PNG_CRC_QUIET_USE: /* Quiet/use data */ - png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; - png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE | - PNG_FLAG_CRC_CRITICAL_IGNORE; - break; - - case PNG_CRC_WARN_DISCARD: /* Not a valid action for critical data */ - png_warning(png_ptr, - "Can't discard critical data on CRC error"); - case PNG_CRC_ERROR_QUIT: /* Error/quit */ - - case PNG_CRC_DEFAULT: - default: - png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; - break; + sp += channels; } - /* Tell libpng how we react to CRC errors in ancillary chunks */ - switch (ancil_action) - { - case PNG_CRC_NO_CHANGE: /* Leave setting as is */ - break; - - case PNG_CRC_WARN_USE: /* Warn/use data */ - png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; - png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE; - break; - - case PNG_CRC_QUIET_USE: /* Quiet/use data */ - png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; - png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE | - PNG_FLAG_CRC_ANCILLARY_NOWARN; - break; - - case PNG_CRC_ERROR_QUIT: /* Error/quit */ - png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; - png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_NOWARN; - break; - - case PNG_CRC_WARN_DISCARD: /* Warn/discard data */ - - case PNG_CRC_DEFAULT: - default: - png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; - break; - } + affirm(sp == ep+channels); + UNTESTED +# undef png_ptr } -#ifdef PNG_READ_TRANSFORMS_SUPPORTED -/* Is it OK to set a transformation now? Only if png_start_read_image or - * png_read_update_info have not been called. It is not necessary for the IHDR - * to have been read in all cases, the parameter allows for this check too. - */ -static int -png_rtran_ok(png_structrp png_ptr, int need_IHDR) +static void +do_quantize_pal(png_transformp *transform, png_transform_controlp tc) { - if (png_ptr != NULL) +# define png_ptr (tc->png_ptr) + png_transform_quantize *tr = png_transform_cast(png_transform_quantize, + *transform); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + + affirm(tc->bit_depth == 8 && (tc->format & PNG_FORMAT_FLAG_COLORMAP) != 0 && + !(tc->format & PNG_FORMAT_FLAG_SWAPPED) && + (tr->tr.args & PNG_QUANTIZE_MAP) != 0); + + tc->sp = dp; + + while (sp < ep) + *dp++ = tr->map[*sp++]; + + UNTESTED +# undef png_ptr +} + +static void +png_init_quantize(png_transformp *transform, png_transform_controlp tc) +{ + if (tc->bit_depth == 8 && (tc->format & PNG_FORMAT_FLAG_COLOR) != 0) { - if (png_ptr->flags & PNG_FLAG_ROW_INIT) - png_app_error(png_ptr, - "invalid after png_start_read_image or png_read_update_info"); - - else if (need_IHDR && (png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_app_error(png_ptr, "invalid before the PNG header has been read"); - - else + /* Either colormapped input, RGB or RGBA: */ + if (!(tc->format & PNG_FORMAT_FLAG_COLORMAP)) /* RGB, RGBA */ { - /* Turn on failure to initialize correctly for all transforms. */ - png_ptr->flags |= PNG_FLAG_DETECT_UNINITIALIZED; + /* This must be a 'palette' lookup */ + if (((*transform)->args & PNG_QUANTIZE_LUT) != 0) + { + /* This changes the format and invalidates pretty much everything in + * the info struct: + */ + tc->format |= PNG_FORMAT_FLAG_COLORMAP; - return 1; /* Ok */ + if (tc->init == PNG_TC_INIT_FINAL) + { + (*transform)->fn = do_quantize_rgb; + tc->invalid_info |= PNG_INFO_tRNS+PNG_INFO_hIST+PNG_INFO_pCAL+ + PNG_INFO_sBIT+PNG_INFO_bKGD; + tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A = + png_check_byte(tc->png_ptr, tc->bit_depth); + } + + return; + } + } + + else /* colormapped */ + { + /* This must be a 'quantize' lookup */ + if (((*transform)->args & PNG_QUANTIZE_MAP) != 0) + { + /* This doesn't change the format, just the values: */ + if (tc->init == PNG_TC_INIT_FINAL) + { + (*transform)->fn = do_quantize_pal; + tc->invalid_info |= PNG_INFO_sBIT+PNG_INFO_pCAL; + tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A = + png_check_byte(tc->png_ptr, tc->bit_depth); + } + + return; + } } } - return 0; /* no png_error possible! */ + /* Else not applicable */ + (*transform)->fn = NULL; } -#endif -#ifdef PNG_READ_BACKGROUND_SUPPORTED -/* Handle alpha and tRNS via a background color */ -void PNGFAPI -png_set_background_fixed(png_structrp png_ptr, - png_const_color_16p background_color, int background_gamma_code, - int need_expand, png_fixed_point background_gamma) +/* Dither file to 8-bit. Supply a palette, the current number + * of elements in the palette, the maximum number of elements + * allowed, and a histogram if possible. If the current number + * of colors is greater then the maximum number, the palette will be + * modified to fit in the maximum number. "full_quantize" indicates + * whether we need a quantizing cube set up for RGB images, or if we + * simply are reducing the number of colors in a paletted image. + */ +typedef struct png_dsort_struct { - png_debug(1, "in png_set_background_fixed"); + struct png_dsort_struct * next; + png_byte left; + png_byte right; +} png_dsort; +typedef png_dsort * png_dsortp; +typedef png_dsort * * png_dsortpp; - if (!png_rtran_ok(png_ptr, 0) || background_color == NULL) - return; +static void +init_map(png_bytep map) + /* Initialize a mapping table to be 1:1 */ +{ + png_byte b = 0U; - if (background_gamma_code != PNG_BACKGROUND_GAMMA_SCREEN && - background_gamma_code != PNG_BACKGROUND_GAMMA_FILE && - background_gamma_code != PNG_BACKGROUND_GAMMA_UNIQUE) + do + map[b] = b; + while (b++ != 255U); +} + +/* Save typing and make code easier to understand */ +#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \ + abs((int)((c1).green) - (int)((c2).green)) + \ + abs((int)((c1).blue) - (int)((c2).blue))) + +void PNGAPI +png_set_quantize(png_structrp png_ptr, png_colorp palette, + int num_palette, int maximum_colors, png_const_uint_16p histogram, + int full_quantize) +{ + png_debug(1, "in png_set_quantize"); + + if (png_ptr != NULL) { - png_app_error(png_ptr, "invalid gamma type"); - return; + png_transform_quantize *tr = png_transform_cast(png_transform_quantize, + png_add_transform(png_ptr, sizeof (png_transform_quantize), + png_init_quantize, PNG_TR_QUANTIZE)); + + /* This is weird (consider what happens to png_set_background on a palette + * image with a tRNS chunk). + */ + if (palette == png_ptr->palette) + png_app_warning(png_ptr, "png_set_quantize: PLTE will be damaged"); + + if (maximum_colors <= 0 || num_palette > 256) + { + /* The spuriously allocated transform will be removed by the init + * code. + */ + png_app_error(png_ptr, "png_set_quantize: invalid color count"); + return; + } + + /* The app passed in a palette with too many colors, it's not clear why + * libpng is providing this functionality, it's nothing to do with PNG and + * can be done by the application without any PNG specific knowledge. + */ + if (num_palette > maximum_colors) + { + int map_changed = 0; + + /* The map table must be preset to do no mapping initially: */ + init_map(tr->map); + + if (histogram != NULL) + { + /* This is easy enough, just throw out the least used colors. + * Perhaps not the best solution, but good enough. + */ + int i; + png_byte quantize_sort[256U]; + + /* Initialize an array to sort colors */ + init_map(quantize_sort); + + /* Find the least used palette entries by starting a + * bubble sort, and running it until we have sorted + * out enough colors. Note that we don't care about + * sorting all the colors, just finding which are + * least used. + */ + for (i = num_palette - 1; i >= maximum_colors; i--) + { + int done; /* To stop early if the list is pre-sorted */ + int j; + + done = 1; + for (j = 0; j < i; j++) + { + if (histogram[quantize_sort[j]] < + histogram[quantize_sort[j+1]]) + { + png_byte t = quantize_sort[j]; + quantize_sort[j] = quantize_sort[j+1]; + quantize_sort[j+1] = t; + done = 0; + } + } + + if (done != 0) + break; + } + + /* Swap the palette around, and set up a table, if necessary */ + if (full_quantize) + { + int j = num_palette; + + /* Put all the useful colors within the max, but don't + * move the others. + * + * NOTE: if the app passes in the result of png_get_PLTE it will + * be overwritten at this point, what is the API? + */ + for (i = 0; i < maximum_colors; i++) + { + if (quantize_sort[i] >= maximum_colors) + { + do + j--; + while (quantize_sort[j] >= maximum_colors); + + /* NOTE: NOT swapped, so the original palette[i] has been + * lost. + */ + palette[i] = palette[j]; + } + } + } + + else /* !full_quantize */ + { + int j = num_palette; + + /* Move all the used colors inside the max limit, and + * develop a translation table. + */ + for (i = 0; i < maximum_colors; i++) + { + /* Only move the colors we need to */ + if (quantize_sort[i] >= maximum_colors) + { + png_color tmp_color; + + do + j--; + while (quantize_sort[j] >= maximum_colors); + + tmp_color = palette[j]; + palette[j] = palette[i]; + palette[i] = tmp_color; + /* Indicate where the color went */ + tr->map[j] = png_check_byte(png_ptr, i); + tr->map[i] = png_check_byte(png_ptr, j); + map_changed = 1; + } + } + + /* Find closest color for those colors we are not using */ + for (i = 0; i < num_palette; i++) + { + if (tr->map[i] >= maximum_colors) + { + int min_d, k, min_k, d_index; + + /* Find the closest color to one we threw out */ + d_index = tr->map[i]; + min_d = PNG_COLOR_DIST(palette[d_index], palette[0]); + for (k = 1, min_k = 0; k < maximum_colors; k++) + { + int d; + + d = PNG_COLOR_DIST(palette[d_index], palette[k]); + + if (d < min_d) + { + min_d = d; + min_k = k; + } + } + + /* Point to closest color */ + tr->map[i] = png_check_byte(png_ptr, min_k); + map_changed = 1; + } + } + } /* !full_quantize */ + } /* have a histogram */ + + else /* no histogram */ + { + /* This is much harder to do simply (and quickly). Perhaps + * we need to go through a median cut routine, but those + * don't always behave themselves with only a few colors + * as input. So we will just find the closest two colors, + * and throw out one of them (chosen somewhat randomly). + * [We don't understand this at all, so if someone wants to + * work on improving it, be our guest - AED, GRP] + */ + int max_d; + int num_new_palette; + png_byte index_to_palette[256U]; + png_byte palette_to_index[256U]; + png_dsortp hash[769]; + + /* Initialize palette index sort arrays */ + init_map(index_to_palette); + init_map(palette_to_index); + memset(hash, 0, sizeof hash); + num_new_palette = num_palette; + + /* Initial wild guess at how far apart the farthest pixel + * pair we will be eliminating will be. Larger + * numbers mean more areas will be allocated, Smaller + * numbers run the risk of not saving enough data, and + * having to do this all over again. + * + * I have not done extensive checking on this number. + */ + max_d = 96; + + while (num_new_palette > maximum_colors) + { + int i; + png_dsortp t = NULL; + + for (i = 0; i < num_new_palette - 1; i++) + { + int j; + + for (j = i + 1; j < num_new_palette; j++) + { + int d = PNG_COLOR_DIST(palette[i], palette[j]); + + if (d <= max_d) + { + + t = png_voidcast(png_dsortp, png_malloc_warn(png_ptr, + sizeof (*t))); + + if (t == NULL) + break; + + t->next = hash[d]; + t->left = png_check_byte(png_ptr, i); + t->right = png_check_byte(png_ptr, j); + hash[d] = t; + } + } + if (t == NULL) + break; + } + + if (t != NULL) for (i = 0; i <= max_d; i++) + { + if (hash[i] != NULL) + { + png_dsortp p; + + for (p = hash[i]; p != NULL; p = p->next) + { + if (index_to_palette[p->left] < num_new_palette && + index_to_palette[p->right] < num_new_palette) + { + int j, next_j; + + if (num_new_palette & 0x01) + { + j = p->left; + next_j = p->right; + } + else + { + j = p->right; + next_j = p->left; + } + + num_new_palette--; + /* NOTE: overwrites palette */ + palette[index_to_palette[j]] = + palette[num_new_palette]; + + if (full_quantize == 0) + { + int k; + + for (k = 0; k < num_palette; k++) + { + if (tr->map[k] == index_to_palette[j]) + { + tr->map[k] = index_to_palette[next_j]; + map_changed = 1; + } + + if (tr->map[k] == num_new_palette) + { + tr->map[k] = index_to_palette[j]; + map_changed = 1; + } + } + } + + index_to_palette[palette_to_index[num_new_palette]] = + index_to_palette[j]; + + palette_to_index[index_to_palette[j]] = + palette_to_index[num_new_palette]; + + index_to_palette[j] = + png_check_byte(png_ptr, num_new_palette); + + palette_to_index[num_new_palette] = + png_check_byte(png_ptr, j); + } + + if (num_new_palette <= maximum_colors) + break; + } + + if (num_new_palette <= maximum_colors) + break; + } + } + + for (i = 0; i < 769; i++) + { + if (hash[i] != NULL) + { + png_dsortp p = hash[i]; + + while (p) + { + t = p->next; + png_free(png_ptr, p); + p = t; + } + + hash[i] = NULL; + } + } + + max_d += 96; + } /* while num_new_colors > maximum_colors */ + } /* no histogram */ + + num_palette = maximum_colors; + + if (map_changed) /* else the map is 1:1 */ + tr->tr.args |= PNG_QUANTIZE_MAP; + } /* num_palette > maximum_colors */ + + /* The palette has been reduced to the requested number of colors if it + * was over maximum colors before. + */ + + /* TODO: what is this? Apparently the png_struct::palette member gets + * updated if it didn't originally have a palette, but the update relies + * on the app not freeing the passed in palette. + */ + if (png_ptr->palette == NULL) + png_ptr->palette = palette; + + png_ptr->num_palette = png_check_bits(png_ptr, num_palette, 9); + + if (full_quantize) + { + int i; + png_byte distance[1U << (PNG_QUANTIZE_RED_BITS+PNG_QUANTIZE_GREEN_BITS+ + PNG_QUANTIZE_BLUE_BITS)]; + + memset(distance, 0xff, sizeof distance); + + for (i = 0; i < num_palette; i++) + { + int ir; + int r = (palette[i].red >> (8 - PNG_QUANTIZE_RED_BITS)); + int g = (palette[i].green >> (8 - PNG_QUANTIZE_GREEN_BITS)); + int b = (palette[i].blue >> (8 - PNG_QUANTIZE_BLUE_BITS)); + + for (ir = 0; ir < (1< r) ? ir - r : r - ir); + int index_r = (ir << (PNG_QUANTIZE_BLUE_BITS + + PNG_QUANTIZE_GREEN_BITS)); + + for (ig = 0; ig < (1< g) ? ig - g : g - ig); + int dt = dr + dg; + int dm = ((dr > dg) ? dr : dg); + int index_g = index_r | (ig << PNG_QUANTIZE_BLUE_BITS); + + for (ib = 0; ib < (1< b) ? ib - b : b - ib); + int dmax = ((dm > db) ? dm : db); + int d = dmax + dt + db; + + if (d < distance[d_index]) + { + distance[d_index] = png_check_byte(png_ptr, d); + tr->lut[d_index] = png_check_byte(png_ptr, i); + } + } /* for blue */ + } /* for green */ + } /* for red */ + } /* num_palette */ + } /* full_quantize */ + } /* png_ptr != NULL */ +} +#endif /* READ_QUANTIZE */ + +#ifdef PNG_READ_PACK_SUPPORTED +/* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel, + * without changing the actual values. Thus, if you had a row with + * a bit depth of 1, you would end up with bytes that only contained + * the numbers 0 or 1. If you would rather they contain 0 and 255, use + * png_set_expand_gray_1_2_4_to_8 instead. + */ +static void +png_do_read_unpack(png_transformp *transform, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_const_bytep ep = png_voidcast(png_const_bytep, tc->dp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + + sp += PNG_TC_ROWBYTES(*tc) - 1; /* Start from end */ + dp += tc->width; /* output bit depth is 8 */ + +# define png_ptr (tc->png_ptr) + png_debug(1, "in png_do_unpack"); + + switch (tc->bit_depth) + { + case 1: + { + /* Because we copy from the last pixel down the shift required + * at the start is 8-pixels_in_last_byte, which is just: + */ + unsigned int shift = 7U & -tc->width; + + while (dp > ep) + { + *--dp = (*sp >> shift) & 1U; + shift = 7U & (shift+1U); + if (shift == 0U) + --sp; + } + + debug(shift == 0U); + break; + } + + case 2: + { + unsigned int shift = 7U & -(tc->width << 1); + + while (dp > ep) + { + *--dp = (*sp >> shift) & 3U; + shift = 7U & (shift+2U); + if (shift == 0U) + --sp; + } + + debug(shift == 0U); + break; + } + + case 4: + { + unsigned int shift = 7U & -(tc->width << 2); + + while (dp > ep) + { + *--dp = (*sp >> shift) & 15U; + shift = 7U & (shift+4U); + if (shift == 0U) + --sp; + } + + debug(shift == 0U); + break; + } + + default: + impossible("bit depth"); } - png_ptr->transformations |= PNG_COMPOSE | PNG_STRIP_ALPHA; - png_ptr->transformations &= ~PNG_ENCODE_ALPHA; - png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + debug(dp == ep && sp == png_upcast(png_const_bytep, tc->sp)-1U); + tc->sp = dp; - png_ptr->background = *background_color; - png_ptr->background_gamma = background_gamma; - png_ptr->background_gamma_type = (png_byte)(background_gamma_code); + if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0U) + { + tc->range++; + tc->format |= PNG_FORMAT_FLAG_RANGE; + } - if (need_expand != 0) - png_ptr->flags |= PNG_FLAG_BACKGROUND_EXPAND; - - else - png_ptr->flags &= ~PNG_FLAG_BACKGROUND_EXPAND; + tc->bit_depth = 8U; + PNG_UNUSED(transform) +# undef png_ptr } -# ifdef PNG_FLOATING_POINT_SUPPORTED -void PNGAPI -png_set_background(png_structrp png_ptr, - png_const_color_16p background_color, int background_gamma_code, - int need_expand, double background_gamma) -{ - png_set_background_fixed(png_ptr, background_color, background_gamma_code, - need_expand, png_fixed(png_ptr, background_gamma, "png_set_background")); -} -# endif /* FLOATING_POINT */ -#endif /* READ_BACKGROUND */ - -/* Scale 16-bit depth files to 8-bit depth. If both of these are set then the - * one that pngrtran does first (scale) happens. This is necessary to allow the - * TRANSFORM and API behavior to be somewhat consistent, and it's simpler. +/* Called from the curiously named png_set_packing API in pngtrans.c; the read + * and write code is separated because read 'unpacks' (from PNG format) and + * write 'packs' (to PNG format.) */ -#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED -void PNGAPI -png_set_scale_16(png_structrp png_ptr) +void /* PRIVATE */ +png_init_read_pack(png_transformp *transform, png_transform_controlp tc) { - png_debug(1, "in png_set_scale_16"); +# define png_ptr tc->png_ptr + debug(tc->init); - if (!png_rtran_ok(png_ptr, 0)) - return; + if (tc->bit_depth < 8) /* else no packing/unpacking */ + { + /* For indexed images the pack operation does not invalidate the range; in + * fact the corresponding shift operation would! + */ + if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0U) + { + tc->range++; + tc->format |= PNG_FORMAT_FLAG_RANGE; + } - png_ptr->transformations |= PNG_SCALE_16_TO_8; + tc->bit_depth = 8U; + + if (tc->init == PNG_TC_INIT_FINAL) + (*transform)->fn = png_do_read_unpack/* sic: it unpacks */; + } + + else /* the transform is not applicable */ + (*transform)->fn = NULL; + +# undef png_ptr } -#endif +#endif /* READ_PACK */ -#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED -/* Chop 16-bit depth files to 8-bit depth */ -void PNGAPI -png_set_strip_16(png_structrp png_ptr) +#if defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) +# ifdef PNG_READ_tRNS_SUPPORTED +static unsigned int +fill_transparent_pixel(png_const_structrp png_ptr, png_byte *trans) + /* Fill a byte array according to the transparent pixel value and return a + * count of the number of bytes. Low bit depth gray values are replicated in + * the first byte. Writes from 1 to 6 bytes. + */ { - png_debug(1, "in png_set_strip_16"); + /* There must be a tRNS chunk and this must not be a palette image: */ + debug(png_ptr->num_trans == 1 && + !(png_ptr->color_type & (PNG_COLOR_MASK_ALPHA+PNG_COLOR_MASK_PALETTE))); - if (!png_rtran_ok(png_ptr, 0)) - return; + if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) /* gray */ + { + unsigned int t = png_ptr->trans_color.gray; + unsigned int depth = png_ptr->bit_depth; - png_ptr->transformations |= PNG_16_TO_8; + if (depth < 16U) + { + /* ISO PNG 11.3.2.1 "tRNS Transparency": "If the image bit depth is + * less than 16, the least significant bits are used and the others are + * 0." So mask out the upper bits. + */ + t &= (1U<> 8); + trans[1] = PNG_BYTE(t); + return 2U; + } + + else /* color */ switch (png_ptr->bit_depth) + { + case 8: /* 8-bit RGB */ + trans[0] = PNG_BYTE(png_ptr->trans_color.red); + trans[1] = PNG_BYTE(png_ptr->trans_color.green); + trans[2] = PNG_BYTE(png_ptr->trans_color.blue); + return 3U; + + case 16: /* 16-bit RGB */ + trans[0] = PNG_BYTE(png_ptr->trans_color.red >> 8); + trans[1] = PNG_BYTE(png_ptr->trans_color.red); + trans[2] = PNG_BYTE(png_ptr->trans_color.green >> 8); + trans[3] = PNG_BYTE(png_ptr->trans_color.green); + trans[4] = PNG_BYTE(png_ptr->trans_color.blue >> 8); + trans[5] = PNG_BYTE(png_ptr->trans_color.blue); + return 6U; + + default: + NOT_REACHED; + return 0U; /* safe */ + } } -#endif +# endif /* READ_tRNS */ +#endif /* READ_EXPAND || READ_BACKGROUND */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Flags for png_init_expand */ +#define PNG_EXPAND_PALETTE 1U /* palette images only, includes tRNS */ +#define PNG_EXPAND_LBD_GRAY 2U /* grayscale low-bit depth only */ +#define PNG_EXPAND_tRNS 4U /* non-palette images only */ + +/* This struct is only required for tRNS matching, but it is convenient to + * allocated it anyway even if READ_tRNS is not supported. + */ +typedef struct +{ + png_transform tr; + unsigned int ntrans; /* number of bytes below */ + png_byte transparent_pixel[6]; /* the transparent pixel value */ +} png_expand; + +#ifdef PNG_READ_tRNS_SUPPORTED +/* Look for colors matching the trans_color in png_ptr, low bit depth gray is + * covered below so this only need handle 8 abd 16-bit channels. + */ +static void +png_do_expand_tRNS(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_expand *tr = png_transform_cast(png_expand, *transform); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp; + const unsigned int spixel_size = PNG_TC_PIXEL_DEPTH(*tc) >> 3; + unsigned int alpha_size; + + /* We expect opaque and transparent pixels to be interleaved but with long + * sequences of each. Because we are adding an alpha channel we must copy + * down. + */ + debug(!(tc->format & PNG_FORMAT_FLAG_ALPHA)); + debug(spixel_size == tr->ntrans); + sp += PNG_TC_ROWBYTES(*tc); + tc->sp = dp; + tc->format |= PNG_FORMAT_FLAG_ALPHA; + tc->invalid_info |= PNG_INFO_tRNS; + tc->transparent_alpha = 1U; + alpha_size = (PNG_TC_PIXEL_DEPTH(*tc)>>3) - spixel_size; + debug(alpha_size == 1 || alpha_size == 2); + dp += PNG_TC_ROWBYTES(*tc); + + do + { + unsigned int i = spixel_size; + png_byte alpha = 0U; + + dp -= alpha_size; + alpha = 0U; + + /* Copy and check one source pixel (backwards, to avoid any + * overwrite): + */ + do if ((*--dp = *--sp) != tr->transparent_pixel[--i]) /* pixel != tRNS */ + alpha = 0xFFU; + while (i != 0U); + + /* i == 0 */ + do + dp[spixel_size + i] = alpha; + while (++i < alpha_size); + } while (sp > ep); + + debug(sp == ep && dp == tc->dp); /* else overwrite */ +# undef png_ptr +} +#endif /* READ_tRNS */ + +/* Expand grayscale images of less than 8-bit depth to 8 bits. + * libpng 1.7.0: this no longer expands everything, it just expands the low bit + * depth gray row. It does *NOT* expand the tRNS into an alpha channel unless + * it is told to do so. + * + * API CHANGE: the function now does what it was always meant to do. + * + * This is like do_unpack except that the packed data is expanded to the full + * 8-bit range; scaled up. This is not a good thing to do on an indexed image; + * the indices will be invalid. + * + * The tRNS handling is included here too; speed is not important because the + * result will always be cached unless the PNG is very small. + */ +static void +png_do_expand_lbd_gray(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_bytep dp = png_voidcast(png_bytep, tc->dp); + const png_const_bytep ep = dp; + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const unsigned int bit_depth = tc->bit_depth; +# ifdef PNG_READ_sBIT_SUPPORTED + unsigned int insignificant_bits = 0U; +# endif /* READ_sBIT */ +# ifdef PNG_READ_tRNS_SUPPORTED + unsigned int gray = 0xffffU; /* doesn't match anything */ + unsigned int do_alpha = 0U; +# endif /* READ_tRNS */ + + sp += PNG_TC_ROWBYTES(*tc); /* last byte +1 */ + tc->bit_depth = 8U; + tc->invalid_info |= PNG_INFO_tRNS; +# ifdef PNG_READ_sBIT_SUPPORTED + if (bit_depth > 1U /* irrelevant for bit depth 1 */ && + !(tc->invalid_info & PNG_INFO_sBIT) && + tc->sBIT_G > 0U/*SAFETY*/ && tc->sBIT_G < bit_depth) + insignificant_bits = bit_depth - tc->sBIT_G; +# endif /* READ_sBIT */ + +# ifdef PNG_READ_tRNS_SUPPORTED + if (((*transform)->args & PNG_EXPAND_tRNS) != 0) + { + tc->format |= PNG_FORMAT_FLAG_ALPHA; + tc->transparent_alpha = 1U; + gray = png_ptr->trans_color.gray & ((1U << bit_depth)-1U); + do_alpha = 1U; + } + + /* This helps avoid cluttering the code up with #ifdefs: */ +# define check_tRNS if (do_alpha) *--dp = (pixel != gray) * 255U; +# else /* !READ_tRNS */ +# define check_tRNS +# endif /* READ_tRNS */ + + dp += PNG_TC_ROWBYTES(*tc); /* pre-decremented below */ + + switch (bit_depth) + { + case 1: + { + unsigned int shift = 7U & -tc->width; + unsigned int s = *--sp; + + for(;;) + { + if (shift == 8U) s = *--sp, shift = 0; + + { + const unsigned int pixel = (s >> shift) & 1U; + + check_tRNS + *--dp = PNG_BYTE(pixel * 255U); + if (dp <= ep) break; + } + ++shift; + } + + debug(dp == ep && shift == 7U && sp == tc->sp); + break; + } + + case 2: + { + unsigned int shift = 7U & -(tc->width << 1)/*overflow ok*/; + unsigned int s = *--sp; + + for (;;) + { + if (shift == 8U) s = *--sp, shift = 0; + { + const unsigned int pixel = (s >> shift) & 3U; + + check_tRNS + +# ifdef PNG_READ_sBIT_SUPPORTED + /* 'sig_bits' must be 1 or 2 leaving insignificant_bits 0 or + * 1. This may look silly but it allows a compact representation + * of 1 bit gray + 1 bit alpha (transparency): + */ + if (insignificant_bits /* only 1 bit significant */) + *--dp = PNG_BYTE((pixel >> 1) * 255U); + + else +# endif + *--dp = PNG_BYTE(pixel * 85U); + + if (dp <= ep) break; + } + shift += 2U; + } + + debug(dp == ep && shift == 6U && sp == tc->sp); + break; + } + + case 4: + { + unsigned int shift = 7U & -(tc->width << 2)/*overflow ok*/; + unsigned int s = *--sp; +# ifdef PNG_READ_sBIT_SUPPORTED + const unsigned int div = (1U << (4U-insignificant_bits)) - 1U; +# endif + + for (;;) + { + if (shift == 8U) s = *--sp, shift = 0; + { + unsigned int pixel = (s >> shift) & 15U; + + check_tRNS + +# ifdef PNG_READ_sBIT_SUPPORTED + /* insignificant_bits may be 0, 1, 2 or 3, requiring a multiply + * by 17, 255/7, 85 or 255. Since this operation is always + * cached we don't much care about the time to do the divide + * below. + */ + if (insignificant_bits) + pixel = ((pixel>>insignificant_bits) * 255U + (div>>1)) / div; + + else +# endif + pixel *= 17U; + + *--dp = PNG_BYTE(pixel); + if (dp <= ep) break; + } + + shift += 4U; + } + + debug(dp == ep && shift == 4U && sp == tc->sp); + break; + } + + default: + impossible("bit depth"); + } + + tc->sp = ep; + +# undef check_tRNS +# undef png_ptr +} + +static void +png_init_expand(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + /* The possible combinations are: + * + * 1) PALETTE: the 'palette' flag should be set on the transform control and + * all that need be done is cancel this to cause the cache code to do the + * expansion. + * + * 2) LBP_GRAY, LBP_GRAY+tRNS: use png_do_expand_lbd_gray to do the required + * expand. Can be cached. + * + * 3) tRNS: scan the row for the relevant tRNS value. + * + * Note that expanding 8 to 16 bits is a byte op in pngtrans.c (it just + * replicates bytes). + */ + if (tc->palette) + { + debug(tc->caching && !(tc->format & PNG_FORMAT_FLAG_COLORMAP)); + + if (((*transform)->args & PNG_EXPAND_PALETTE) != 0U) + { + tc->palette = 0U; + tc->invalid_info |= PNG_INFO_PLTE + PNG_INFO_tRNS; + tc->cost = PNG_CACHE_COST_LIMIT; /* the cache is required! */ + } + + /* Note that this needs to happen when the row is processed (!tc->init) as + * well. + */ + } + + else if (!(tc->format & PNG_FORMAT_FLAG_COLORMAP)) + { + png_uint_32 args = (*transform)->args & PNG_BIC_MASK(PNG_EXPAND_PALETTE); + unsigned int bit_depth = tc->bit_depth; + + debug(tc->init); + + if (bit_depth >= 8U) + args &= PNG_BIC_MASK(PNG_EXPAND_LBD_GRAY); + +# ifdef PNG_READ_tRNS_SUPPORTED + if (png_ptr->num_trans == 0U || + (tc->format & PNG_FORMAT_FLAG_ALPHA) != 0U || + (tc->invalid_info & PNG_INFO_tRNS) != 0U) +# endif + args &= PNG_BIC_MASK(PNG_EXPAND_tRNS); + + (*transform)->args = args; + + switch (args) + { + case PNG_EXPAND_LBD_GRAY: + tc->bit_depth = 8U; + tc->invalid_info |= PNG_INFO_tRNS; + + if (tc->init == PNG_TC_INIT_FINAL) + (*transform)->fn = png_do_expand_lbd_gray; + break; + +# ifdef PNG_READ_tRNS_SUPPORTED + case PNG_EXPAND_LBD_GRAY + PNG_EXPAND_tRNS: + tc->bit_depth = 8U; + tc->format |= PNG_FORMAT_FLAG_ALPHA; + tc->invalid_info |= PNG_INFO_tRNS; + tc->transparent_alpha = 1U; + + /* In this case tRNS must be left unmodified for the expansion code + * to handle. + */ + if (tc->init == PNG_TC_INIT_FINAL) + (*transform)->fn = png_do_expand_lbd_gray; + break; + + case PNG_EXPAND_tRNS: + if (tc->init == PNG_TC_INIT_FINAL) + { + png_expand *tr = png_transform_cast(png_expand, *transform); + + affirm((tc->bit_depth == 8U || tc->bit_depth == 16U) && + (tc->format & + (PNG_FORMAT_FLAG_COLORMAP|PNG_FORMAT_FLAG_ALPHA)) == 0U); + + tr->ntrans = fill_transparent_pixel(png_ptr, + tr->transparent_pixel); + tr->tr.fn = png_do_expand_tRNS; + } /* TC_INIT_FINAL */ + + tc->format |= PNG_FORMAT_FLAG_ALPHA; + tc->invalid_info |= PNG_INFO_tRNS; + tc->transparent_alpha = 1U; + break; +# endif /* READ_tRNS */ + + default: /* transform not applicable */ + (*transform)->fn = NULL; + break; + } + + implies(tc->init == PNG_TC_INIT_FINAL, + (*transform)->fn != png_init_expand); + } + + else /* not applicable */ + { + debug(tc->init); + (*transform)->fn = NULL; + NOT_REACHED; + } +# undef png_ptr +} + +void PNGAPI +png_set_expand_gray_1_2_4_to_8(png_structrp png_ptr) +{ + if (png_ptr != NULL) + png_add_transform(png_ptr, sizeof (png_expand), png_init_expand, + PNG_TR_EXPAND)->args |= PNG_EXPAND_LBD_GRAY; +} + +/* Expand paletted images to 8-bit RGB or, if there is a tRNS chunk, RGBA. + * Note that this is effectively handled by the read code palette optimizations. + * + * API CHANGE: this used to have the completely unexpected side effect of + * turning on the above two optimizations. + */ +void PNGAPI +png_set_palette_to_rgb(png_structrp png_ptr) +{ + if (png_ptr != NULL) + png_add_transform(png_ptr, sizeof (png_expand), png_init_expand, + PNG_TR_EXPAND)->args |= PNG_EXPAND_PALETTE; +} + +/* Expand paletted images to RGB, expand grayscale images of less than 8-bit + * depth to 8-bit depth, and expand tRNS chunks to alpha channels. I.e. all the + * above. + */ +void PNGAPI +png_set_expand(png_structrp png_ptr) +{ + if (png_ptr != NULL) + { + png_set_palette_to_rgb(png_ptr); + png_set_expand_gray_1_2_4_to_8(png_ptr); + png_set_tRNS_to_alpha(png_ptr); + } +} +#endif /* READ_EXPAND */ + +#if defined(PNG_READ_EXPAND_SUPPORTED) ||\ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) + +#define PNG_INIT_STRIP_ALPHA 1U +#define PNG_INIT_EXPAND_tRNS 2U +static void +png_init_alpha(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + int required = 0; + +# if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_tRNS_SUPPORTED) + if ((*transform)->args & PNG_INIT_EXPAND_tRNS) + { + /* Prior to 1.7 the alpha channel was stripped after expanding the tRNS + * chunk, so this effectively cancelled out the expand. + */ + if (png_ptr->num_trans > 0 && !tc->palette && + !((*transform)->args & PNG_INIT_STRIP_ALPHA)) + { + debug((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0); + + required = 1; + tc->expand_tRNS = 1U; + + /* This happens as a result of an explicit API call to + * png_set_tRNS_to_alpha, so expand low-bit-depth gray too: + */ + if (tc->init == PNG_TC_INIT_FORMAT) + png_add_transform(png_ptr, sizeof (png_expand), png_init_expand, + PNG_TR_EXPAND)->args |= PNG_EXPAND_tRNS + PNG_EXPAND_LBD_GRAY; + } + + else + (*transform)->args &= PNG_BIC_MASK(PNG_INIT_EXPAND_tRNS); + } +# endif /* READ_EXPAND && READ_tRNS */ + +# ifdef PNG_READ_STRIP_ALPHA_SUPPORTED + if ((*transform)->args & PNG_INIT_STRIP_ALPHA) + { + /* When compose is being done tRNS will be expanded regardless of the + * above test. Rather that trying to work out if this will happen the + * code just inserts a strip operation; it will be removed later if it + * is not needed. + */ + required = 1; + tc->strip_alpha = 1U; + + if (tc->init == PNG_TC_INIT_FORMAT) + png_add_strip_alpha_byte_ops(png_ptr); + } +# endif /* READ_STRIP_ALPHA */ + + if (!required) + (*transform)->fn = NULL; +# undef png_ptr +} +#endif /* READ_EXPAND || READ_STRIP_ALPHA */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand tRNS chunks to alpha channels. This only expands the tRNS chunk on + * non-palette formats; call png_set_palette_to_rgb to get the corresponding + * effect for a palette. + * + * Note that this will expand low bit depth gray if there is a tRNS chunk, but + * if not nothing will happen. + * + * API CHANGE: this used to do all the expansions, it was rather pointless + * calling it. + */ +void PNGAPI +png_set_tRNS_to_alpha(png_structrp png_ptr) +{ + if (png_ptr != NULL) + png_add_transform(png_ptr, 0/*size*/, png_init_alpha, PNG_TR_INIT_ALPHA)-> + args |= PNG_INIT_EXPAND_tRNS; +} +#endif /* READ_EXPAND */ #ifdef PNG_READ_STRIP_ALPHA_SUPPORTED void PNGAPI png_set_strip_alpha(png_structrp png_ptr) { - png_debug(1, "in png_set_strip_alpha"); - - if (!png_rtran_ok(png_ptr, 0)) - return; - - png_ptr->transformations |= PNG_STRIP_ALPHA; + if (png_ptr != NULL) + png_add_transform(png_ptr, 0/*size*/, png_init_alpha, PNG_TR_INIT_ALPHA)-> + args |= PNG_INIT_STRIP_ALPHA; } +#endif /* READ_STRIP_ALPHA */ + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +static void +png_do_chop_16_to_8(png_transformp *transform, png_transform_controlp tc) + /* This is actually a repeat of the byte transform, unnecessary code + * replication. + * + * TODO: remove this + */ +{ +# define png_ptr (tc->png_ptr) + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); /* source */ + png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc); /* end+1 */ + png_bytep dp = png_voidcast(png_bytep, tc->dp); /* destination */ + + debug(tc->bit_depth == 16U); + tc->sp = dp; + tc->bit_depth = 8U; + + while (sp < ep) + *dp++ = *sp, sp += 2; + + debug(sp == ep); +# undef png_ptr + + PNG_UNUSED(transform) +} + +/* A transform containing some useful scaling values... */ +typedef struct +{ + png_transform tr; + png_uint_32 shifts; /* 4 4-bit values preceeded by a shibboleth (1) */ + png_uint_32 channel_scale[4]; +} png_transform_scale_16_to_8; + +/* Scale rows of bit depth 16 down to 8 accurately */ +static void +png_do_scale_16_to_8(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); /* source */ + png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc); /* end+1 */ + png_bytep dp = png_voidcast(png_bytep, tc->dp); /* destination */ + png_transform_scale_16_to_8 *tr = + png_transform_cast(png_transform_scale_16_to_8, *transform); + png_uint_32p scale = 0; + png_uint_32 shift = 1U; /* set the shibboleth at the start */ + + debug(tc->bit_depth == 16U); + tc->sp = dp; + tc->bit_depth = 8U; + + while (sp < ep) + { + /* The input is an array of 16 bit components, these must be scaled to + * 8 bits each taking into account the sBIT setting. The calculation + * requires that the insignificant bits be stripped from the input value + * via a shift then scaled back to 8 bits: + * + * output = ((input >> shift) * scale + round) >> 24 + * + * The shifts are packed into tr->shifts, with the end of the list marked + * by a shibboleth, 1, which is preset above. + */ + png_uint_32 v = png_get_uint_16(sp); + + sp += 2; + + if (shift == 1U) + { + shift = tr->shifts; + scale = tr->channel_scale; + } + + *dp++ = PNG_BYTE(((v >> (shift & 0xFU)) * *scale++ + 0x800000U) >> 24); + shift >>= 4; + } + + affirm(sp == ep); +# undef png_ptr +} + +static int +add_scale(png_transform_scale_16_to_8 *tr, unsigned int sBIT, unsigned int ch) +{ + /* This is the output max (255) scaled by 2^24 divided by the input max' + * (which is variable) and rounded. It gives the exact 8-bit answer for all + * input sBIT depths when used in the calculation: + * + * output = ((input >> shift) * scale + 0x800000U) >> 24 + */ + tr->channel_scale[ch] = (0xFF000000U + ((1U<>1)) / ((1U<shifts |= ((16U-sBIT) & 0xFU) << (4U*ch); + + /* The result says whether there are 8 or fewer significant bits in the + * input value; if so we can just drop the low byte. + */ + return sBIT <= 8U; +} + +static void +png_init_scale_16_to_8(png_transformp *transform, png_transform_controlp tc) +{ + if (tc->bit_depth == 16U) + { +# define png_ptr (tc->png_ptr) + tc->bit_depth = 8U; + /* But this invalidates tRNS (a 16-bit tRNS cannot be updated to match + * 8-bit data correctly). + */ + tc->invalid_info |= PNG_INFO_tRNS+PNG_INFO_hIST+PNG_INFO_pCAL; + /* TODO: These need further processing: PNG_INFO_bKGD */ + + if (tc->init == PNG_TC_INIT_FINAL) + { + png_transform_scale_16_to_8 *tr = + png_transform_cast(png_transform_scale_16_to_8, *transform); + + /* Set the scale factors for each channel (up to 4), the factors are + * made so that: + * + * ((channel >> shift) * factor + 0x800000U) >> 24 + * + * Gives the required 8-bit value. The 'shift' is stored in a single + * png_uint_32 with a shibboleth at the end. + */ + unsigned int channels = 0U; + int chop_ok = 1; + + tr->shifts = 0U; + + /* This adds up to four scale factors, the remainder are left as 0 + * which is safe and leads to obvious errors in the output images in + * the event of an (internal) error. + */ + if (tc->format & PNG_FORMAT_FLAG_COLOR) + chop_ok &= add_scale(tr, tc->sBIT_R, channels++); + + chop_ok &= add_scale(tr, tc->sBIT_G, channels++); + + if (tc->format & PNG_FORMAT_FLAG_COLOR) + chop_ok &= add_scale(tr, tc->sBIT_B, channels++); + + if (tc->format & PNG_FORMAT_FLAG_ALPHA) + chop_ok &= add_scale(tr, tc->sBIT_A, channels++); + + if (chop_ok) + tr->tr.fn = png_do_chop_16_to_8; + + else + { + int handled = 1; + + /* Add the shibboleth at the end */ + tr->shifts |= 1U << (4U*channels); + tr->tr.fn = png_do_scale_16_to_8; + + /* sBIT is a little tricky; it has to be processed in the scaling + * operation. The result will have the same number of bits unless + * there were more than 8 before. The sBIT flags in the transform + * control are left unchanged here because the data is still valid, + * unless all the values end up as 8 in which case there is no + * remaining sBIT info. + * + * Note that fields, such as alpha, which are not set for this row + * format will always have max values, so won't reset 'handled': + */ + if (tc->sBIT_R >= 8U) tc->sBIT_R = 8U; else handled = 0; + if (tc->sBIT_G >= 8U) tc->sBIT_G = 8U; else handled = 0; + if (tc->sBIT_B >= 8U) tc->sBIT_B = 8U; else handled = 0; + if (tc->sBIT_A >= 8U) tc->sBIT_A = 8U; else handled = 0; + + /* If all the sBIT values were >= 8U all the bits are now + * significant: + */ + if (handled) + tc->invalid_info |= PNG_INFO_sBIT; + } + } + +# undef png_ptr + } + + else /* not applicable */ + (*transform)->fn = NULL; +} + +void PNGAPI +png_set_scale_16(png_structrp png_ptr) +{ + if (png_ptr != NULL) + png_add_transform(png_ptr, sizeof (png_transform_scale_16_to_8), + png_init_scale_16_to_8, PNG_TR_SCALE_16_TO_8); +} +#endif /* READ_SCALE_16_TO_8 */ + +#ifdef PNG_READ_GAMMA_SUPPORTED + /* Code that depends on READ_GAMMA support; RGB to gray convertion and + * background composition (including the various alpha-mode handling + * operations which produce pre-multiplied alpha by composing on 0). + */ +/* Calculate a reciprocal, return 0 on div-by-zero or overflow. */ +static png_fixed_point +png_reciprocal(png_fixed_point a) +{ +#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + double r = floor(1E10/a+.5); + + if (r <= 2147483647. && r >= -2147483648.) + return (png_fixed_point)r; +#else + png_fixed_point res; + + if (png_muldiv(&res, PNG_FP_1, PNG_FP_1, a) != 0) + return res; #endif -#if defined(PNG_READ_ALPHA_MODE_SUPPORTED) || defined(PNG_READ_GAMMA_SUPPORTED) + return 0; /* error/overflow */ +} + +/* This is the shared test on whether a gamma value is 'significant' - whether + * it is worth doing gamma correction. 'significant_bits' is the number of bits + * in the values to be corrected which are significant. + */ +static int +png_gamma_significant(png_const_structrp png_ptr, png_fixed_point gamma_val, + unsigned int sbits) +{ +#if 0 + /* This seems to be wrong. The issue is that when the app asks for a higher + * bit depth output than the input has significant bits it causes gamma + * correction to be skipped (this was the intent) however there's no + * particular guarantee that the app won't go on to do further gamma + * processing - pngstest does this - and this messes up the results + * completely. + * + * TODO: work out how to optimize this correctly. + */ + /* The following table lists the threshold as a difference from PNG_FP_1 at + * which the gamma correction will make a change to at least an 'sbits' + * value. There is no entry for 1 bit values; gamma correction is never + * significant. + */ + static const png_uint_16 gamma_threshold_by_sbit[15][2] = + { + { 36907, 63092 }, /* 2 bits */ + { 17812, 21518 }, /* 3 bits */ + { 8675, 9496 }, /* 4 bits */ + { 4290, 4484 }, /* 5 bits */ + { 2134, 2181 }, /* 6 bits */ + { 1064, 1075 }, /* 7 bits */ + { 531, 534 }, /* 8 bits */ + { 265, 266 }, /* 9 bits */ + { 132, 132 }, /* 10 bits */ + { 66, 66 }, /* 11 bits */ + { 33, 33 }, /* 12 bits */ + { 16, 16 }, /* 13 bits */ + { 8, 8 }, /* 14 bits */ + { 4, 4 }, /* 15 bits */ + { 2, 2 }, /* 16 bits */ + }; + + /* Handle out of range values in release by doing the gamma correction: */ + debug_handled(sbits > 0U && sbits <= 16U); + if (sbits == 0U || sbits > 16U) + return 1; + + /* 1 bit input or zero gamma, no correction possible/required: */ + if (gamma_val == 0 || sbits < 2U) + return 0; + + if (gamma_val < PNG_FP_1 - gamma_threshold_by_sbit[sbits-2U][0U]) + return gamma_val < PNG_FP_1 - png_ptr->gamma_threshold; + + else if (gamma_val > PNG_FP_1 + gamma_threshold_by_sbit[sbits-2U][1U]) + return gamma_val > PNG_FP_1 + png_ptr->gamma_threshold; +#else /* FIXUP */ + if (gamma_val < PNG_FP_1) + return gamma_val < PNG_FP_1 - png_ptr->gamma_threshold; + + else if (gamma_val > PNG_FP_1) + return gamma_val > PNG_FP_1 + png_ptr->gamma_threshold; + + PNG_UNUSED(sbits) +#endif /* FIXUP */ + + return 0; /* not significant */ +} + +static int +png_gamma_equal(png_const_structrp png_ptr, png_fixed_point g1, + png_fixed_point g2, png_fixed_point *c, unsigned int sbits) + /* Gamma values are equal, or at least one is unknown; c is the correction + * factor from g1 to g2, i.e. g2/g1. + */ +{ + return sbits == 1U || g1 == 0 || g2 == 0 || g1 == g2 || + (png_muldiv(c, g2, PNG_FP_1, g1) && + !png_gamma_significant(png_ptr, *c, sbits)); +} + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +int +png_need_gamma_correction(png_const_structrp png_ptr, png_fixed_point gamma, + int sRGB_output) + /* This is a hook for the simplified code; it just decides whether or not the + * given gamma (which defaults to that of the PNG data) is close enough to + * linear or sRGB not to require gamma correction. + */ +{ + if (gamma == 0) + gamma = png_ptr->colorspace.gamma; + + if (gamma != 0 && + (png_ptr->colorspace.flags & + (PNG_COLORSPACE_INVALID|PNG_COLORSPACE_HAVE_GAMMA)) == + PNG_COLORSPACE_HAVE_GAMMA) + { + + if (sRGB_output && !png_muldiv(&gamma, gamma, PNG_GAMMA_sRGB, PNG_FP_1)) + return 0; /* overflow, so no correction */ + + return png_gamma_significant(png_ptr, gamma, (png_ptr->color_type & + PNG_COLOR_MASK_PALETTE) ? 8U : png_ptr->bit_depth); + } + + return 0; /* no info, no correction */ +} +#endif /* SIMPLIFIED_READ */ + +#ifndef PNG_FLOATING_ARITHMETIC_SUPPORTED +/* Fixed point gamma. + * + * The code to calculate the tables used below can be found in the shell script + * contrib/tools/intgamma.sh + * + * To calculate gamma this code implements fast log() and exp() calls using only + * fixed point arithmetic. This code has sufficient precision for either 8-bit + * or 16-bit sample values. + * + * The tables used here were calculated using simple 'bc' programs, but C double + * precision floating point arithmetic would work fine. + * + * 8-bit log table + * This is a table of -log(value/255)/log(2) for 'value' in the range 128 to + * 255, so it's the base 2 logarithm of a normalized 8-bit floating point + * mantissa. The numbers are 32-bit fractions. + */ +static const png_uint_32 +png_8bit_l2[128] = +{ + 4270715492U, 4222494797U, 4174646467U, 4127164793U, 4080044201U, 4033279239U, + 3986864580U, 3940795015U, 3895065449U, 3849670902U, 3804606499U, 3759867474U, + 3715449162U, 3671346997U, 3627556511U, 3584073329U, 3540893168U, 3498011834U, + 3455425220U, 3413129301U, 3371120137U, 3329393864U, 3287946700U, 3246774933U, + 3205874930U, 3165243125U, 3124876025U, 3084770202U, 3044922296U, 3005329011U, + 2965987113U, 2926893432U, 2888044853U, 2849438323U, 2811070844U, 2772939474U, + 2735041326U, 2697373562U, 2659933400U, 2622718104U, 2585724991U, 2548951424U, + 2512394810U, 2476052606U, 2439922311U, 2404001468U, 2368287663U, 2332778523U, + 2297471715U, 2262364947U, 2227455964U, 2192742551U, 2158222529U, 2123893754U, + 2089754119U, 2055801552U, 2022034013U, 1988449497U, 1955046031U, 1921821672U, + 1888774511U, 1855902668U, 1823204291U, 1790677560U, 1758320682U, 1726131893U, + 1694109454U, 1662251657U, 1630556815U, 1599023271U, 1567649391U, 1536433567U, + 1505374214U, 1474469770U, 1443718700U, 1413119487U, 1382670639U, 1352370686U, + 1322218179U, 1292211689U, 1262349810U, 1232631153U, 1203054352U, 1173618059U, + 1144320946U, 1115161701U, 1086139034U, 1057251672U, 1028498358U, 999877854U, + 971388940U, 943030410U, 914801076U, 886699767U, 858725327U, 830876614U, + 803152505U, 775551890U, 748073672U, 720716771U, 693480120U, 666362667U, + 639363374U, 612481215U, 585715177U, 559064263U, 532527486U, 506103872U, + 479792461U, 453592303U, 427502463U, 401522014U, 375650043U, 349885648U, + 324227938U, 298676034U, 273229066U, 247886176U, 222646516U, 197509248U, + 172473545U, 147538590U, 122703574U, 97967701U, 73330182U, 48790236U, + 24347096U, 0U + +#if 0 /* NOT USED */ + /* The following are the values for 16-bit tables - these work fine for the + * 8-bit conversions but produce very slightly larger errors in the 16-bit + * log (about 1.2 as opposed to 0.7 absolute error in the final value). To + * use these all the shifts below must be adjusted appropriately. + */ + 65166, 64430, 63700, 62976, 62257, 61543, 60835, 60132, 59434, 58741, 58054, + 57371, 56693, 56020, 55352, 54689, 54030, 53375, 52726, 52080, 51439, 50803, + 50170, 49542, 48918, 48298, 47682, 47070, 46462, 45858, 45257, 44661, 44068, + 43479, 42894, 42312, 41733, 41159, 40587, 40020, 39455, 38894, 38336, 37782, + 37230, 36682, 36137, 35595, 35057, 34521, 33988, 33459, 32932, 32408, 31887, + 31369, 30854, 30341, 29832, 29325, 28820, 28319, 27820, 27324, 26830, 26339, + 25850, 25364, 24880, 24399, 23920, 23444, 22970, 22499, 22029, 21562, 21098, + 20636, 20175, 19718, 19262, 18808, 18357, 17908, 17461, 17016, 16573, 16132, + 15694, 15257, 14822, 14390, 13959, 13530, 13103, 12678, 12255, 11834, 11415, + 10997, 10582, 10168, 9756, 9346, 8937, 8531, 8126, 7723, 7321, 6921, 6523, + 6127, 5732, 5339, 4947, 4557, 4169, 3782, 3397, 3014, 2632, 2251, 1872, 1495, + 1119, 744, 372 +#endif +}; + +#if 0 /* UNUSED */ +static png_int_32 +png_log8bit(unsigned int x) +{ + png_uint_32 lg2 = 0U; + + /* Each time 'x' is multiplied by 2, 1 must be subtracted off the final log, + * because the log is actually negate that means adding 1. The final + * returned value thus has the range 0 (for 255 input) to 7.994 (for 1 + * input), return -1 for the overflow (log 0) case, - so the result is + * always at most 19 bits. + */ + if ((x &= 0xffU) == 0U) /* 0 input, -inf output */ + return -0xfffff; + + if ((x & 0xf0U) == 0U) + lg2 = 4U, x <<= 4; + + if ((x & 0xc0U) == 0U) + lg2 += 2U, x <<= 2; + + if ((x & 0x80U) == 0U) + lg2 += 1U, x <<= 1; + + /* result is at most 19 bits, so this cast is safe: */ + return (png_int_32)((lg2 << 16) + ((png_8bit_l2[x-128U]+32768U)>>16)); +} +#endif /* UNUSED */ + +/* The above gives exact (to 16 binary places) log2 values for 8-bit images, + * for 16-bit images we use the most significant 8 bits of the 16-bit value to + * get an approximation then multiply the approximation by a correction factor + * determined by the remaining up to 8 bits. This requires an additional step + * in the 16-bit case. + * + * We want log2(value/65535), we have log2(v'/255), where: + * + * value = v' * 256 + v'' + * = v' * f + * + * So f is value/v', which is equal to (256+v''/v') since v' is in the range 128 + * to 255 and v'' is in the range 0 to 255 f will be in the range 256 to less + * than 258. The final factor also needs to correct for the fact that our 8-bit + * value is scaled by 255, whereas the 16-bit values must be scaled by 65535. + * + * This gives a final formula using a calculated value 'x' which is value/v' and + * scaling by 65536 to match the above table: + * + * log2(x/257) * 65536 + * + * Since these numbers are so close to '1' we can use simple linear + * interpolation between the two end values 256/257 (result -368.61) and 258/257 + * (result 367.179). The values used below are scaled by a further 64 to give + * 16-bit precision in the interpolation: + * + * Start (256): -23591 + * Zero (257): 0 + * End (258): 23499 + * + * In libpng 1.7.0 this is further generalized to return -log2(value/maxval) for + * any maxval up to 65535. This is done by evaluating -log2(value/65535) first + * then adjusting for the required maxval: + * + * ( value) (value 65535) (value) ( 65535) + * -log2(------) = -log2(----- x ------) = -log2(-----)-log2(------) + * (maxval) (65535 maxval) (65535) (maxval) + * + * The extra argument, 'factor', is (2^(16+12))*log2(65535/maxval) (a positive + * value less than 2^32) and this is *subtracted* from the intermediate + * calculation below. + */ +static png_int_32 +png_log(unsigned int x, png_uint_32 factor) + /* x: a value of up to 16 bits, + * factor: a 4.28 number which is subtracted from the log below + */ +{ + png_uint_32 lg2 = 0U; + + /* As above, but now the input has 16 bits. */ + if ((x &= 0xffffU) == 0U) + return -0xfffff; + + if ((x & 0xff00U) == 0U) + lg2 = 8U, x <<= 8; + + if ((x & 0xf000U) == 0U) + lg2 += 4U, x <<= 4; + + if ((x & 0xc000U) == 0U) + lg2 += 2U, x <<= 2; + + if ((x & 0x8000U) == 0U) + lg2 += 1U, x <<= 1; + + /* Calculate the base logarithm from the top 8 bits as a 28-bit fractional + * value. + */ + lg2 <<= 28; + lg2 += (png_8bit_l2[(x>>8)-128U]+8U) >> 4; + + /* Now we need to interpolate the factor, this requires a division by the top + * 8 bits. Do this with maximum precision. + */ + { + png_uint_32 i = x; + + i = ((i << 16) + (i >> 9)) / (x>> 8); + + /* Since we divided by the top 8 bits of 'x' there will be a '1' at 1<<24, + * the value at 1<<16 (ignoring this) will be 0 or 1; this gives us + * exactly 16 bits to interpolate to get the low bits of the result. + * Round the answer. Note that the end point values are scaled by 64 to + * retain overall precision and that 'lg2' is current scaled by an extra + * 12 bits, so adjust the overall scaling by 6-12. Round at every step. + */ + i -= 1U << 24; + + if (i <= 65536U) /* <= '257' */ + lg2 += ((23591U * (65536U-i)) + (1U << (16+6-12-1))) >> (16+6-12); + + else + lg2 -= ((23499U * (i-65536U)) + (1U << (16+6-12-1))) >> (16+6-12); + } + + if (lg2 >= factor) + return (png_int_32)/*SAFE*/((lg2 - factor + 2048U) >> 12); + + else /* the result will be greater than 1.0, so negative: */ + return -(png_int_32)/*SAFE*/((factor - lg2 + 2048U) >> 12); +} + +#if 0 /* UNUSED */ +static png_int_32 +png_log16bit(unsigned int x) +{ + return png_log(x, 0U); +} +#endif /* UNUSED */ + +/* libpng 1.7.0: generalization of png_log{8,16}bit to accept an n-bit input + * value. We want to maintain 1% accuracy in linear light space. This + * corresponds to, approximately, (1*g)% in a gamma encoded space where the + * gamma encoding is 'g' (in the PNG sense, e.g. 0.45455 for sRGB). Apparently + * this requires unbounded accuracy as the gamma encoding value goes down and + * this is a problem for modern HDR data because it may require a high gamma to + * accurately encode image data over a wide dynamic range; the dynamic range of + * 16-bit linear data is only 655:1 if 1% accuracy is needed! + * + * However 16-bit gamma encoded data is still limited because PNG can only + * express gamma encoding. (A log-to-base-1.01 encoding is unlimited; a 12-bit + * value, with 4094 steps, has a dynamic range of more than 1:10^17, which + * exceeds the human eye's range of 1:10^14.) + * + * Notice that sRGB uses a 1/2.4 encoding and CIELab uses a 1/3 encoding. It is + * obvious that, if we assume a maximum D difference in the luminance of + * adjacent pixel values the dynamic range is given by the lowest pixel value + * which is D or less greater than its predecessor, so: + * + * ( P ) (1) + * (---)^(-) = D + * (P-1) (g) + * + * and the maximum dynamic range that can be achieved using M+1 separate values, + * where M+1 is 2^N-1 for an N bit value, reserving the first value for 0, is: + * + * (M) (1) + * range(R) = (-)^(-) + * (P) (g) + * + * So we can eliminate 'P' from the two equations: + * + * P = (P-1) x (D^g) + * + * D^g + * P = ----- + * D^g-1 + * + * (M x (D^g-1)) (1) + * R = (-----------)^(-) + * ( D^g ) (g) + * + * (M x (D^g-1)) ^ (1/g) + * = --------------------- + * D + * + * Which is a function in two variables (R and g) for a given D (maximum delta + * between two adjacent pixel values) and M (number of pixel values, controlled + * by the channel bit depth). + * + * See contrib/tools/dynamic-range.c for code exploring this function. This + * program will output the optimal gamma for a given number of bits and + * precision. + * + * The range of sensitivity of human vision is roughly as follows (this comes + * from the wikipedia article on scotopic vision): + * + * scotopic: 10^-6 to 10^-3.5 cd/m^2 + * mesopic: 10^-3 to 10^0.5 cd/m^2 + * photopic: 10 to 10^8 cd/m^2 + * + * Giving a total range of about 1:10^14. The maximum precision at which this + * range can be achieved using 16-bit channels is about .15% using a gamma of + * 36, higher ranges are possible using higher gammas but precision is reduced. + * The range with 1% precision and 16-bit channels is 1:10^104, using a gamma of + * 240. + * + * In general the optimal gamma for n-bit channels (where 'n' is at least 7 and + * precision is .01 or less) is: + * + * 2^n * precision + * gamma = --------------- + * 2.736 + * + * Or: (24000 * precision) for 16-bit data. + * + * The net effect is that we can't rely on the encoding gamma being limited to + * values around 1/2.5! + */ +static png_int_32 +png_log_nbit(unsigned int x, unsigned int nbits) +{ + static const png_uint_32 factors[16] = + { + 4294961387U, /* 1 bit */ + 3869501255U, /* 2 bit */ + 3541367788U, /* 3 bit */ + 3246213428U, /* 4 bit */ + 2965079441U, /* 5 bit */ + 2690447525U, /* 6 bit */ + 2418950626U, /* 7 bit */ + 2148993476U, /* 8 bit */ + 1879799410U, /* 9 bit */ + 1610985205U, /* 10 bit */ + 1342360514U, /* 11 bit */ + 1073830475U, /* 12 bit */ + 805347736U, /* 13 bit */ + 536888641U, /* 14 bit */ + 268441365U, /* 15 bit */ + 0U /* 16 bit */ + }; + + return png_log(x, factors[nbits-1]); +} + + +/* The 'exp()' case must invert the above, taking a 20-bit fixed point + * logarithmic value and returning a 16 or 8-bit number as appropriate. In + * each case only the low 16 bits are relevant - the fraction - since the + * integer bits (the top 4) simply determine a shift. + * + * The worst case is the 16-bit distinction between 65535 and 65534. This + * requires perhaps spurious accuracy in the decoding of the logarithm to + * distinguish log2(65535/65534.5) - 10^-5 or 17 bits. There is little chance + * of needing this accuracy in practice. + * + * To deal with this the following exp() function works out the exponent of the + * frational part of the logarithm by using an accurate 32-bit value from the + * top four fractional bits then multiplying in the remaining bits. + */ +static const png_uint_32 +png_32bit_exp[16] = +{ + /* NOTE: the first entry is deliberately set to the maximum 32-bit value. */ + 4294967295U, 4112874773U, 3938502376U, 3771522796U, 3611622603U, 3458501653U, + 3311872529U, 3171459999U, 3037000500U, 2908241642U, 2784941738U, 2666869345U, + 2553802834U, 2445529972U, 2341847524U, 2242560872U +}; + +/* Adjustment table; provided to explain the numbers in the code below. */ +#if 0 /* BC CODE */ +for (i=11;i>=0;--i){ print i, " ", (1 - e(-(2^i)/65536*l(2))) * 2^(32-i), "\n"} + 11 44937.64284865548751208448 + 10 45180.98734845585101160448 + 9 45303.31936980687359311872 + 8 45364.65110595323018870784 + 7 45395.35850361789624614912 + 6 45410.72259715102037508096 + 5 45418.40724413220722311168 + 4 45422.25021786898173001728 + 3 45424.17186732298419044352 + 2 45425.13273269940811464704 + 1 45425.61317555035558641664 + 0 45425.85339951654943850496 +#endif + +static png_uint_32 +png_exp(png_int_32 x) + /* Utility, the value 'x' must be in the range 0..0x1fffff */ +{ + /* Obtain a 4-bit approximation */ + png_uint_32 e = png_32bit_exp[(x >> 12) & 0xf]; + + /* Incorporate the low 12 bits - these decrease the returned value by + * multiplying by a number less than 1 if the bit is set. The multiplier + * is determined by the above table and the shift. Notice that the values + * converge on 45426 and this is used to allow linear interpolation of the + * low bits. + */ + if (x & 0x800) + e -= (((e >> 16) * 44938U) + 16U) >> 5; + + if (x & 0x400) + e -= (((e >> 16) * 45181U) + 32U) >> 6; + + if (x & 0x200) + e -= (((e >> 16) * 45303U) + 64U) >> 7; + + if (x & 0x100) + e -= (((e >> 16) * 45365U) + 128U) >> 8; + + if (x & 0x080) + e -= (((e >> 16) * 45395U) + 256U) >> 9; + + if (x & 0x040) + e -= (((e >> 16) * 45410U) + 512U) >> 10; + + /* And handle the low 6 bits in a single block. */ + e -= (((e >> 16) * 355U * (x & 0x3fU)) + 256U) >> 9; + + /* Handle the upper bits of x, note that this works for x up to 0x1fffff but + * fails for larger or negative x, where the shift (x >> 16) exceeds 31: + */ + e >>= x >> 16; + return e; +} + +#if 0 /* UNUSED */ +static png_byte +png_exp8bit(png_int_32 lg2) +{ + /* The input is a negative fixed point (16:16) logarithm with a useable range + * of [0.0..8.0). Clamp the value so that the output of png_exp is in the + * range (254.5/255..0.5/255): + */ + if (lg2 <= 185) /* -log2(254.5/255) */ + return 255U; + + else if (lg2 > 589453) /* -log2(0.5/255) */ + return 0U; + + else + { + /* Get a 32-bit value: */ + png_uint_32 x = png_exp(lg2); + + /* Convert the 32-bit value to 0..255 by multiplying by 256-1. Note that + * the second, rounding, step can't overflow because of the first, + * subtraction, step. + */ + x -= x >> 8; + return PNG_BYTE((x + 0x7fffffU) >> 24); + } +} + +static png_uint_16 +png_exp16bit(png_int_32 lg2) +{ + if (lg2 <= 0) /* -log2(65534.5/65535) */ + return 65535U; + + else if (lg2 > 1114110) /* -log2(0.5/65535) */ + return 0U; + + else + { + /* Get a 32-bit value: */ + png_uint_32 x = png_exp(lg2); + + /* Convert the 32-bit value to 0..65535 by multiplying by 65536-1: */ + x -= x >> 16; + return PNG_UINT_16((x + 32767U) >> 16); + } +} +#endif /* UNUSED */ + +static png_uint_32 +png_exp_nbit(png_int_32 lg2, unsigned int n) +{ + /* These pre-computed limits give the low value of lg2 at and below which + * 2^(-lg2/65536) * (2^n-1) gives (2^n-1) and the high value of lg2 above + * which 2(^-lg2/65536) * (2^n-1) gives 0: + */ + static const png_int_32 limits[16][2] = + { + { 65535, 65535 }, /* bits = 1 */ + { 17238, 169408 }, /* bits = 2 */ + { 7006, 249518 }, /* bits = 3 */ + { 3205, 321577 }, /* bits = 4 */ + { 1537, 390214 }, /* bits = 5 */ + { 753, 457263 }, /* bits = 6 */ + { 372, 523546 }, /* bits = 7 */ + { 185, 589453 }, /* bits = 8 */ + { 92, 655175 }, /* bits = 9 */ + { 46, 720803 }, /* bits = 10 */ + { 23, 786385 }, /* bits = 11 */ + { 11, 851944 }, /* bits = 12 */ + { 5, 917492 }, /* bits = 13 */ + { 2, 983034 }, /* bits = 14 */ + { 1, 1048573 }, /* bits = 15 */ + { 0, 1114110 } /* bits = 16 */ + }; + + /* If 'max' is 2^n-1: */ + if (lg2 <= limits[n-1][0]) /* -log2((max-.5)/max) */ + return (1U << n)-1U; + + else if (lg2 > limits[n-1][1]) /* -log2(.5/max) */ + return 0U; + + else /* 'n' will be at least 2 */ + { + /* Get a 32-bit value: */ + png_uint_32 x = png_exp(lg2); + + /* Convert the 32-bit value to 0..(2^n-1) by multiplying by 2^n-1: */ + x -= x >> n; + return (x + ((1U<<(31U-n))-1U)) >> (32U-n); + } +} +#endif /* !FLOATING_ARITHMETIC */ + +#if 0 /* UNUSED */ +static png_byte +png_gamma_8bit_correct(unsigned int value, png_fixed_point gamma_val) +{ + if (value == 0U) + return 0U; + + else if (value >= 255U) + return 255U; + + else + { +# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + /* 'value' is unsigned, ANSI-C90 requires the compiler to correctly + * convert this to a floating point value. This includes values that + * would overflow if 'value' were to be converted to 'int'. + * + * Apparently GCC, however, does an intermediate conversion to (int) + * on some (ARM) but not all (x86) platforms, possibly because of + * hardware FP limitations. (E.g. if the hardware conversion always + * assumes the integer register contains a signed value.) This results + * in ANSI-C undefined behavior for large values. + * + * Other implementations on the same machine might actually be ANSI-C90 + * conformant and therefore compile spurious extra code for the large + * values. + * + * We can be reasonably sure that an unsigned to float conversion + * won't be faster than an int to float one. Therefore this code + * assumes responsibility for the undefined behavior, which it knows + * can't happen because of the check above. + * + * Note the argument to this routine is an (unsigned int) because, on + * 16-bit platforms, it is assigned a value which might be out of + * range for an (int); that would result in undefined behavior in the + * caller if the *argument* ('value') were to be declared (int). + */ + double r = 255*pow((int)/*SAFE*/value/255.,gamma_val*.00001); + if (r < .5) + return 0U; + + else if (r >= 254.5) + return 255U; + + r = floor(r+.5); + return (png_byte)/*SAFE*/r; +# else + png_int_32 lg2 = png_log8bit(value); + png_int_32 res; + + /* Overflow in the muldiv means underflow in the calculation, this is + * OK (it happens for ridiculously high gamma). + */ + if (!png_muldiv(&res, gamma_val, lg2, PNG_FP_1)) + return 0U; /* underflow */ + + return png_exp8bit(res); +# endif + } +} +#endif /* UNUSED */ + +/* libpng-1.7.0: this private function converts an n-bit input value to an + * m-bit output value. + */ +unsigned int +png_gamma_nxmbit_correct(unsigned int value, png_fixed_point gamma_val, + unsigned int n/*input bits*/, unsigned int m/*output bits */) +{ + if (value == 0U) + return 0U; + + else + { + unsigned int min = (1U<= min) + return mout; + + else + { +# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + double r = value; + r /= min; + r = floor(mout * pow(r, gamma_val*.00001)+.5); + if (r < 1) + return 0U; + + else if (r >= mout) + return mout; + + return (unsigned int)/*SAFE*/r; +# else + png_int_32 lg2 = png_log_nbit(value, n); + png_int_32 res; + + if (!png_muldiv(&res, gamma_val, lg2, PNG_FP_1)) + return 0U; /* underflow */ + + return png_exp_nbit(res, m); +# endif + } + } +} + +#if 0 /*UNUSED*/ +static unsigned int +png_gamma_sbit_correct(unsigned int value, png_fixed_point gamma_val, + unsigned int n/*input bits*/, unsigned int sbits, + unsigned int m/*output bits */) + /* As above but the number of significant bits in 'n' is passed in. */ +{ + if (sbits < n) + { + value >>= (n-sbits); + n = sbits; + } + + return png_gamma_nxmbit_correct(value, gamma_val, n, m); +} +#endif /*UNUSED*/ + +static int +push_gamma_expand(png_transformp *transform, png_transform_controlp tc, + int need_alpha) + /* Utility to push a transform to expand low-bit-depth gray and, where + * required, tRNS chunks. The caller must return immediately if this + * returns true because the init of the new transform has been run in place + * of the caller's. + */ +{ +# define png_ptr (tc->png_ptr) + unsigned int expand = 0; + + affirm(tc->init == PNG_TC_INIT_FINAL); + + if (tc->bit_depth < 8U) /* low bit gray: expand to 8 bits */ + expand = PNG_EXPAND_LBD_GRAY; + + /* Gamma correction invalidates tRNS, so if it is being expanded and + * alpha is not being stripped expand it now. + */ + if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0 && !tc->palette && + png_ptr->num_trans == 1 && (tc->invalid_info & PNG_INFO_tRNS) == 0) + { + if (need_alpha || (tc->expand_tRNS && !tc->strip_alpha)) + expand |= PNG_EXPAND_tRNS; + + else + tc->invalid_info |= PNG_INFO_tRNS; + } + + if (expand == 0) + return 0; /* nothing needs to be done */ + + { + png_transformp tr = png_push_transform(png_ptr, sizeof (png_expand), + png_init_expand, transform, NULL/*don't run init*/); + + debug(tr == *transform); + tr->args |= expand; + + /* This must be run immediately, because it just got inserted where this + * transform is; this is safe, the caller must return immediately. + */ + png_init_expand(transform, tc); + affirm(tr->fn != NULL); /* because it should need to do something! */ + } + + return 1; +# undef png_ptr +} + +/* Low bit depth gray gamma correction. The 1-bit case is a no-op because 0 and + * 1 always map to 0 and 1. The 2-bit case has the following possiblities: + * + * bits/correction: g0 g1 g2 g3 g4 g5 g6 + * 00 -> 00 00 00 00 00 00 00 + * 01 -> 11 10 10 01 00 00 00 + * 10 -> 11 11 10 10 10 01 00 + * 11 -> 11 11 11 11 11 11 11 + * + * Where the breakpoints are: + * + * g0: correction <= 16595 (1 - log(2.5/3)) + * g1: 16595 < correction <= 44966 (log(2.5/3)/log(2/3)) + * g2: 44966 < correction <= 63092 (1 - log(1.5/3)) + * g3: 63092 < correction <= 163092 (1 - log(.5/3)) + * g4: 163092 < correction <= 170951 (log(1.5/3)/log(2/3)) + * g5: 170951 < correction <= 441902 (log(.5/3)/log(2/3) + * g6 441902 < correction + * + * This can be done by bit-hacking on the byte values (4 pixels), given that + * the correction is fixed (indeed, it can be done on whole 32-bit values!) + * + * g0: B |= B>>1; B &= 0x55U; B |= B<<1; * either bit set + * g1: B ^= B>>1; B &= 0x55U; B += B; * one bit set + * g2: B &= (~B)>>1; B &= 0x55U; B += B; * low bit set, high bit unset + * g3: no-op + * g4: B &= (~B)>>1; B &= 0x55U; B -= B; * low bit set, high bit unset + * g5: B ^= B>>1; B &= 0x55U; B -= B; * one bit set + * g6: B &= B>>1; B &= 0x55U; B |= B<<1; * both bits set + */ +typedef struct +{ + png_transform tr; + png_fixed_point correct; + png_fixed_point to_gamma; + png_uint_32 shifts; /* 1 followed by up to 4 4-bit shifts */ + png_uint_32 channel_scale[4]; /* up to 4 channel scale factors */ + /* These factors are used: + * + * (input >> (shifts & 0xFU) * channel_scale + SCALE_R) >> SCALE_S + * + * Where the rounding value, SCALE_R and the shift SCALE_S are dependent + * on the bit depth: + * + * SCALE_S = 32 - bit_depth range 16..31 + * SCALE_R = 1 << (SCALE_S-1) + */ + unsigned int to_bit_depth; + unsigned int encode_alpha :1; + unsigned int optimize_alpha :1; +} png_transform_gamma; + +static unsigned int +init_gamma_sBIT(png_transform_gamma *tr, png_transform_controlp tc) + /* Returns true if sBIT processing is required, otherwise all relevant sBIT + * values match the from (tc) bit depth. + */ +{ + /* The to_bit_depth and to_gamma fields are already set, but updated values + * are needed for sBIT and the shifts and channel_scale fields must be filled + * in correctly. The do_gamma setting says whether gamma correction will be + * done, but the scale factors are filled in regardless. + * + * The general scaling equation is: + * + * ((in >> shift) * factor + round) >> (32 - to_bit_depth) + * + * 'factor' is then the rounded value of: + * + * out_max + * ------- . (1 << (32-to_bit_depth)) + * in_max + */ +# define png_ptr (tc->png_ptr) + const unsigned int to_bit_depth = tr->to_bit_depth; + const png_uint_32 numerator = ((1U<bit_depth; + + /* The data in the gamma transform is stored in the order of the channels in + * the input row, which is the PNG order. It may be reversed below. + */ + png_uint_32p channel_scale = tr->channel_scale; + png_uint_32 shifts = 0U; + unsigned int count = 0U; + unsigned int need_sBIT = 0U; + + if (tc->format & PNG_FORMAT_FLAG_COLOR) + { + const unsigned int sBIT = tc->sBIT_R; + + if (sBIT < from_bit_depth) + need_sBIT = 1U; + + debug(sBIT > 0U && sBIT <= from_bit_depth); + shifts |= (from_bit_depth - sBIT) << count; + count += 4U; + /* round the scale: */ + *channel_scale++ = (numerator + (1U<<(sBIT-1U))) / ((1U << sBIT)-1U); + } + + { + const unsigned int sBIT = tc->sBIT_G; + + if (sBIT < from_bit_depth) + need_sBIT = 1U; + + debug(sBIT > 0U && sBIT <= from_bit_depth); + shifts |= (from_bit_depth - sBIT) << count; + count += 4U; + *channel_scale++ = (numerator + (1U<<(sBIT-1U))) / ((1U << sBIT)-1U); + } + + if (tc->format & PNG_FORMAT_FLAG_COLOR) + { + const unsigned int sBIT = tc->sBIT_B; + + if (sBIT < from_bit_depth) + need_sBIT = 1U; + + debug(sBIT > 0U && sBIT <= from_bit_depth); + shifts |= (from_bit_depth - sBIT) << count; + count += 4U; + /* round the scale: */ + *channel_scale++ = (numerator + (1U<<(sBIT-1U))) / ((1U << sBIT)-1U); + } + + if (tc->format & PNG_FORMAT_FLAG_ALPHA) + { + const unsigned int sBIT = tc->sBIT_A; + + if (sBIT < from_bit_depth) + need_sBIT = 1U; + + debug(sBIT > 0U && sBIT <= from_bit_depth); + shifts |= (from_bit_depth - sBIT) << count; + count += 4U; + /* round the scale: */ + *channel_scale++ = (numerator + (1U<<(sBIT-1U))) / ((1U << sBIT)-1U); + } + + tr->shifts = shifts | (1U << count); + + return need_sBIT; +# undef png_ptr +} + +static void +reverse_gamma_sBIT(png_transform_gamma *tr) +{ + /* This is called for the 'down' gamma implementations, they read the shifts + * and the channel scales in reverse, so: + */ + png_uint_32 shifts = tr->shifts; + png_uint_32 scales[4U]; + unsigned int count = 0U; + + tr->shifts = 1U; + + while (shifts != 1U) + { + scales[3U-count] = tr->channel_scale[count]; + ++count; + tr->shifts <<= 4; + tr->shifts |= shifts & 0xFU; + shifts >>= 4; + } + + memcpy(tr->channel_scale, scales+(4U-count), count * sizeof (png_uint_32)); +} + +static void +png_do_gamma8_up(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_transform_gamma *tr = + png_transform_cast(png_transform_gamma, *transform); + const png_fixed_point correct = tr->correct; + const unsigned int bit_depth = tr->to_bit_depth; + const png_uint_32 shifts = tr->shifts; + + affirm(tc->bit_depth == 8U); + affirm(tr->shifts != 0U/*uninitialized*/); + debug((shifts & 0x8888U) == 0U); /* all shifts 7 or less */ + debug(!tr->encode_alpha && !tr->optimize_alpha); /* only set for 16 bits */ + + tc->sp = dp; + tc->bit_depth = bit_depth; + tc->gamma = tr->to_gamma; + + /* Handle the <8 bit output case differently because there can be no alpha + * channel. + */ + if (bit_depth < 8U) + { + const unsigned int shift = shifts & 0xFU; + unsigned int bits = 8U; + unsigned int ob = 0U; + + debug((shifts >> 4) == 1U && shift < 8U); + affirm(PNG_TC_CHANNELS(*tc) == 1); + + do + { + const unsigned int inb = png_gamma_nxmbit_correct( + *sp++ >> shift, correct, 8U-shift, bit_depth); + bits -= bit_depth; + ob = ob | (inb << bits); + if (bits == 0U) + bits = 8U, *dp++ = PNG_BYTE(ob), ob = 0U; + } + while (sp < ep); + + if (bits < 8U) + *dp++ = PNG_BYTE(ob); + } + + else /* 8-bit --> 8-bit */ + { + png_uint_32 alpha_scale; + const unsigned int channels = PNG_TC_CHANNELS(*tc); + unsigned int channel, alpha; + + debug(bit_depth == 8U && (shifts >> (4*channels)) == 1U); + + /* The alpha channel is always last, so if present checking against the + * top bits of 'channels' works because of the 1U shibboleth at the end. + */ + if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0) + alpha_scale = alpha = 0U; + + else + { + alpha = shifts >> (4U*(channels-1U)); + alpha_scale = tr->channel_scale[channels-1U]; + } + + channel = 1U; + + do + { + unsigned int inb = *sp++, shift; + + if (channel == 1U) + channel = shifts; + + shift = channel & 0xFU; + inb >>= shift; + + /* The alpha channel is not gamma encoded but it may need some + * appropriate scaling. + */ + if (channel == alpha) + inb = (inb * alpha_scale + 0x800000U) >> 24; + + else + inb = png_gamma_nxmbit_correct(inb, correct, 8U-shift, 8U); + + channel >>= 4; /* for the next channel, or the shibboleth */ + *dp++ = PNG_BYTE(inb); + } + while (sp < ep); + + debug(channel == 1U); + } +# undef png_ptr +} + +static void +png_do_gamma16_up(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 1U/*safety*/; + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_transform_gamma *tr = + png_transform_cast(png_transform_gamma, *transform); + const png_fixed_point correct = tr->correct; + const unsigned int bit_depth = tr->to_bit_depth; + const png_uint_32 shifts = tr->shifts; + + affirm(tc->bit_depth == 16U); + affirm(tr->shifts != 0U/*uninitialized*/); + debug(!tr->optimize_alpha); + + /* This is exactly the same as above but the input has 16 bits per component, + * not 8. + */ + tc->sp = dp; + tc->bit_depth = bit_depth; + tc->gamma = tr->to_gamma; + + /* Handle the <8 bit output case differently, the input cannot be color (at + * present) and, if there is an alpha channel, then it is for the + * low-bit-depth gray input case and we expect the alpha to be transparent. + */ + if (bit_depth < 8U) + { + const unsigned int shift = shifts & 0xFU; + unsigned int bits = 8U; + unsigned int ob = 0U; + + affirm((tc->format & PNG_FORMAT_FLAG_COLOR) == 0U); + + if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0U) + { + debug((shifts >> 4) == 1U && shift < 16U); + debug(!tr->encode_alpha && !tr->optimize_alpha); + + do + { + unsigned int inb = *sp++ << 8; /* high bits first */ + inb = png_gamma_nxmbit_correct( + (inb + *sp++) >> shift, correct, 16U-shift, bit_depth); + + bits -= bit_depth; + ob = ob | (inb << bits); + if (bits == 0U) + bits = 8U, *dp++ = PNG_BYTE(ob), ob = 0U; + } + while (sp < ep); + + UNTESTED + } + + else /* low bit GA intermediate format */ + { + debug((shifts >> 8) == 1U && shift < 16U); + debug(!tr->encode_alpha && !tr->optimize_alpha); + debug(tc->transparent_alpha); + + /* Gray is first then the alpha component, the alpha component is just + * mapped to 0 or 1. + */ + do + { + unsigned int gray = *sp++ << 8; /* high bits first */ + unsigned int alpha; + gray += *sp++; + + alpha = (*sp++ << 8); + alpha += *sp++; + + if (alpha == 0U) + gray = 0U; /* will be replaced later */ + + else + { + gray = png_gamma_nxmbit_correct(gray >> shift, correct, + 16U-shift, bit_depth); + debug(alpha == 65535U); + alpha = (1U << bit_depth)-1U; + } + + bits -= bit_depth; + ob = ob | (gray << bits); + bits -= bit_depth; + ob = ob | (alpha << bits); + + if (bits == 0U) + bits = 8U, *dp++ = PNG_BYTE(ob), ob = 0U; + } + while (sp < ep-2U); + } + + if (bits < 8U) + *dp++ = PNG_BYTE(ob); + + debug(sp == ep+1U); + } + + else + { + png_uint_32 alpha_scale; + const unsigned int channels = PNG_TC_CHANNELS(*tc); + unsigned int channel, alpha; + + debug((bit_depth == 8U || bit_depth == 16U) && + (shifts >> (4*channels)) == 1U); + + /* Note that 'encode_alpha' turns on gamma encoding of the alpha + * channel (and this is a really weird operation!) + */ + if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0 || tr->encode_alpha) + alpha_scale = alpha = 0U; + + else + { + alpha = shifts >> (4U*(channels-1U)); + alpha_scale = tr->channel_scale[channels-1U]; + } + + channel = 1U; + + if (bit_depth == 16U) + { + do + { + unsigned int inb = *sp++ << 8, shift; + inb += *sp++; + + if (channel == 1U) + channel = shifts; + + shift = channel & 0xFU; + inb >>= shift; + + /* The 16-16bit scaling factor equation may be off-by-1 but this + * hardly matters for alpha or for gamma operations. + */ + if (channel == alpha) + inb = (inb * alpha_scale + 0x8000U) >> 16; + + else + inb = png_gamma_nxmbit_correct(inb, correct, 16U-shift, 16U); + + channel >>= 4; /* for the next channel, or the shibboleth */ + *dp++ = PNG_BYTE(inb >> 8); + *dp++ = PNG_BYTE(inb); + } + while (sp < ep); + + debug(channel == 1U && sp == ep+1U); + } + + else /* bit_depth == 8U */ + { + do + { + unsigned int inb = *sp++ << 8, shift; + inb += *sp++; + + if (channel == 1U) + channel = shifts; + + shift = channel & 0xFU; + inb >>= shift; + + if (channel == alpha) + inb = (inb * alpha_scale + 0x800000U) >> 24; + + else + inb = png_gamma_nxmbit_correct(inb, correct, 16U-shift, 8U); + + channel >>= 4; /* for the next channel, or the shibboleth */ + *dp++ = PNG_BYTE(inb); + } + while (sp < ep); + + debug(channel == 1U && sp == ep+1U); + } + } +# undef png_ptr +} + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +static void +png_do_gamma16_up_optimize(png_transformp *transform, png_transform_controlp tc) + /* As above, but the alpha channel is 'optimized' */ +{ +# define png_ptr (tc->png_ptr) + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_transform_gamma *tr = + png_transform_cast(png_transform_gamma, *transform); + const png_fixed_point correct = tr->correct; + + /* The input always as 16 bits, the output 8 or 16. There is always an alpha + * channel and it is converted to the 'optimized' form, where pixels with + * alpha not 0.0 or 1.0 are left in linear form (not gamma corrected.) Where + * bit depth convertion is required it is from 16-bits to 8-bits and the + * DIV257 macro can be used. + * + * The following affirms and NOT_REACHED cases are consequences of the way + * the background (compose) code works: + */ + affirm(tr->optimize_alpha && !tr->encode_alpha && tc->bit_depth == 16U); + + /* TODO: split this into separate functions */ + switch (tr->to_bit_depth) + { + case 8U: /* 16-bit --> 8-bit */ + tc->sp = dp; + tc->bit_depth = 8U; + tc->gamma = tr->to_gamma; + + switch (PNG_TC_CHANNELS(*tc)) + { + case 2:/* GA */ + debug(tr->shifts == 0x100U); + ep -= 3U; /*SAFETY*/ + + do + { + png_uint_32 alpha = PNG_DIV257((sp[2] << 8) + sp[3]); + + switch (alpha) + { + case 0U: + dp[1] = dp[0] = 0U; + break; + + default: /* optimized case: linear color data */ + dp[0] = png_check_byte(png_ptr, + PNG_DIV257((sp[0] << 8) + sp[1])); + dp[1] = PNG_BYTE(alpha); + break; + + case 255U: /* opaque pixels are encoded */ + dp[0] = PNG_BYTE(png_gamma_nxmbit_correct( + (sp[0] << 8) + sp[1], correct, 16U, 8U)); + dp[1] = 255U; + break; + } + + sp += 4U; + dp += 2U; + } + while (sp < ep); + + debug(sp == ep+3U); + break; + + case 4:/* RGBA */ + debug(tr->shifts == 0x10000U); + ep -= 7U; /*SAFETY*/ + + do + { + png_uint_32 alpha = PNG_DIV257((sp[6] << 8) + sp[7]); + + switch (alpha) + { + case 0U: + memset(dp, 0U, 4U); + break; + + default: /* optimized case: linear color data */ + dp[0] = PNG_BYTE(PNG_DIV257((sp[0] << 8) + sp[1])); + dp[1] = PNG_BYTE(PNG_DIV257((sp[2] << 8) + sp[3])); + dp[2] = PNG_BYTE(PNG_DIV257((sp[4] << 8) + sp[5])); + dp[3] = PNG_BYTE(alpha); + break; + + case 255U: /* opaque pixels are encoded */ + dp[0] = PNG_BYTE(png_gamma_nxmbit_correct( + (sp[0] << 8) + sp[1], correct, 16U, 8U)); + dp[1] = PNG_BYTE(png_gamma_nxmbit_correct( + (sp[2] << 8) + sp[3], correct, 16U, 8U)); + dp[2] = PNG_BYTE(png_gamma_nxmbit_correct( + (sp[4] << 8) + sp[5], correct, 16U, 8U)); + dp[3] = 255U; + break; + } + + sp += 8U; + dp += 4U; + } + while (sp < ep); + + debug(sp == ep+7U); + break; + + default: + NOT_REACHED; + break; + } + break; + + case 16: /* 16-bit to 16-bit */ + tc->sp = dp; + tc->bit_depth = 16U; + tc->gamma = tr->to_gamma; + + switch (PNG_TC_CHANNELS(*tc)) + { + case 2:/* GA */ + debug(tr->shifts == 0x100U); + ep -= 3U; /*SAFETY*/ + + do + { + unsigned int alpha = (sp[2] << 8) + sp[3]; + + switch (alpha) + { + case 0U: + memset(dp, 0U, 4U); + break; + + default: /* optimized case: linear color data */ + if (dp != sp) + { + memcpy(dp, sp, 4U); + UNTESTED + } + break; + + case 65535U: /* opaque pixels are encoded */ + { + unsigned int gray = png_gamma_nxmbit_correct( + (sp[0] << 8) + sp[1], correct, 16U, 16U); + dp[0] = PNG_BYTE(gray >> 8); + dp[1] = PNG_BYTE(gray); + } + dp[3] = dp[2] = 255U; + break; + } + + sp += 4U; + dp += 4U; + } + while (sp < ep); + + debug(sp == ep+3U); + break; + + case 4:/* RGBA */ + debug(tr->shifts == 0x10000U); + ep -= 7U; /*SAFETY*/ + + do + { + unsigned int alpha = (sp[6] << 8) + sp[7]; + + switch (alpha) + { + case 0U: + memset(dp, 0U, 8U); + break; + + default: /* optimized case: linear color data */ + if (dp != sp) + { + memcpy(dp, sp, 8U); + UNTESTED + } + break; + + case 65535U: /* opaque pixels are encoded */ + { + unsigned int c = png_gamma_nxmbit_correct( + (sp[0] << 8) + sp[1], correct, 16U, 16U); + dp[0] = PNG_BYTE(c >> 8); + dp[1] = PNG_BYTE(c); + + c = png_gamma_nxmbit_correct( + (sp[2] << 8) + sp[3], correct, 16U, 16U); + dp[2] = PNG_BYTE(c >> 8); + dp[3] = PNG_BYTE(c); + + c = png_gamma_nxmbit_correct( + (sp[4] << 8) + sp[5], correct, 16U, 16U); + dp[4] = PNG_BYTE(c >> 8); + dp[5] = PNG_BYTE(c); + } + dp[7] = dp[6] = 255U; + break; + } + + sp += 8U; + dp += 8U; + } + while (sp < ep); + + debug(sp == ep+7U); + break; + + default: + NOT_REACHED; + break; + } + break; + + default: + NOT_REACHED; + break; + } +# undef png_ptr +} +#endif /* READ_ALPHA_MODE */ + +static void +png_do_scale16_up(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_transform_gamma *tr = + png_transform_cast(png_transform_gamma, *transform); + const unsigned int bit_depth = tr->to_bit_depth; + + affirm(tc->bit_depth == 16U && bit_depth < 8U); + affirm(tr->shifts != 0U/*uninitialized*/); + + /* This is exactly the same as above but without the gamma correction and + * without the 8-bit target support. The code handles one or two channels, + * but the result is not a PNG format unless the number of channels is just + * 1 (grayscale). + * + * For multi-channel low bit depth the channels are packed into bytes using + * the standard PNG big-endian packing. + */ + affirm((tc->format & PNG_FORMAT_FLAG_COLOR) == 0); + /* The alpha shift is actually ignored; at present we only get here with an + * alpha channel if it is to be removed for transparent alpha processing. + */ + debug(tc->format & PNG_FORMAT_FLAG_ALPHA ? + (tr->shifts >> 8) == 1U : (tr->shifts >> 4) == 1U); + debug(tc->transparent_alpha); + + tc->sp = dp; + /* This is a pure scaling operation so sBIT is not invalidated or altered. */ + tc->bit_depth = bit_depth; + + /* TODO: maybe do this properly and use the alpha shift, but only the top bit + * matters. + */ + { + const unsigned int shift = tr->shifts & 0xFU; + const png_uint_32 factor = tr->channel_scale[0]; + const png_uint_32 round = 1U << (31U-bit_depth); + unsigned int bits = 8U; + unsigned int ob = 0U; + + do + { + png_uint_32 inb = *sp++ << 8; /* high bits first */ + inb += *sp++; + + inb = ((inb >> shift) * factor + round) >> (32U-bit_depth); + bits -= bit_depth; + ob = ob | (inb << bits); + if (bits == 0U) + bits = 8U, *dp++ = PNG_BYTE(ob), ob = 0U; + } + while (sp < ep); + + if (bits < 8U) + *dp++ = PNG_BYTE(ob); + } +# undef png_ptr +} + +static void +png_do_gamma8_down(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep ep = dp + 1U/*safety*/; + png_transform_gamma *tr = + png_transform_cast(png_transform_gamma, *transform); + const png_fixed_point correct = tr->correct; + const png_uint_32 shifts = tr->shifts; + + affirm(tc->bit_depth == 8U && tr->to_bit_depth == 16U); + affirm(tr->shifts != 0U/*uninitialized*/); + debug((shifts & 0x8888U) == 0U); /* all shifts 7 or less */ + debug(!tr->encode_alpha && !tr->optimize_alpha); /* only set for 16 bits */ + + sp += PNG_TC_ROWBYTES(*tc); + tc->sp = dp; + tc->bit_depth = tr->to_bit_depth; + tc->gamma = tr->to_gamma; + dp += PNG_TC_ROWBYTES(*tc); + + { + png_uint_32 alpha_scale; + unsigned int channel, alpha; + + debug((shifts >> (4*PNG_TC_CHANNELS(*tc))) == 1U); + + /* We are going down so alpha, if present, is first. Notice that the init + * routine has to reverse both 'shifts' and 'channel_scale' for the _down + * cases. + */ + if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0) + alpha_scale = alpha = 0U; + + else + { + alpha = shifts; + alpha_scale = tr->channel_scale[0U]; + } + + channel = 1U; + + do /* 8-bit --> 16-bit */ + { + unsigned int inb = *--sp, shift; + + if (channel == 1U) + channel = shifts; + + shift = channel & 0xFU; + inb >>= shift; + + if (channel == alpha) /* unencoded alpha, must scale */ + inb = (inb * alpha_scale + 0x8000U) >> 16; + + else + inb = png_gamma_nxmbit_correct(inb, correct, 8U-shift, 16U); + + channel >>= 4; + + *--dp = PNG_BYTE(inb); + *--dp = PNG_BYTE(inb >> 8); + } + while (dp > ep); + + debug(channel == 1U && dp == ep-1U); + } +# undef png_ptr +} + +static void +png_do_expand8_down(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep ep = dp + 1U/*safety*/; + png_transform_gamma *tr = + png_transform_cast(png_transform_gamma, *transform); + const png_uint_32 shifts = tr->shifts; + + affirm(tc->bit_depth == 8U && tr->to_bit_depth == 16U); + affirm(tr->shifts != 0U/*uninitialized*/); + + sp += PNG_TC_ROWBYTES(*tc); + tc->sp = dp; + tc->bit_depth = 16U; + dp += PNG_TC_ROWBYTES(*tc); + + { + png_uint_32 channel = 1U; + png_const_uint_32p scale = 0U; + + do /* 8-bit -> 16-bit */ + { + unsigned int inb = *--sp, shift; + + if (channel == 1U) + channel = shifts, scale = tr->channel_scale; + + shift = channel & 0xFU; + channel >>= 4; + inb >>= shift; + inb = (inb * *scale++ + 0x8000U) >> 16; + /* dp starts beyond the end: */ + *--dp = PNG_BYTE(inb); + *--dp = PNG_BYTE(inb >> 8); + } + while (dp > ep); + + debug(channel == 1U && dp == ep-1U); + } +# undef png_ptr +} + +static void +png_do_expand8_down_fast(png_transformp *transform, png_transform_controlp tc) + /* Optimized version of the above for when the sBIT settings are all a full 8 + * bits (the normal case). + */ +{ +# define png_ptr (tc->png_ptr) + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep ep = dp + 1U/*safety*/; + png_transform_gamma *tr = + png_transform_cast(png_transform_gamma, *transform); + + affirm(tc->bit_depth == 8U && tr->to_bit_depth == 16U); + affirm(tr->shifts != 0U/*uninitialized*/); + + sp += PNG_TC_ROWBYTES(*tc); + tc->sp = dp; + tc->bit_depth = 16U; + dp += PNG_TC_ROWBYTES(*tc); + + do + dp -= 2, dp[0] = dp[1] = *--sp; + while (dp > ep); + + debug(dp == ep-1U); +# undef png_ptr +} + +static void +png_init_gamma_uncached(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_gamma *tr = + png_transform_cast(png_transform_gamma, *transform); + + debug(tc->init == PNG_TC_INIT_FINAL); + + /* Set this first; the result says if the sBIT data is significant, but it is + * ignored here. + */ + (void)init_gamma_sBIT(tr, tc); + + /* If png_set_alpha_mode is called but no background processing needs to be + * done (because there is no alpha channel or tRNS) we get to here with + * potentially spurious alpha mode flags. + */ + if (!(tc->format & PNG_FORMAT_FLAG_ALPHA)) + tr->encode_alpha = tr->optimize_alpha = 0U; + + /* Use separate functions for the two input depths but not for the five + * possible output depths and four channel counts. + */ + if (tc->bit_depth == 8U) + { + if (tr->to_bit_depth <= 8U) + tr->tr.fn = png_do_gamma8_up; + + else + { + debug(tr->to_bit_depth == 16U); + reverse_gamma_sBIT(tr); + tr->tr.fn = png_do_gamma8_down; + } + } + + else + { + affirm(tc->bit_depth == 16U); +# ifdef PNG_READ_ALPHA_MODE_SUPPORTED + if (!tr->optimize_alpha) + tr->tr.fn = png_do_gamma16_up; + else + tr->tr.fn = png_do_gamma16_up_optimize; +# else /* !READ_ALPHA_MODE */ + tr->tr.fn = png_do_gamma16_up; +# endif /* !READ_ALPHA_MODE */ + } + + /* Since the 'do' routines always perform gamma correction they will always + * expand the significant bits to the full output bit depth. + */ + tc->invalid_info |= PNG_INFO_sBIT; + tc->bit_depth = tr->to_bit_depth; + tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = + png_check_byte(png_ptr, tc->bit_depth); + if (tr->encode_alpha) + tc->sBIT_A = tc->sBIT_G; + tc->gamma = tr->to_gamma; +# undef png_ptr +} + +#ifdef PNG_READ_sBIT_SUPPORTED +static unsigned int +tc_sBIT(png_const_transform_controlp tc) + /* Determine the maximum number of significant bits in the row at this point. + * This uses the png_struct::sig_bit field if it has not been invalidated, + * otherwise it just returns the current bit depth. + */ +{ + const png_structrp png_ptr = tc->png_ptr; + unsigned int bit_depth = tc->bit_depth; + + if ((tc->invalid_info & PNG_INFO_sBIT) == 0U) + { + /* Normally the bit depth will not have been changed from the original PNG + * depth, but it currently is changed by the grayscale expand to 8 bits, + * an operation which doesn't invalidate sBIT. + */ + unsigned int sBIT; + + if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0U) + { + /* Must use the largest of the sBIT depths, except that unset values + * take priority. + */ + sBIT = png_ptr->sig_bit.red && png_ptr->sig_bit.green && + png_ptr->sig_bit.blue; + + if (sBIT != 0U) + { + sBIT = png_ptr->sig_bit.red; + + if (png_ptr->sig_bit.green > sBIT) + sBIT = png_ptr->sig_bit.green; + if (png_ptr->sig_bit.blue > sBIT) + sBIT = png_ptr->sig_bit.blue; + } + } + + else + sBIT = png_ptr->sig_bit.gray; + + if (sBIT > 0U && sBIT < bit_depth) + bit_depth = sBIT; + } + + return bit_depth; +} +#else /* !READ_sBIT */ +# define tc_sBIT(tc) ((tc)->bit_depth) +#endif /* READ_sBIT */ + +static void +png_init_gamma(png_transformp *transform, png_transform_controlp tc) +{ + const png_structrp png_ptr = tc->png_ptr; + png_transform_gamma *tr = + png_transform_cast(png_transform_gamma, *transform); + + if (tc->init == PNG_TC_INIT_FORMAT) + { + /* This should only happen for the final encode gamma transform, which + * never initializes the target bit depth (see png_set_gamma and + * png_set_alpha_mode). The affirm is required here; in we can't continue + * safely if the bit depth has been set somehow. + */ + debug(tr->tr.order == PNG_TR_GAMMA_ENCODE); + affirm(tr->to_gamma > 0 && tr->to_bit_depth == 0U); + + /* At this point the output gamma should not have been set yet: */ + debug(png_ptr->row_gamma == 0); + + /* The following must be true; png_set_gamma and png_set_alpha_mode set + * (or default) the PNG gamma and other routines that insert a gamma + * transform must only do in PNG_TC_INIT_FINAL: + */ + debug(tc->gamma > 0); + + /* At this point the data gamma must be updated so that we get the correct + * png_struct::row_gamma at the end of the init: + */ + tc->gamma = tr->to_gamma; + + /* For safety invalidate the sBIT information too; we don't know yet + * whether a gamma transform will be required but if it is the sBIT + * information becomes invalid. + */ + tc->invalid_info |= PNG_INFO_sBIT; + } + + else /* PNG_TC_INIT_FINAL */ + { + /* It is very bad if we get here when processing a row: */ + affirm(tc->init == PNG_TC_INIT_FINAL && png_ptr->row_bit_depth > 0); + + /* There are three cases: + * + * 1) Gamma correction is required, output bit depth may need to be + * defaulted. + * 2) Gamma correction is not required but a bit depth change is + * necessary. + * 3) Neither is required; the transform can be eliminated. + * + * First default the bit depth if it is not already set. Note that if the + * output is a palette then 'row_bit_depth' refers to the palette size and + * 8U must be used here. tc->palette is irrelevant; it only tells us that + * the data came from a palette. + */ + if (tr->to_bit_depth == 0) + { + if ((png_ptr->row_format & PNG_FORMAT_FLAG_COLORMAP) != 0U) + tr->to_bit_depth = 8U; + + else + tr->to_bit_depth = png_ptr->row_bit_depth; + } + + /* (1); is gamma correction required? If tc->gamma is 0 at this point it + * is not, but then the png_struct::row_gamma should be 0 too. + */ + implies(tc->gamma == 0, png_ptr->row_gamma == 0); + implies(tr->to_gamma == 0, tc->gamma == 0); + + if (!png_gamma_equal(png_ptr, tc->gamma, tr->to_gamma, &tr->correct, + tc_sBIT(tc))) + { + /* First make sure the input doesn't have a tRNS chunk which needs to + * be expanded now; if it does push_gamma_expand will push an + * appropriate transform *before* this one and we need to return + * immediately (the caller will call back to this function). + */ + if (push_gamma_expand(transform, tc, 0/*need alpha*/)) + { + affirm(tc->bit_depth >= 8U && + (tc->invalid_info & PNG_INFO_tRNS) != 0U && + *transform != &tr->tr); + return; + } + + debug(*transform == &tr->tr && tc->bit_depth >= 8U); + + /* The format is now 8 or 16-bit G, GA, RGB or RGBA and gamma + * correction is required. + */ + png_init_gamma_uncached(transform, tc); + /* TODO: implement caching for the !tc->caching cases! */ + return; + } + + /* The cases where the two gamma values are close enough to be considered + * equal. The code lies about the gamma; this prevents apps and the + * simplified API getting into loops or bad conditions because the gamma + * was not set to the expected value. + * + * Note that png_transform_control::gamma is only set here if both the + * input and output gamma values are known, otherwise the transform + * introduces a spurious know gamma value. + */ + if (tr->to_gamma > 0 && tc->gamma > 0) + tc->gamma = tr->to_gamma; + + if (tr->to_bit_depth > tc->bit_depth) + { + /* This is either the to-linear operation, in which case the expected + * bit depth is 16U, or it is the final encode in the case where an + * 'expand' operation was also specified. + * + * We don't care about the PNG_TR_GAMMA_ENCODE case because we know + * that there has to be an expand operation further down the pipeline. + */ + if (tr->tr.order < PNG_TR_GAMMA_ENCODE) + { + affirm(tr->to_bit_depth == 16U); + + if (push_gamma_expand(transform, tc, 0/*need alpha*/)) + { + affirm(tc->bit_depth == 8U && + (tc->invalid_info & PNG_INFO_tRNS) != 0U && + *transform != &tr->tr); + return; + } + + debug(*transform == &tr->tr); + affirm(tc->bit_depth == 8U); /* if 16U we would not be here! */ + + /* not using byte_ops here, but if there is no sBIT required + * (normally the case) the fast code can be used: + */ + if (init_gamma_sBIT(tr, tc)) + tr->tr.fn = png_do_expand8_down; + + else + tr->tr.fn = png_do_expand8_down_fast; + + tc->bit_depth = 16U; + } + + else /* PNG_TR_GAMMA_ENCODE: nothing need be done */ + tr->tr.fn = NULL; + } + + else if (tr->to_bit_depth < tc->bit_depth) + { + /* No gamma correction but bit depth *reduction* is required. Expect + * the 'from' bit depth to always be 16, otherwise this transform + * should not have been pushed. Also expect this to be the gamma + * 'encode' operation at the end of the arithmetic. + */ + affirm(tc->bit_depth == 16U && tr->tr.order == PNG_TR_GAMMA_ENCODE); + + /* If the target bit depth is 8-bit delay the operation and use the + * standard 16-8-bit scale code. For low bit depth do it now. + */ + if (tr->to_bit_depth == 8U) + { + png_set_scale_16(png_ptr); + tr->tr.fn = NULL; + } + + else /* low bit depth */ + { + (void)init_gamma_sBIT(tr, tc); + tr->tr.fn = png_do_scale16_up; + tc->bit_depth = tr->to_bit_depth; + } + } + + else /* gamma !significant and nothing to do */ + tr->tr.fn = NULL; + } +} + +#if !PNG_RELEASE_BUILD +int /* PRIVATE(debug only) */ +png_gamma_check(png_const_structrp png_ptr, png_const_transform_controlp tc) + /* Debugging only routine to repeat the test used above to determine if the + * gamma was insignificant. + * + * NOTE: JB20160723: This may still be incorrect in a complicated transform + * pipeline because it uses 'tc_sBIT' for the end of the pipeline whereas the + * init above happens earlier. I don't think this matters because the test + * is only invoked if the gamma transform is eliminated or if there is a bug + * and in the former case the sBIT values should remain unchanged. + */ +{ + png_fixed_point dummy; + + return png_gamma_equal(png_ptr, png_ptr->row_gamma, tc->gamma, &dummy, + tc_sBIT(tc)); +} +#endif /* !RELEASE_BUILD */ + static png_fixed_point -translate_gamma_flags(png_structrp png_ptr, png_fixed_point output_gamma, - int is_screen) +translate_gamma_flags(png_const_structrp png_ptr, png_fixed_point gamma, + int is_screen) + /* If 'is_screen' is set this returns the inverse of the supplied value; i.e. + * this routine always returns an encoding value. + */ { /* Check for flag values. The main reason for having the old Mac value as a * flag is that it is pretty near impossible to work out what the correct * value is from Apple documentation - a working Mac system is needed to * discover the value! */ - if (output_gamma == PNG_DEFAULT_sRGB || - output_gamma == PNG_FP_1 / PNG_DEFAULT_sRGB) + switch (gamma) { - /* If there is no sRGB support this just sets the gamma to the standard - * sRGB value. (This is a side effect of using this function!) - */ -# ifdef PNG_READ_sRGB_SUPPORTED - png_ptr->flags |= PNG_FLAG_ASSUME_sRGB; -# else - PNG_UNUSED(png_ptr) -# endif - if (is_screen != 0) - output_gamma = PNG_GAMMA_sRGB; - else - output_gamma = PNG_GAMMA_sRGB_INVERSE; + case PNG_DEFAULT_sRGB: + case PNG_GAMMA_sRGB: + case PNG_FP_1/PNG_GAMMA_sRGB: /* stupid case: -100000 */ + gamma = PNG_GAMMA_sRGB_INVERSE; + break; + + case PNG_GAMMA_MAC_18: + case PNG_FP_1/PNG_GAMMA_MAC_18: /* stupid case: -50000 */ + gamma = PNG_GAMMA_MAC_INVERSE; + break; + + default: + if (is_screen) + { + /* Check for a ridiculously low value; this will result in an + * overflow + * in the reciprocal calculation. + */ + if (gamma < 5) + { + png_app_error(png_ptr, "invalid screen gamma (too low)"); + gamma = 0; + } + + else if (gamma != PNG_FP_1) /* optimize linear */ + gamma = png_reciprocal(gamma); + } + + else if (gamma <= 0) + { + png_app_error(png_ptr, "invalid file gamma (too low)"); + gamma = 0; + } + break; } - else if (output_gamma == PNG_GAMMA_MAC_18 || - output_gamma == PNG_FP_1 / PNG_GAMMA_MAC_18) - { - if (is_screen != 0) - output_gamma = PNG_GAMMA_MAC_OLD; - else - output_gamma = PNG_GAMMA_MAC_INVERSE; - } - - return output_gamma; + return gamma; } -# ifdef PNG_FLOATING_POINT_SUPPORTED +static png_transform_gamma * +add_gamma_transform(png_structrp png_ptr, unsigned int order, + png_fixed_point gamma, unsigned int bit_depth, int force) +{ + /* Add a png_transform_gamma transform at the given position; this is a + * utility which just adds the transform and (unconditionally) overwrites the + * to_gamma field. gamma must be valid. If 'force' is true the gamma value + * in an existing transform will be overwritten, otherwise this is just a + * default value. + */ + png_transform_gamma *tr = png_transform_cast(png_transform_gamma, + png_add_transform(png_ptr, sizeof (png_transform_gamma), png_init_gamma, + order)); + + if (force || tr->to_gamma == 0) + tr->to_gamma = gamma; + + tr->to_bit_depth = bit_depth; + + return tr; +} + +void PNGFAPI +png_set_gamma_fixed(png_structrp png_ptr, png_fixed_point scrn_gamma, + png_fixed_point file_gamma) +{ + png_debug(1, "in png_set_gamma_fixed"); + + /* Validate the passed in file gamma value: */ + file_gamma = translate_gamma_flags(png_ptr, file_gamma, 0/*file*/); + + /* The returned value may be 0, this results in a png_app_error above which + * may be ignored; if that happens simply ignore the setting. + */ + if (file_gamma > 0) + { + /* Set the colorspace gamma value unconditionally - this overrides the + * value in the PNG file if a gAMA chunk was present. png_set_alpha_mode + * provides a different, easier, way to default the file gamma. + */ + png_ptr->colorspace.gamma = file_gamma; + if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) + png_ptr->colorspace.flags = PNG_COLORSPACE_HAVE_GAMMA; + else + png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; + } + + /* Do the same thing with the screen gamma; check it and handle it if valid. + * This adds/sets the encoding of the final gamma transform in the chain. + * png_set_alpha_mode does the same thing. + */ + scrn_gamma = translate_gamma_flags(png_ptr, scrn_gamma, 1/*screen*/); + + if (scrn_gamma > 0) + (void)add_gamma_transform(png_ptr, PNG_TR_GAMMA_ENCODE, scrn_gamma, + 0/*bit depth*/, 1/*force to_gamma to scrn_gamma*/); +} + +#ifdef PNG_FLOATING_POINT_SUPPORTED static png_fixed_point convert_gamma_value(png_structrp png_ptr, double output_gamma) { @@ -264,771 +3584,478 @@ convert_gamma_value(png_structrp png_ptr, double output_gamma) return png_fixed(png_ptr, output_gamma, "gamma value"); } -# endif -#endif /* READ_ALPHA_MODE || READ_GAMMA */ -#ifdef PNG_READ_ALPHA_MODE_SUPPORTED -void PNGFAPI -png_set_alpha_mode_fixed(png_structrp png_ptr, int mode, - png_fixed_point output_gamma) -{ - int compose = 0; - png_fixed_point file_gamma; - - png_debug(1, "in png_set_alpha_mode"); - - if (!png_rtran_ok(png_ptr, 0)) - return; - - output_gamma = translate_gamma_flags(png_ptr, output_gamma, 1/*screen*/); - - /* Validate the value to ensure it is in a reasonable range. The value - * is expected to be 1 or greater, but this range test allows for some - * viewing correction values. The intent is to weed out users of this API - * who use the inverse of the gamma value accidentally! Since some of these - * values are reasonable this may have to be changed. - */ - if (output_gamma < 70000 || output_gamma > 300000) - png_error(png_ptr, "output gamma out of expected range"); - - /* The default file gamma is the inverse of the output gamma; the output - * gamma may be changed below so get the file value first: - */ - file_gamma = png_reciprocal(output_gamma); - - /* There are really 8 possibilities here, composed of any combination - * of: - * - * premultiply the color channels - * do not encode non-opaque pixels - * encode the alpha as well as the color channels - * - * The differences disappear if the input/output ('screen') gamma is 1.0, - * because then the encoding is a no-op and there is only the choice of - * premultiplying the color channels or not. - * - * png_set_alpha_mode and png_set_background interact because both use - * png_compose to do the work. Calling both is only useful when - * png_set_alpha_mode is used to set the default mode - PNG_ALPHA_PNG - along - * with a default gamma value. Otherwise PNG_COMPOSE must not be set. - */ - switch (mode) - { - case PNG_ALPHA_PNG: /* default: png standard */ - /* No compose, but it may be set by png_set_background! */ - png_ptr->transformations &= ~PNG_ENCODE_ALPHA; - png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; - break; - - case PNG_ALPHA_ASSOCIATED: /* color channels premultiplied */ - compose = 1; - png_ptr->transformations &= ~PNG_ENCODE_ALPHA; - png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; - /* The output is linear: */ - output_gamma = PNG_FP_1; - break; - - case PNG_ALPHA_OPTIMIZED: /* associated, non-opaque pixels linear */ - compose = 1; - png_ptr->transformations &= ~PNG_ENCODE_ALPHA; - png_ptr->flags |= PNG_FLAG_OPTIMIZE_ALPHA; - /* output_gamma records the encoding of opaque pixels! */ - break; - - case PNG_ALPHA_BROKEN: /* associated, non-linear, alpha encoded */ - compose = 1; - png_ptr->transformations |= PNG_ENCODE_ALPHA; - png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; - break; - - default: - png_error(png_ptr, "invalid alpha mode"); - } - - /* Only set the default gamma if the file gamma has not been set (this has - * the side effect that the gamma in a second call to png_set_alpha_mode will - * be ignored.) - */ - if (png_ptr->colorspace.gamma == 0) - { - png_ptr->colorspace.gamma = file_gamma; - png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; - } - - /* But always set the output gamma: */ - png_ptr->screen_gamma = output_gamma; - - /* Finally, if pre-multiplying, set the background fields to achieve the - * desired result. - */ - if (compose != 0) - { - /* And obtain alpha pre-multiplication by composing on black: */ - memset(&png_ptr->background, 0, (sizeof png_ptr->background)); - png_ptr->background_gamma = png_ptr->colorspace.gamma; /* just in case */ - png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_FILE; - png_ptr->flags &= ~PNG_FLAG_BACKGROUND_EXPAND; - - if (png_ptr->transformations & PNG_COMPOSE) - png_error(png_ptr, - "conflicting calls to set alpha mode and background"); - - png_ptr->transformations |= PNG_COMPOSE; - } -} - -# ifdef PNG_FLOATING_POINT_SUPPORTED -void PNGAPI -png_set_alpha_mode(png_structrp png_ptr, int mode, double output_gamma) -{ - png_set_alpha_mode_fixed(png_ptr, mode, convert_gamma_value(png_ptr, - output_gamma)); -} -# endif -#endif - -#ifdef PNG_READ_QUANTIZE_SUPPORTED -/* Dither file to 8-bit. Supply a palette, the current number - * of elements in the palette, the maximum number of elements - * allowed, and a histogram if possible. If the current number - * of colors is greater then the maximum number, the palette will be - * modified to fit in the maximum number. "full_quantize" indicates - * whether we need a quantizing cube set up for RGB images, or if we - * simply are reducing the number of colors in a paletted image. - */ - -typedef struct png_dsort_struct -{ - struct png_dsort_struct * next; - png_byte left; - png_byte right; -} png_dsort; -typedef png_dsort * png_dsortp; -typedef png_dsort * * png_dsortpp; - -void PNGAPI -png_set_quantize(png_structrp png_ptr, png_colorp palette, - int num_palette, int maximum_colors, png_const_uint_16p histogram, - int full_quantize) -{ - png_debug(1, "in png_set_quantize"); - - if (!png_rtran_ok(png_ptr, 0)) - return; - - png_ptr->transformations |= PNG_QUANTIZE; - - if (full_quantize == 0) - { - int i; - - png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr, - (png_uint_32)(num_palette * (sizeof (png_byte)))); - for (i = 0; i < num_palette; i++) - png_ptr->quantize_index[i] = (png_byte)i; - } - - if (num_palette > maximum_colors) - { - if (histogram != NULL) - { - /* This is easy enough, just throw out the least used colors. - * Perhaps not the best solution, but good enough. - */ - - int i; - - /* Initialize an array to sort colors */ - png_ptr->quantize_sort = (png_bytep)png_malloc(png_ptr, - (png_uint_32)(num_palette * (sizeof (png_byte)))); - - /* Initialize the quantize_sort array */ - for (i = 0; i < num_palette; i++) - png_ptr->quantize_sort[i] = (png_byte)i; - - /* Find the least used palette entries by starting a - * bubble sort, and running it until we have sorted - * out enough colors. Note that we don't care about - * sorting all the colors, just finding which are - * least used. - */ - - for (i = num_palette - 1; i >= maximum_colors; i--) - { - int done; /* To stop early if the list is pre-sorted */ - int j; - - done = 1; - for (j = 0; j < i; j++) - { - if (histogram[png_ptr->quantize_sort[j]] - < histogram[png_ptr->quantize_sort[j + 1]]) - { - png_byte t; - - t = png_ptr->quantize_sort[j]; - png_ptr->quantize_sort[j] = png_ptr->quantize_sort[j + 1]; - png_ptr->quantize_sort[j + 1] = t; - done = 0; - } - } - - if (done != 0) - break; - } - - /* Swap the palette around, and set up a table, if necessary */ - if (full_quantize != 0) - { - int j = num_palette; - - /* Put all the useful colors within the max, but don't - * move the others. - */ - for (i = 0; i < maximum_colors; i++) - { - if ((int)png_ptr->quantize_sort[i] >= maximum_colors) - { - do - j--; - while ((int)png_ptr->quantize_sort[j] >= maximum_colors); - - palette[i] = palette[j]; - } - } - } - else - { - int j = num_palette; - - /* Move all the used colors inside the max limit, and - * develop a translation table. - */ - for (i = 0; i < maximum_colors; i++) - { - /* Only move the colors we need to */ - if ((int)png_ptr->quantize_sort[i] >= maximum_colors) - { - png_color tmp_color; - - do - j--; - while ((int)png_ptr->quantize_sort[j] >= maximum_colors); - - tmp_color = palette[j]; - palette[j] = palette[i]; - palette[i] = tmp_color; - /* Indicate where the color went */ - png_ptr->quantize_index[j] = (png_byte)i; - png_ptr->quantize_index[i] = (png_byte)j; - } - } - - /* Find closest color for those colors we are not using */ - for (i = 0; i < num_palette; i++) - { - if ((int)png_ptr->quantize_index[i] >= maximum_colors) - { - int min_d, k, min_k, d_index; - - /* Find the closest color to one we threw out */ - d_index = png_ptr->quantize_index[i]; - min_d = PNG_COLOR_DIST(palette[d_index], palette[0]); - for (k = 1, min_k = 0; k < maximum_colors; k++) - { - int d; - - d = PNG_COLOR_DIST(palette[d_index], palette[k]); - - if (d < min_d) - { - min_d = d; - min_k = k; - } - } - /* Point to closest color */ - png_ptr->quantize_index[i] = (png_byte)min_k; - } - } - } - png_free(png_ptr, png_ptr->quantize_sort); - png_ptr->quantize_sort = NULL; - } - else - { - /* This is much harder to do simply (and quickly). Perhaps - * we need to go through a median cut routine, but those - * don't always behave themselves with only a few colors - * as input. So we will just find the closest two colors, - * and throw out one of them (chosen somewhat randomly). - * [We don't understand this at all, so if someone wants to - * work on improving it, be our guest - AED, GRP] - */ - int i; - int max_d; - int num_new_palette; - png_dsortp t; - png_dsortpp hash; - - t = NULL; - - /* Initialize palette index arrays */ - png_ptr->index_to_palette = (png_bytep)png_malloc(png_ptr, - (png_uint_32)(num_palette * (sizeof (png_byte)))); - png_ptr->palette_to_index = (png_bytep)png_malloc(png_ptr, - (png_uint_32)(num_palette * (sizeof (png_byte)))); - - /* Initialize the sort array */ - for (i = 0; i < num_palette; i++) - { - png_ptr->index_to_palette[i] = (png_byte)i; - png_ptr->palette_to_index[i] = (png_byte)i; - } - - hash = (png_dsortpp)png_calloc(png_ptr, (png_uint_32)(769 * - (sizeof (png_dsortp)))); - - num_new_palette = num_palette; - - /* Initial wild guess at how far apart the farthest pixel - * pair we will be eliminating will be. Larger - * numbers mean more areas will be allocated, Smaller - * numbers run the risk of not saving enough data, and - * having to do this all over again. - * - * I have not done extensive checking on this number. - */ - max_d = 96; - - while (num_new_palette > maximum_colors) - { - for (i = 0; i < num_new_palette - 1; i++) - { - int j; - - for (j = i + 1; j < num_new_palette; j++) - { - int d; - - d = PNG_COLOR_DIST(palette[i], palette[j]); - - if (d <= max_d) - { - - t = (png_dsortp)png_malloc_warn(png_ptr, - (png_uint_32)(sizeof (png_dsort))); - - if (t == NULL) - break; - - t->next = hash[d]; - t->left = (png_byte)i; - t->right = (png_byte)j; - hash[d] = t; - } - } - if (t == NULL) - break; - } - - if (t != NULL) - for (i = 0; i <= max_d; i++) - { - if (hash[i] != NULL) - { - png_dsortp p; - - for (p = hash[i]; p; p = p->next) - { - if ((int)png_ptr->index_to_palette[p->left] - < num_new_palette && - (int)png_ptr->index_to_palette[p->right] - < num_new_palette) - { - int j, next_j; - - if (num_new_palette & 0x01) - { - j = p->left; - next_j = p->right; - } - else - { - j = p->right; - next_j = p->left; - } - - num_new_palette--; - palette[png_ptr->index_to_palette[j]] - = palette[num_new_palette]; - if (full_quantize == 0) - { - int k; - - for (k = 0; k < num_palette; k++) - { - if (png_ptr->quantize_index[k] == - png_ptr->index_to_palette[j]) - png_ptr->quantize_index[k] = - png_ptr->index_to_palette[next_j]; - - if ((int)png_ptr->quantize_index[k] == - num_new_palette) - png_ptr->quantize_index[k] = - png_ptr->index_to_palette[j]; - } - } - - png_ptr->index_to_palette[png_ptr->palette_to_index - [num_new_palette]] = png_ptr->index_to_palette[j]; - - png_ptr->palette_to_index[png_ptr->index_to_palette[j]] - = png_ptr->palette_to_index[num_new_palette]; - - png_ptr->index_to_palette[j] = - (png_byte)num_new_palette; - - png_ptr->palette_to_index[num_new_palette] = - (png_byte)j; - } - if (num_new_palette <= maximum_colors) - break; - } - if (num_new_palette <= maximum_colors) - break; - } - } - - for (i = 0; i < 769; i++) - { - if (hash[i] != NULL) - { - png_dsortp p = hash[i]; - while (p) - { - t = p->next; - png_free(png_ptr, p); - p = t; - } - } - hash[i] = 0; - } - max_d += 96; - } - png_free(png_ptr, hash); - png_free(png_ptr, png_ptr->palette_to_index); - png_free(png_ptr, png_ptr->index_to_palette); - png_ptr->palette_to_index = NULL; - png_ptr->index_to_palette = NULL; - } - num_palette = maximum_colors; - } - if (png_ptr->palette == NULL) - { - png_ptr->palette = palette; - } - png_ptr->num_palette = (png_uint_16)num_palette; - - if (full_quantize != 0) - { - int i; - png_bytep distance; - int total_bits = PNG_QUANTIZE_RED_BITS + PNG_QUANTIZE_GREEN_BITS + - PNG_QUANTIZE_BLUE_BITS; - int num_red = (1 << PNG_QUANTIZE_RED_BITS); - int num_green = (1 << PNG_QUANTIZE_GREEN_BITS); - int num_blue = (1 << PNG_QUANTIZE_BLUE_BITS); - png_size_t num_entries = ((png_size_t)1 << total_bits); - - png_ptr->palette_lookup = (png_bytep)png_calloc(png_ptr, - (png_uint_32)(num_entries * (sizeof (png_byte)))); - - distance = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_entries * - (sizeof (png_byte)))); - - memset(distance, 0xff, num_entries * (sizeof (png_byte))); - - for (i = 0; i < num_palette; i++) - { - int ir, ig, ib; - int r = (palette[i].red >> (8 - PNG_QUANTIZE_RED_BITS)); - int g = (palette[i].green >> (8 - PNG_QUANTIZE_GREEN_BITS)); - int b = (palette[i].blue >> (8 - PNG_QUANTIZE_BLUE_BITS)); - - for (ir = 0; ir < num_red; ir++) - { - /* int dr = abs(ir - r); */ - int dr = ((ir > r) ? ir - r : r - ir); - int index_r = (ir << (PNG_QUANTIZE_BLUE_BITS + - PNG_QUANTIZE_GREEN_BITS)); - - for (ig = 0; ig < num_green; ig++) - { - /* int dg = abs(ig - g); */ - int dg = ((ig > g) ? ig - g : g - ig); - int dt = dr + dg; - int dm = ((dr > dg) ? dr : dg); - int index_g = index_r | (ig << PNG_QUANTIZE_BLUE_BITS); - - for (ib = 0; ib < num_blue; ib++) - { - int d_index = index_g | ib; - /* int db = abs(ib - b); */ - int db = ((ib > b) ? ib - b : b - ib); - int dmax = ((dm > db) ? dm : db); - int d = dmax + dt + db; - - if (d < (int)distance[d_index]) - { - distance[d_index] = (png_byte)d; - png_ptr->palette_lookup[d_index] = (png_byte)i; - } - } - } - } - } - - png_free(png_ptr, distance); - } -} -#endif /* PNG_READ_QUANTIZE_SUPPORTED */ - -#ifdef PNG_READ_GAMMA_SUPPORTED -void PNGFAPI -png_set_gamma_fixed(png_structrp png_ptr, png_fixed_point scrn_gamma, - png_fixed_point file_gamma) -{ - png_debug(1, "in png_set_gamma_fixed"); - - if (!png_rtran_ok(png_ptr, 0)) - return; - - /* New in libpng-1.5.4 - reserve particular negative values as flags. */ - scrn_gamma = translate_gamma_flags(png_ptr, scrn_gamma, 1/*screen*/); - file_gamma = translate_gamma_flags(png_ptr, file_gamma, 0/*file*/); - - /* Checking the gamma values for being >0 was added in 1.5.4 along with the - * premultiplied alpha support; this actually hides an undocumented feature - * of the previous implementation which allowed gamma processing to be - * disabled in background handling. There is no evidence (so far) that this - * was being used; however, png_set_background itself accepted and must still - * accept '0' for the gamma value it takes, because it isn't always used. - * - * Since this is an API change (albeit a very minor one that removes an - * undocumented API feature) the following checks were only enabled in - * libpng-1.6.0. - */ - if (file_gamma <= 0) - png_error(png_ptr, "invalid file gamma in png_set_gamma"); - - if (scrn_gamma <= 0) - png_error(png_ptr, "invalid screen gamma in png_set_gamma"); - - /* Set the gamma values unconditionally - this overrides the value in the PNG - * file if a gAMA chunk was present. png_set_alpha_mode provides a - * different, easier, way to default the file gamma. - */ - png_ptr->colorspace.gamma = file_gamma; - png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; - png_ptr->screen_gamma = scrn_gamma; -} - -# ifdef PNG_FLOATING_POINT_SUPPORTED void PNGAPI png_set_gamma(png_structrp png_ptr, double scrn_gamma, double file_gamma) { png_set_gamma_fixed(png_ptr, convert_gamma_value(png_ptr, scrn_gamma), - convert_gamma_value(png_ptr, file_gamma)); + convert_gamma_value(png_ptr, file_gamma)); } -# endif /* FLOATING_POINT_SUPPORTED */ +#endif /* FLOATING_POINT */ #endif /* READ_GAMMA */ -#ifdef PNG_READ_EXPAND_SUPPORTED -/* Expand paletted images to RGB, expand grayscale images of - * less than 8-bit depth to 8-bit depth, and expand tRNS chunks - * to alpha channels. - */ -void PNGAPI -png_set_expand(png_structrp png_ptr) -{ - png_debug(1, "in png_set_expand"); - - if (!png_rtran_ok(png_ptr, 0)) - return; - - png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); -} - -/* GRR 19990627: the following three functions currently are identical - * to png_set_expand(). However, it is entirely reasonable that someone - * might wish to expand an indexed image to RGB but *not* expand a single, - * fully transparent palette entry to a full alpha channel--perhaps instead - * convert tRNS to the grayscale/RGB format (16-bit RGB value), or replace - * the transparent color with a particular RGB value, or drop tRNS entirely. - * IOW, a future version of the library may make the transformations flag - * a bit more fine-grained, with separate bits for each of these three - * functions. - * - * More to the point, these functions make it obvious what libpng will be - * doing, whereas "expand" can (and does) mean any number of things. - * - * GRP 20060307: In libpng-1.2.9, png_set_gray_1_2_4_to_8() was modified - * to expand only the sample depth but not to expand the tRNS to alpha - * and its name was changed to png_set_expand_gray_1_2_4_to_8(). - */ - -/* Expand paletted images to RGB. */ -void PNGAPI -png_set_palette_to_rgb(png_structrp png_ptr) -{ - png_debug(1, "in png_set_palette_to_rgb"); - - if (!png_rtran_ok(png_ptr, 0)) - return; - - png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); -} - -/* Expand grayscale images of less than 8-bit depth to 8 bits. */ -void PNGAPI -png_set_expand_gray_1_2_4_to_8(png_structrp png_ptr) -{ - png_debug(1, "in png_set_expand_gray_1_2_4_to_8"); - - if (!png_rtran_ok(png_ptr, 0)) - return; - - png_ptr->transformations |= PNG_EXPAND; -} - -/* Expand tRNS chunks to alpha channels. */ -void PNGAPI -png_set_tRNS_to_alpha(png_structrp png_ptr) -{ - png_debug(1, "in png_set_tRNS_to_alpha"); - - if (!png_rtran_ok(png_ptr, 0)) - return; - - png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); -} -#endif /* defined(PNG_READ_EXPAND_SUPPORTED) */ - -#ifdef PNG_READ_EXPAND_16_SUPPORTED -/* Expand to 16-bit channels, expand the tRNS chunk too (because otherwise - * it may not work correctly.) - */ -void PNGAPI -png_set_expand_16(png_structrp png_ptr) -{ - png_debug(1, "in png_set_expand_16"); - - if (!png_rtran_ok(png_ptr, 0)) - return; - - png_ptr->transformations |= (PNG_EXPAND_16 | PNG_EXPAND | PNG_EXPAND_tRNS); -} -#endif - -#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED -void PNGAPI -png_set_gray_to_rgb(png_structrp png_ptr) -{ - png_debug(1, "in png_set_gray_to_rgb"); - - if (!png_rtran_ok(png_ptr, 0)) - return; - - /* Because rgb must be 8 bits or more: */ - png_set_expand_gray_1_2_4_to_8(png_ptr); - png_ptr->transformations |= PNG_GRAY_TO_RGB; -} -#endif - #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED -void PNGFAPI -png_set_rgb_to_gray_fixed(png_structrp png_ptr, int error_action, - png_fixed_point red, png_fixed_point green) +static void +png_do_rtog_48(png_transformp *transform, png_transform_controlp tc) { - png_debug(1, "in png_set_rgb_to_gray"); +# define png_ptr (tc->png_ptr) + const png_uint_32 r = (*transform)->args >> 16; + const png_uint_32 g = (*transform)->args & 0xFFFFU; + const png_uint_32 b = 65536U - r - g; - /* Need the IHDR here because of the check on color_type below. */ - /* TODO: fix this */ - if (!png_rtran_ok(png_ptr, 1)) - return; + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 6U; + png_bytep dp = png_voidcast(png_bytep, tc->dp); - switch(error_action) + debug(tc->bit_depth == 16U && tc->format == PNG_FORMAT_FLAG_COLOR && + (tc->gamma == 0U || !png_gamma_significant(png_ptr, tc->gamma, 16U))); + + tc->sp = dp; + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR); + + while (sp <= ep) { - case PNG_ERROR_ACTION_NONE: - png_ptr->transformations |= PNG_RGB_TO_GRAY; - break; + png_uint_32 gray = (((sp[0] << 8) + sp[1]) * r + + ((sp[2] << 8) + sp[3]) * g + + ((sp[4] << 8) + sp[5]) * b + 32767U) >> 16; + debug(gray < 65536U); + *dp++ = PNG_BYTE(gray >> 8); + *dp++ = PNG_BYTE(gray); + sp += 6U; + } +# undef png_ptr +} + +static void +png_do_rtog_64(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + const png_uint_32 r = (*transform)->args >> 16; + const png_uint_32 g = (*transform)->args & 0xFFFFU; + const png_uint_32 b = 65536U - r - g; + + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 8U; + png_bytep dp = png_voidcast(png_bytep, tc->dp); + + debug(tc->bit_depth == 16U && + tc->format == PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA && + (tc->gamma == 0U || !png_gamma_significant(png_ptr, tc->gamma, 16U))); + + tc->sp = dp; + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR); + + while (sp <= ep) + { + png_uint_32 gray = (((sp[0] << 8) + sp[1]) * r + + ((sp[2] << 8) + sp[3]) * g + + ((sp[4] << 8) + sp[5]) * b + 32767U) >> 16; + + debug(gray < 65536U); + *dp++ = PNG_BYTE(gray >> 8); + *dp++ = PNG_BYTE(gray); + sp += 6U; + *dp++ = *sp++; /* alpha */ + *dp++ = *sp++; + } +# undef png_ptr +} + +static void +png_init_rgb_to_gray_arithmetic(png_transformp *transform, + png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + /* This only gets used in the final init stage: */ + debug(tc->init == PNG_TC_INIT_FINAL && tc->bit_depth == 16U && + (tc->format & PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA)) == + PNG_FORMAT_FLAG_COLOR); + + (*transform)->fn = (tc->format & PNG_FORMAT_FLAG_ALPHA) ? png_do_rtog_64 : + png_do_rtog_48; + + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR); + tc->invalid_info |= PNG_INFO_sBIT; + tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A = + png_check_byte(png_ptr, tc->bit_depth); +# undef png_ptr +} + +typedef struct +{ + png_transform tr; + png_fixed_point red_coefficient; + png_fixed_point green_coefficient; + unsigned int coefficients_set :1; + unsigned int error_action :2; +} png_transform_rgb_to_gray; + +static void +png_update_rgb_status(png_structrp png_ptr, png_transformp *transform) +{ + png_transform_rgb_to_gray *tr = png_transform_cast(png_transform_rgb_to_gray, + *transform); + + png_ptr->rgb_to_gray_status = 1U; + tr->tr.fn = NULL; /* one warning/error only */ + + switch (tr->error_action) + { case PNG_ERROR_ACTION_WARN: - png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN; + png_warning(png_ptr, "RGB to gray found nongray pixel"); break; case PNG_ERROR_ACTION_ERROR: - png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR; + png_error(png_ptr, "RGB to gray found nongray pixel"); break; default: - png_error(png_ptr, "invalid error action to rgb_to_gray"); break; } +} - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) -#ifdef PNG_READ_EXPAND_SUPPORTED - png_ptr->transformations |= PNG_EXPAND; -#else - { - /* Make this an error in 1.6 because otherwise the application may assume - * that it just worked and get a memory overwrite. - */ - png_error(png_ptr, - "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED"); +static void +png_do_rgb_check24(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + /* Sets 'rgb_to_gray' status if a pixel is found where the red green and blue + * channels are not equal. + */ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 3U; - /* png_ptr->transformations &= ~PNG_RGB_TO_GRAY; */ - } -#endif + debug(tc->bit_depth == 8U && tc->format == PNG_FORMAT_FLAG_COLOR); + + while (sp <= ep) { - if (red >= 0 && green >= 0 && red + green <= PNG_FP_1) + if ((sp[0] ^ sp[1]) | (sp[2] ^ sp[1])) { - png_uint_16 red_int, green_int; - - /* NOTE: this calculation does not round, but this behavior is retained - * for consistency, the inaccuracy is very small. The code here always - * overwrites the coefficients, regardless of whether they have been - * defaulted or set already. - */ - red_int = (png_uint_16)(((png_uint_32)red*32768)/100000); - green_int = (png_uint_16)(((png_uint_32)green*32768)/100000); - - png_ptr->rgb_to_gray_red_coeff = red_int; - png_ptr->rgb_to_gray_green_coeff = green_int; -# if defined(PNG_COLORS_SPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED) - png_ptr->colorspace.flags |= PNG_COLORSPACE_RGB_TO_GRAY_SET; -# endif + png_update_rgb_status(png_ptr, transform); + break; } - else - { - if (red >= 0 && green >= 0) - png_app_warning(png_ptr, - "ignoring out of range rgb_to_gray coefficients"); + sp += 3U; + } +# undef png_ptr +} - /* Use the defaults, from the cHRM chunk if set, else the historical - * values which are close to the sRGB/HDTV/ITU-Rec 709 values. See - * png_do_rgb_to_gray for more discussion of the values. In this case - * the coefficients are not marked as 'set' and are not overwritten if - * something has already provided a default. +static void +png_do_rgb_check32(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + /* Sets 'rgb_to_gray' status if a pixel is found where the red green and blue + * channels are not equal and alpha is not zero. + */ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 4U; + + debug(tc->bit_depth == 8U && + tc->format == PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA); + + while (sp <= ep) + { + if (((sp[0] ^ sp[1]) | (sp[2] ^ sp[1])) && sp[3] != 0) + { + png_update_rgb_status(png_ptr, transform); + break; + } + + sp += 4U; + } +# undef png_ptr +} + +static void +png_do_rgb_check48(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + /* Sets 'rgb_to_gray' status if a pixel is found where the red green and blue + * channels are not equal. + */ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 6U; + + debug(tc->bit_depth == 16U && tc->format == PNG_FORMAT_FLAG_COLOR); + + while (sp <= ep) + { + if ((sp[0] ^ sp[2]) | (sp[4] ^ sp[2]) | + (sp[1] ^ sp[3]) | (sp[5] ^ sp[3])) + { + png_update_rgb_status(png_ptr, transform); + break; + } + + sp += 6U; + } +# undef png_ptr +} + +static void +png_do_rgb_check64(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + /* Sets 'rgb_to_gray' status if a pixel is found where the red green and blue + * channels are not equal and alpha is not zero. + */ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 8U; + + debug(tc->bit_depth == 16U && + tc->format == PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA); + + while (sp <= ep) + { + if (((sp[0] ^ sp[2]) | (sp[4] ^ sp[2]) | + (sp[1] ^ sp[3]) | (sp[5] ^ sp[3])) && + (sp[6] | sp[7]) != 0) + { + png_update_rgb_status(png_ptr, transform); + break; + } + + sp += 8U; + } +# undef png_ptr +} + +static void +png_init_rgb_to_gray(png_transformp *transform, png_transform_controlp tc) +{ + png_structrp png_ptr = tc->png_ptr; + + /* Basic checks: if there is no color in the format this transform is not + * applicable. + */ + if ((tc->format & PNG_FORMAT_FLAG_COLOR) != 0) + { + png_transform_rgb_to_gray *tr = png_transform_cast( + png_transform_rgb_to_gray, *transform); + + /* no colormap allowed: */ + affirm(tc->init && !(tc->format & PNG_FORMAT_FLAG_COLORMAP)); + /* no extra flags yet: */ + debug(!(tc->format & + PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA))); + /* at present no non-palette caching: */ + implies(tc->caching, tc->palette); + + if (tc->init == PNG_TC_INIT_FORMAT) + { + /* The convertion should just remove the 'COLOR' flag and do nothing + * else, but if a tRNS chunk is present this would invalidate it. + * Handle this by expanding it now. */ - if (png_ptr->rgb_to_gray_red_coeff == 0 && - png_ptr->rgb_to_gray_green_coeff == 0) + if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0 && !tc->palette && + png_ptr->num_trans == 1 && !(tc->invalid_info & PNG_INFO_tRNS)) { - png_ptr->rgb_to_gray_red_coeff = 6968; - png_ptr->rgb_to_gray_green_coeff = 23434; - /* png_ptr->rgb_to_gray_blue_coeff = 2366; */ + /* Only if expand was requested and not cancelled: */ + if (tc->expand_tRNS && !tc->strip_alpha) + tc->format |= PNG_FORMAT_FLAG_ALPHA; + + tc->invalid_info |= PNG_INFO_tRNS; /* prevent expansion later */ } + + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR); + } + + else /* PNG_TC_INIT_FINAL */ + { + unsigned int index; /* channel to select (invalid) */ + png_byte sBIT_color; /* sBIT of that channel if valid */ + png_fixed_point r, g; /* Coefficients in range 0..65536 */ + + /* Push a tRNS transform if required. Because this is a push the + * transform the init needs to be run now. This needs to go in + * before the check on r==g==b because a color key might be used. + */ + if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0 && !tc->palette && + png_ptr->num_trans == 1 && !(tc->invalid_info & PNG_INFO_tRNS)) + { + if (tc->expand_tRNS && !tc->strip_alpha) + { + png_transformp tr_expand = png_push_transform(png_ptr, + sizeof (png_expand), png_init_expand, transform, NULL); + + debug(*transform == tr_expand); + tr_expand->args |= PNG_EXPAND_tRNS; + png_init_expand(transform, tc); + /* Check for the infinite loop possibility: */ + affirm((tc->invalid_info & PNG_INFO_tRNS) != 0); + return; + } + + else + tc->invalid_info |= PNG_INFO_tRNS; + } + + { + png_fixed_point red, green; + + if (tr->coefficients_set) + { + red = tr->red_coefficient; + green = tr->green_coefficient; + } + +# ifdef PNG_COLORSPACE_SUPPORTED + else if ((png_ptr->colorspace.flags & + (PNG_COLORSPACE_HAVE_ENDPOINTS+PNG_COLORSPACE_INVALID)) + == PNG_COLORSPACE_HAVE_ENDPOINTS) + { + red = png_ptr->colorspace.end_points_XYZ.red_Y; + green = png_ptr->colorspace.end_points_XYZ.green_Y; + } +# endif + + else /* no colorspace support, assume sRGB */ + { + /* From IEC 61966-2-1:1999, the reverse transformation from sRGB + * RGB values to XYZ D65 values (not CIEXYZ!). These are not + * exact inverses of the forward transformation; they only have + * four (decimal) digits of precision. + * + * API CHANGE: in 1.7.0 the sRGB values from the official IEC + * specification are used, previously libpng used values from + * Charles Poynton's ColorFAQ of 1998-01-04. The original page + * is gone, however up to date information can be found below: + * + * http://www.poynton.com/ColorFAQ.html + * + * At the time of reading (20150628) this web site quotes the + * same values as below and cites ITU Rec 709 as the source. + */ + red = 21260; + green = 71520; + } + + /* Prior to 1.7 this calculation was done with 15-bit precision, + * this is because the code was written pre-muldiv and tried to + * work round the problems caused by the signs in integer + * calculations. + */ + (void)png_muldiv(&r, red, 65536, PNG_FP_1); + (void)png_muldiv(&g, green, 65536, PNG_FP_1); + } + + /* If the convertion can be deduced to select a single channel do so. + * If the error action is set to error just copy the red channel, if + * the coefficients select just one channel use that. + */ + if (tr->error_action == PNG_ERROR_ACTION_ERROR || r >= 65536) + index = 0U, sBIT_color = tc->sBIT_R; /* select red */ + + else if (g >= 65536) + index = 1U, sBIT_color = tc->sBIT_G; /* select green */ + + else if (r + g == 0) + index = 2U, sBIT_color = tc->sBIT_B; /* select blue */ + + else + index = 3U, sBIT_color = 0U/*UNUSED*/; + + if (index == 3U) + { + /* Arithmetic will have to be done. For this we need linear 16-bit + * data which must then be converted back to the required bit depth, + * png_init_gamma handles this. It may push other expand operations + * (it shouldn't but it can), so give it some space. + * + * The gamma must be restored to the original value, 0U for the bit + * depth means use the output bit depth. + */ + (void)add_gamma_transform(png_ptr, PNG_TR_GAMMA_ENCODE, tc->gamma, + 0U/*bit depth*/, 0/*default*/); + + /* If png_init_gamma is called with tc->gamma 0 it does the right + * thing in PNG_TC_INIT_FINAL; it just does any required bit depth + * adjustment. + */ + (void)add_gamma_transform(png_ptr, tr->tr.order + 0x10U, PNG_FP_1, + 16U, 1/*force: doesn't matter*/); + + { + /* This init routine will update the sBIT information + * appropriately. + */ + png_transformp tr_rtog = png_add_transform(png_ptr, 0/*size*/, + png_init_rgb_to_gray_arithmetic, tr->tr.order + 0x20U); + + /* r and g are known to be in the range 0..65535, so pack them + * into the 'args' argument of a new transform. + */ + tr_rtog->args = (((png_uint_32)r) << 16) + g; + } + } + + else /* index < 3 */ + { + /* TODO: does this need to select the correct sBIT value too? */ + png_add_rgb_to_gray_byte_ops(png_ptr, tc, index, + tr->tr.order + 0x10U); + tc->sBIT_G = sBIT_color; + } + + /* Prior to 1.7 libpng would always check for r!=g!=b. In 1.7 an extra + * error_action setting is added to prevent this overhead. + */ + if (tr->error_action) + tr->tr.fn = tc->bit_depth == 8 ? + ((tc->format & PNG_FORMAT_FLAG_ALPHA) ? + png_do_rgb_check32 : png_do_rgb_check24) : + ((tc->format & PNG_FORMAT_FLAG_ALPHA) ? + png_do_rgb_check64 : png_do_rgb_check48); + + else + tr->tr.fn = NULL; /* PNG_ERROR_ACTION_NO_CHECK */ + } + } + + else /* not color: transform not applicable */ + (*transform)->fn = NULL; +} + +void PNGFAPI +png_set_rgb_to_gray_fixed(png_structrp png_ptr, int error_action, + png_fixed_point red, png_fixed_point green) + /* API CHANGE: in 1.7 calling this on a palette PNG no longer causes the + * palette to be expanded (unless explicitly requested), rather it converts + * the palette to grayscale. + */ +{ + /* The coefficients must be reasonable, the error handling is to warn (pre + * 1.7) or app error (1.7) and drop back to the cHRM definition of Y. The + * drop back is done in the init routine if relevant flag is unset. Passing + * negative values causes this default to be used without a warning. + */ + int pset = 0; + + if (red >= 0 && green >= 0) + { + if (red <= PNG_FP_1 && green <= PNG_FP_1 && red + green <= PNG_FP_1) + pset = 1; + + else /* overflow */ + png_app_error(png_ptr, "rgb_to_gray coefficients too large (ignored)"); + } + + { + png_transform_rgb_to_gray *tr = + png_transform_cast(png_transform_rgb_to_gray, + png_add_transform(png_ptr, sizeof (png_transform_rgb_to_gray), + png_init_rgb_to_gray, PNG_TR_RGB_TO_GRAY)); + + tr->error_action = 0x3U & error_action; + + if (red < 0 || green < 0) /* use cHRM default */ + tr->coefficients_set = 0U; + + else if (pset) /* else bad coefficients which get ignored */ + { + tr->coefficients_set = 1U; + tr->red_coefficient = red; + tr->green_coefficient = green; } } } @@ -1040,777 +4067,2677 @@ png_set_rgb_to_gray_fixed(png_structrp png_ptr, int error_action, void PNGAPI png_set_rgb_to_gray(png_structrp png_ptr, int error_action, double red, - double green) + double green) { png_set_rgb_to_gray_fixed(png_ptr, error_action, png_fixed(png_ptr, red, "rgb to gray red coefficient"), png_fixed(png_ptr, green, "rgb to gray green coefficient")); } #endif /* FLOATING POINT */ - #endif /* RGB_TO_GRAY */ -#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) -void PNGAPI -png_set_read_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr - read_user_transform_fn) +#ifdef PNG_READ_BACKGROUND_SUPPORTED +typedef struct { - png_debug(1, "in png_set_read_user_transform_fn"); - -#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED - png_ptr->transformations |= PNG_USER_TRANSFORM; - png_ptr->read_user_transform_fn = read_user_transform_fn; -#endif -} -#endif - -#ifdef PNG_READ_TRANSFORMS_SUPPORTED -#ifdef PNG_READ_GAMMA_SUPPORTED -/* In the case of gamma transformations only do transformations on images where - * the [file] gamma and screen_gamma are not close reciprocals, otherwise it - * slows things down slightly, and also needlessly introduces small errors. - */ -static int /* PRIVATE */ -png_gamma_threshold(png_fixed_point screen_gamma, png_fixed_point file_gamma) -{ - /* PNG_GAMMA_THRESHOLD is the threshold for performing gamma - * correction as a difference of the overall transform from 1.0 - * - * We want to compare the threshold with s*f - 1, if we get - * overflow here it is because of wacky gamma values so we - * turn on processing anyway. - */ - png_fixed_point gtest; - return !png_muldiv(>est, screen_gamma, file_gamma, PNG_FP_1) || - png_gamma_significant(gtest); -} -#endif - -/* Initialize everything needed for the read. This includes modifying - * the palette. - */ -#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) -static void -gamma_correct_background(unsigned int value, unsigned int depth, - png_uint_16p backgroundp, png_uint_16p background_1p, - png_fixed_point gamma_correct, png_fixed_point gamma_to_1) -{ - switch (depth) + png_transform tr; + struct { - case 8: - if (gamma_correct != PNG_FP_1) - *backgroundp = png_gamma_8bit_correct(value, gamma_correct); + png_color_16 background; + unsigned int need_expand :1; /* Background matches format of PNG */ + unsigned int rgb_to_gray :1; /* RGB-to-gray transform found */ + unsigned int compose_background :1; /* png_set_background */ + unsigned int associate_alpha :1; + unsigned int encode_alpha :1; + unsigned int optimize_alpha :1; + unsigned int background_is_gray :1; /* Background color is gray */ + unsigned int background_bit_depth :5; /* bit depth, 1..16 */ + unsigned int ntrans :3; /* 1..6 bytes */ + png_byte transparent_pixel[6]; + png_byte background_pixel[6]; + png_fixed_point background_gamma; + } st; /* to allow the whole state to be copied reliably */ +} png_transform_background; - else - *backgroundp = (png_uint_16)value; +static void +resolve_background_color(png_transform_background *tr, + png_transform_controlp tc) +{ + png_const_structp png_ptr = tc->png_ptr; - if (gamma_to_1 != PNG_FP_1) - *background_1p = png_gamma_16bit_correct(value*257, gamma_to_1); - - else - *background_1p = (png_uint_16)(value*257); + /* Deduce the bit depth and color information for the background, the + * special case is when need_expand is set and the PNG has palette format, + * then (and only then) the background value is a palette index. + */ + if (tr->st.need_expand && tc->palette) + { + unsigned int i = tr->st.background.index; + png_byte r, g, b; + if (i >= png_ptr->num_palette) + { + png_app_error(png_ptr, "background index out of range"); + tr->tr.fn = NULL; return; + } - case 16: - if (gamma_correct != PNG_FP_1) - *backgroundp = png_gamma_16bit_correct(value, gamma_correct); + tr->st.background_bit_depth = 8U; + r = png_ptr->palette[i].red; + g = png_ptr->palette[i].green; + b = png_ptr->palette[i].blue; - else - *backgroundp = (png_uint_16)value; + if (r == g && g == b) + { + tr->st.background_is_gray = 1U; + tr->st.background.gray = g; + UNTESTED + } - if (gamma_to_1 != PNG_FP_1) - *background_1p = png_gamma_16bit_correct(value, gamma_to_1); - - else - *background_1p = (png_uint_16)value; - - return; - - default: - /* Low bit depth gray levels; do no harm. */ - break; + else + { + tr->st.background_is_gray = 0U; + tr->st.background.red = r; + tr->st.background.green = g; + tr->st.background.blue = b; + UNTESTED + } } - *backgroundp = (png_uint_16)value; - *background_1p = 0; /* should not be used */ + else /* background is not a palette index */ + { + int use_rgb; + png_uint_16 mask; + + /* First work out the bit depth and whether or not to use the RGB + * fields of the background. + */ + if (tr->st.need_expand) + { + affirm(!(tc->format & PNG_FORMAT_FLAG_COLORMAP)); + tr->st.background_bit_depth = + png_check_bits(png_ptr, png_ptr->bit_depth, 5U); + use_rgb = (png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0; + } + + else /* screen format background */ + { + /* If the final output is in palette format assume the background + * is in a matching format. This covers two cases, an original + * COLORMAP PNG and png_set_quantize. + */ + if ((png_ptr->row_format & PNG_FORMAT_FLAG_COLORMAP) != 0) + tr->st.background_bit_depth = 8U; + + else + tr->st.background_bit_depth = + png_check_bits(png_ptr, png_ptr->row_bit_depth, 5U); + + use_rgb = (png_ptr->row_format & PNG_FORMAT_FLAG_COLOR) != 0; + } + + /* The PNG spec says to use the low bits of the values, so we mask out + * the high bits here (at present no warning is produced if they are + * set.) + */ + mask = png_check_u16(png_ptr, (1U << tr->st.background_bit_depth)-1U); + + if (use_rgb) + { + png_uint_16 r, g, b; + + r = tr->st.background.red & mask; + g = tr->st.background.green & mask; + b = tr->st.background.blue & mask; + + if (r == g && g == b) + { + tr->st.background_is_gray = 1U; + tr->st.background.gray = g; + } + + else + { + tr->st.background_is_gray = 0U; + tr->st.background.red = r; + tr->st.background.green = g; + tr->st.background.blue = b; + } + } + + else /* gray */ + { + tr->st.background_is_gray = 1U; + tr->st.background.gray = tr->st.background.gray & mask; + } + } } -static void /* PRIVATE */ -png_init_background_transformations(png_structrp png_ptr) - /* Set the png_ptr->background and png_ptr->background_1 members correctly - * for the bit depth and format. - */ +static void +gamma_correct_background_component(png_const_structrp png_ptr, png_uint_16p cp, + unsigned int bdc, png_fixed_point correction, unsigned int bdout) + /* Utility function for gamma_correct_background. */ { - /* png_ptr->background is only assigned by png_set_background and - * png_set_alpha_mode (which just zeros out the fields.) png_set_background - * can set the PNG_FLAG_BACKGROUND_EXPAND flag if the input value is in the - * file format, for example if it comes from a bKGD chunk. - * - * Under some circumstances deficiencies in the current libpng code mean that - * the bit depth of the values must differ from the final bit depth; the bit - * depth has to match that at which the processing of the image pixels - * happens and this is not always the final bit depth. This is fixed up - * here. - * - * First find the required depth. - */ - unsigned int bit_depth, required_bit_depth; - unsigned int color_type = png_ptr->color_type; - const png_uint_32 transform = png_ptr->transformations; - const int expand = (png_ptr->flags & PNG_FLAG_BACKGROUND_EXPAND) != 0; + unsigned int c = *cp; - if (color_type & PNG_COLOR_MASK_PALETTE) - required_bit_depth = bit_depth = 8; + /* 0.0 and 1.0 are unchanged (and common): */ + if (c > 0U && c < (1U<png_ptr) + png_fixed_point correction = tc->gamma; + const unsigned int bdback = tr->st.background_bit_depth; + const unsigned int bdrow = tc->bit_depth; + + /* This is harmless if it fails but it will damage the output pixels - they + * won't have the requested color depth accuracy where the background is + * used. + */ + debug(bdback <= bdrow); + debug(tr->st.background_is_gray || (bdrow >= 8U && bdback >= 8U)); + + /* The background is assumed to be full precision; there is no sBIT + * information for it. The convertion converts from the current depth and + * gamma of the background to that in the transform control. It uses the + * full 16-bit precision when considering the gamma values even though this + * is probably spurious. + */ + if (correction != 0 && (tr->st.background_gamma == 0 || + png_gamma_equal(png_ptr, tr->st.background_gamma, correction, + &correction, 16U))) + correction = 0; /* no correction! */ + + if (tr->st.background_is_gray) + gamma_correct_background_component(png_ptr, &tr->st.background.gray, + bdback, correction, bdrow); else { - required_bit_depth = bit_depth = png_ptr->bit_depth; - - /* But not PNG_EXPAND_16 at present because it happens after the compose - * operation where the background is used! - */ - if (bit_depth < 8 && (transform & PNG_EXPAND) != 0) - required_bit_depth = 8; + gamma_correct_background_component(png_ptr, &tr->st.background.red, + bdback, correction, bdrow); + gamma_correct_background_component(png_ptr, &tr->st.background.green, + bdback, correction, bdrow); + gamma_correct_background_component(png_ptr, &tr->st.background.blue, + bdback, correction, bdrow); } - /* bit_depth and color_type now refer to the original file data and - * required_bit_depth is correct for the processing libpng does, however it - * does not necessarily match the output the application gets, fix that and - * the color type here: + /* Regardless of whether there was a correction set the background gamma: */ + tr->st.background_gamma = tc->gamma; + tr->st.background_bit_depth = png_check_bits(png_ptr, bdrow, 5U); +# undef png_ptr +} + +static void +fill_background_pixel(png_transform_background *tr, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + /* Fill in 'background_pixel' if the appropriate sequence of bytes for the + * format given in the transform control. */ - if (expand == 0) - { - /* The background bit_depth and color_type need correcting */ - if ((transform & PNG_EXPAND) != 0) - color_type &= ~PNG_COLOR_MASK_PALETTE; + unsigned int bdtc = tc->bit_depth; - /* The RGB<->gray transformations do the to gray operation first, then the - * from gray. - */ - if ((transform & PNG_RGB_TO_GRAY) != 0) - color_type &= ~PNG_COLOR_MASK_COLOR; - - if ((transform & PNG_GRAY_TO_RGB) != 0) - color_type |= PNG_COLOR_MASK_COLOR; - - bit_depth = required_bit_depth; - - /* The expansion to 16 bits and the scaling back from 16 bits per - * component to only 8 happens after the background processing (at - * present) so these transforms only affect the screen value, not the - * required value. Note that the 16_TO_8 conversions happen before the 8 - * to 16 one, so in theory both could occur - the order of the tests below - * must be correct! - * - * TODO: Note that the second of these changes cause an input 16-bit - * background value to be temporarily crushed to 8-bits per component, - * losing precision. This is a bug and should be fixed. - */ - if (bit_depth == 16 && - (transform & (PNG_16_TO_8|PNG_SCALE_16_TO_8)) != 0) - bit_depth = 8; - - if (bit_depth == 8 && (color_type & PNG_COLOR_MASK_PALETTE) == 0 && - (transform & PNG_EXPAND_16) != 0) - bit_depth = 16; - } - - /* Now make the background have the correct format. This involves reading the - * correct fields from png_ptr->background, adjusting the bit depth of the - * result and potentially gamma correcting the value then calculating the - * png_ptr->background_1 values too. + /* If necessary adjust the background pixel to the current row format (it is + * important to do this as late as possible to avoid spurious + * interconvertions). */ - { - unsigned int mult = 1; - png_fixed_point gamma_to_1, gamma_correct; + gamma_correct_background(tr, tc); - switch (png_ptr->background_gamma_type) + if (tr->st.background_is_gray) + { + unsigned int g = tr->st.background.gray; + + /* 'g' now has enough bits for the destination, note that in the case of + * low bit depth gray this causes the pixel to be replicated through the + * written byte. Fill all six bytes with the replicated background: + */ + while (bdtc < 8U) { - case PNG_BACKGROUND_GAMMA_SCREEN: - gamma_to_1 = png_ptr->screen_gamma; - gamma_correct = PNG_FP_1; + g &= (1U << bdtc) - 1U; /* use only the low bits */ + g |= g << bdtc; + bdtc <<= 1; + } + + memset(tr->st.background_pixel, PNG_BYTE(g), 6U); + if (bdtc == 16U) + tr->st.background_pixel[0] = tr->st.background_pixel[2] = + tr->st.background_pixel[4] = PNG_BYTE(g >> 8); + /* Must not include the alpha channel here: */ + tr->st.ntrans = png_check_bits(png_ptr, + ((tc->format & PNG_FORMAT_FLAG_COLOR)+1U) << (bdtc == 16U), 3U); + } + + else + { + unsigned int r = tr->st.background.red; + unsigned int g = tr->st.background.green; + unsigned int b = tr->st.background.blue; + + debug((tc->format & PNG_FORMAT_FLAG_COLOR) != 0); + + switch (bdtc) + { + case 8U: + tr->st.background_pixel[0] = PNG_BYTE(r); + tr->st.background_pixel[1] = PNG_BYTE(g); + tr->st.background_pixel[2] = PNG_BYTE(b); + tr->st.ntrans = 3U; break; - case PNG_BACKGROUND_GAMMA_FILE: - gamma_to_1 = png_reciprocal(png_ptr->colorspace.gamma); - gamma_correct = png_reciprocal2(png_ptr->colorspace.gamma, - png_ptr->screen_gamma); - break; - - case PNG_BACKGROUND_GAMMA_UNIQUE: - gamma_to_1 = png_reciprocal(png_ptr->background_gamma); - gamma_correct = png_reciprocal2(png_ptr->background_gamma, - png_ptr->screen_gamma); + case 16U: + tr->st.background_pixel[0] = PNG_BYTE(r>>8); + tr->st.background_pixel[1] = PNG_BYTE(r); + tr->st.background_pixel[2] = PNG_BYTE(g>>8); + tr->st.background_pixel[3] = PNG_BYTE(g); + tr->st.background_pixel[4] = PNG_BYTE(b>>8); + tr->st.background_pixel[5] = PNG_BYTE(b); + tr->st.ntrans = 6U; break; default: - gamma_to_1 = PNG_FP_1; - gamma_correct = PNG_FP_1; + NOT_REACHED; + } + } +# undef png_ptr +} + +/* Look for colors matching the trans_color in png_ptr and replace them. This + * must handle all the non-alpha formats. + */ +static void +png_do_replace_tRNS_multi(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + const unsigned int cbytes = tr->st.ntrans; + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - cbytes/*safety*/; + const int copy = dp != sp; + + /* We expect opaque and transparent pixels to be interleaved but with long + * sequences of each. + */ + debug(!(tc->format & PNG_FORMAT_FLAG_ALPHA) && + PNG_TC_PIXEL_DEPTH(*tc) == cbytes << 3); + tc->invalid_info |= PNG_INFO_tRNS; + tc->sp = dp; + + /* Look for pixels that match the transparent value, copying opaque ones as + * required. + */ + do + { + const png_const_bytep opaque_start = sp; + size_t cb; + + /* Find a transparent pixel, or the end: */ + do + { + if (memcmp(sp, tr->st.transparent_pixel, cbytes) == 0) /*transparent*/ + break; + sp += cbytes; + } + while (sp <= ep); + + cb = sp - opaque_start; + + /* Copy any opaque pixels: */ + if (cb > 0) + { + if (copy) + memcpy(dp, opaque_start, cb); + dp += cb; + } + + /* Set transparent pixels to the background (this has to be done one-by + * one; the case where all the bytes in the background are equal is not + * optimized.) + */ + if (sp <= ep) do + { + memcpy(dp, tr->st.background_pixel, cbytes); + sp += cbytes; + dp += cbytes; + } + while (sp <= ep && memcmp(sp, tr->st.transparent_pixel, cbytes) == 0); + } while (sp <= ep); + + debug(sp == ep+cbytes); +# undef png_ptr +} + +static void +png_do_replace_tRNS_8(png_transformp *transform, png_transform_controlp tc) + /* The single byte version: 8-bit gray */ +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_alloc_size_t row_bytes = tc->width; + const int copy = dp != sp; + const int transparent_pixel = tr->st.transparent_pixel[0]; + const int background_pixel = tr->st.background_pixel[0]; + + /* We expect opaque and transparent pixels to be interleaved but with long + * sequences of each. + */ + debug(!(tc->format & PNG_FORMAT_FLAG_ALPHA) && + PNG_TC_PIXEL_DEPTH(*tc) == 8 && tr->st.ntrans == 1); + tc->invalid_info |= PNG_INFO_tRNS; + tc->sp = dp; + + /* Now search for a byte that matches the transparent pixel. */ + do + { + const png_const_bytep tp = png_voidcast(png_const_bytep, + memchr(sp, transparent_pixel, row_bytes)); + png_alloc_size_t cb; + + if (tp == NULL) /* all remaining pixels are opaque */ + { + if (copy) + memcpy(dp, sp, row_bytes); + return; + } + + cb = tp - sp; + if (cb > 0) /* some opaque pixels found */ + { + if (copy) + memcpy(dp, sp, cb); + sp = tp; + dp += cb; + debug(row_bytes > cb); + row_bytes -= cb; + } + + /* Now count the transparent pixels, this could use strspn but for the + * moment does not. + */ + debug(row_bytes > 0); + ++sp; /* next to check, may be beyond the last */ + while (--row_bytes > 0 && *sp == transparent_pixel) ++sp; + + cb = sp - tp; + memset(dp, background_pixel, cb); + dp += cb; + } while (row_bytes > 0); + UNTESTED +# undef png_ptr +} + +static void +png_do_set_row(png_transformp *transform, png_transform_controlp tc) + /* This is a no-op transform that both invalidates INFO from args and sets + * the entire row to the byte given in the top bits. + */ +{ + png_bytep dp = png_voidcast(png_bytep, tc->dp); + + tc->sp = dp; + memset(dp, (*transform)->args >> 24, PNG_TC_ROWBYTES(*tc)); +} + +static void +png_do_replace_tRNS_lbd(png_transformp *transform, png_transform_controlp tc) +{ + /* This is the 2 or 4 bit depth grayscale case; the 1 bit case is handled by + * the two routines above and the 8-bit and 16-bit cases by the two before + * that. + * + * The transform contains pixel values that have been expanded to one byte, + * the code needs to match the tRNS pixel and substitute the background one + * in each byte. + */ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc); + const unsigned int copy = sp != dp; + const png_byte transparent_pixel = tr->st.transparent_pixel[0]; + const png_byte background_pixel = tr->st.background_pixel[0]; + + /* We expect opaque and transparent pixels to be interleaved but with long + * sequences of each. + */ + debug(!(tc->format & PNG_FORMAT_FLAG_ALPHA) && + PNG_TC_PIXEL_DEPTH(*tc) < 8 && tr->st.ntrans == 1); + tc->sp = dp; + + /* Now search for a byte that contains the transparent pixel + * + * NOTE: this is the "strlen" algorithm, I first saw a variant implemented in + * Acorn RISC iX (strlen) around 1991, almost certainly derived from a + * suggestion by Alan Mycroft dating from April 27, 1987 (Mycroft was one of + * the authors of the 'Norcroft' compiler used for RISC iX, and well known to + * the RISC iX implementors.) See, e.g.: + * + * http://bits.stephan-brumme.com/null.html. + * + * The exact form used here is the one reported by Brumme; I haven't been + * able to find the original Mycroft posting, it was probably on comp.arch. + * + * The 4-bit and 2-bit versions (probably slower in the 4-bit case than the + * do-it-by-pixel version, but definately faster once 32-bit handling is + * implemented): + * + * 4 bit: (byte - 0x11) & ~byte & 0x88 + * 2 bit: (byte - 0x55) & ~byte & 0xcc + * + * The generalizations to 32 bits (8 and 16 pixels per step) should be + * obvious. + * + * This algorithm reads pixels within a byte beyond the end of the row and, + * potentially, changes the non-existent pixels. This is harmless and not + * a security risk. + */ + if (tc->bit_depth == 4U) + { + /* For the moment the algorithm isn't used; there are only two pixels in + * each byte so it is likely to be quicker to check as below: + */ + do + { + const png_byte b = *sp++; + const unsigned int m = b ^ transparent_pixel; + + if (m == 0U) /* both transparent */ + *dp = background_pixel; + + else if ((m & 0xF0U) == 0U) /* first transparent */ + *dp = PNG_BYTE((background_pixel & 0xF0U) | (b & 0x0FU)); + + else if ((m & 0x0FU) == 0U) /* second transparent */ + *dp = PNG_BYTE((background_pixel & 0x0FU) | (b & 0xF0U)); + + else if (copy) /* neither transparent */ + *dp = b; + + ++dp; + } while (sp < ep); + } + + else + { + affirm(tc->bit_depth == 2U); + + do + { + const png_byte b = *sp++; + const unsigned int m = b ^ transparent_pixel; + + if (m == 0U) /* transparent */ + *dp = background_pixel; + + else if (0xAAU & ((m - 0x55U) & ~m)) + { + /* One or more pixels transparent */ + const unsigned int mask = + (m & 0xC0U ? 0xC0U : 0U) | + (m & 0x30U ? 0x30U : 0U) | + (m & 0x0CU ? 0x0CU : 0U) | + (m & 0x03U ? 0x03U : 0U); + + *dp = PNG_BYTE((b & mask) | (background_pixel & ~mask)); + } + + else if (copy) /* no transparent pixels */ + *dp = b; + + ++dp; + } while (sp < ep); + } + +# undef png_ptr +} + +static void +png_do_background_with_transparent_GA8(png_transformp *transform, + png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 1U/*safety*/; + const png_byte background_pixel = tr->st.background_pixel[0]; + + /* Because this is an alpha format and we are removing the alpha channel we + * can copy up. + */ + debug(tc->bit_depth == 8U && tc->format == PNG_FORMAT_GA && + tr->st.ntrans == 1U); + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA); + tc->sp = dp; + + /* Look for pixels that have alpha 0; all others should have alpha 1.0, + * however they are simply treated as opaque regardless. + */ + do + { + *dp++ = (sp[1] == 0U) ? background_pixel : sp[0]; + sp += 2U; + } while (sp < ep); + + debug(sp == ep+1U); +# undef png_ptr +} + +static void +png_do_background_with_transparent_GA16(png_transformp *transform, + png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 3U/*safety*/; + + debug(tc->bit_depth == 16U && tc->format == PNG_FORMAT_GA && + tr->st.ntrans == 2U); + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA); + tc->sp = dp; + + do + { + if (sp[2] == 0U && sp[3] == 0U) /* transparent */ + dp[0] = tr->st.background_pixel[0], dp[1] = tr->st.background_pixel[1]; + + else + dp[0] = sp[0], dp[1] = sp[1]; + + dp += 2U; + sp += 4U; + } while (sp < ep); + + debug(sp == ep+3U); +# undef png_ptr +} + +static void +png_do_background_with_transparent_GAlbd(png_transformp *transform, + png_transform_controlp tc) + /* This is the low-bit-depth gray case, the input is 1, 2 or 4-bit per + * channel gray-alpha. + */ +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc); + const unsigned int bit_depth = tc->bit_depth; + const unsigned int mask = (1U << bit_depth) - 1U; + const unsigned int back = tr->st.background_pixel[0] & mask; + unsigned int opos, ob, inb; + + debug(bit_depth < 8U && tc->format == PNG_FORMAT_GA && tr->st.ntrans == 1U); + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA); + tc->sp = dp; + + ob = 0U; /* output byte */ + opos = 0U; /* bit index of previous output pixel (counts down) */ + inb = 0U; /* quiet a GCC 4.8.5 warning */ + + for (;;) + { + /* The output is half the size of the input, so we need a new input byte + * for every 4 bits of output: + */ + if (opos == 0U || opos == 4U) + { + if (sp >= ep) + break; + + inb = *sp++; + } + + /* Move to the next *output* pixel, this wraps when bits is 0U: */ + opos = (opos - bit_depth) & 0x7U; + + /* Extract the whole input pixel to the low bits of a temporary: */ + { + unsigned int pixel = inb >> ((opos*2U) & 0x7U); + + /* The alpha channel is second, check for a value of 0: */ + if ((pixel & mask)/* A component*/ == 0U) + pixel = back; + + else + { + debug((pixel & mask) == mask); + pixel = (pixel >> bit_depth) & mask; /* G component */ + } + + ob |= pixel << opos; + } + + if (opos == 0U) + *dp++ = PNG_BYTE(ob), ob = 0U; + } + + if (opos != 0U) + *dp++ = PNG_BYTE(ob); + + debug(sp == ep); +# undef png_ptr +} + +static void +png_do_background_with_transparent_RGBA8(png_transformp *transform, + png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 3U/*safety*/; + + debug(tc->bit_depth == 8U && tc->format == PNG_FORMAT_RGBA && + tr->st.ntrans == 3U); + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA); + tc->sp = dp; + + do + { + if (sp[3] == 0U) /* transparent */ + memcpy(dp, tr->st.background_pixel, 3U); + + else + memmove(dp, sp, 3U); + + dp += 3U; + sp += 4U; + } while (sp < ep); + + debug(sp == ep+3U); +# undef png_ptr +} + +static void +png_do_background_with_transparent_RGBA16(png_transformp *transform, + png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 7U/*safety*/; + + debug(tc->bit_depth == 16U && tc->format == PNG_FORMAT_RGBA && + tr->st.ntrans == 6U); + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA); + tc->sp = dp; + + do + { + if (sp[6] == 0U && sp[7] == 0U) /* transparent */ + memcpy(dp, tr->st.background_pixel, 6U); + + else + memmove(dp, sp, 6U); + + dp += 6U; + sp += 8U; + } while (sp < ep); + + debug(sp == ep+7U); +# undef png_ptr +} + +static void +png_init_background_transparent(png_transformp *transform, + png_transform_controlp tc) + /* Select the correct version of the above routines. */ +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + + debug(tc->init == PNG_TC_INIT_FINAL /* never called in 'FORMAT' */ && + (tc->format & PNG_FORMAT_FLAG_ALPHA) != 0); + + /* Now we know the format on which processing will happen so it is possible + * to generate the correct fill pixel value to use. + */ + fill_background_pixel(tr, tc); + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA); + tc->invalid_info |= PNG_INFO_sBIT; + tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A = + png_check_byte(png_ptr, tc->bit_depth); + + if (!(tc->format & PNG_FORMAT_FLAG_COLOR)) + { + if (tc->bit_depth == 8U) + tr->tr.fn = png_do_background_with_transparent_GA8; + + else if (tc->bit_depth == 16U) + tr->tr.fn = png_do_background_with_transparent_GA16; + + else /* low-bit-depth gray with alpha (not a PNG format!) */ + tr->tr.fn = png_do_background_with_transparent_GAlbd; + } + + else /* color */ + { + if (tc->bit_depth == 8U) + tr->tr.fn = png_do_background_with_transparent_RGBA8; + + else + { + debug(tc->bit_depth == 16U); + tr->tr.fn = png_do_background_with_transparent_RGBA16; + } + } +# undef png_ptr +} + +/* The calculated values below have the range 0..65535*65535, the output has the + * range 0..65535, so divide by 65535. Two approaches are given here, one + * modifies the value in place, the other uses a more complex expression. With + * gcc on an AMD64 system the in-place approach is very slightly faster. + * + * The two expressions are slightly different in what they calculate but both + * give the exact answer (verified by exhaustive testing.) + * + * The macro must be given a png_uint_32 variable (lvalue), normally an auto + * variable. + */ +#ifndef PNG_COMPOSE_DIV_65535 +# ifdef PNG_COMPOSE_DIV_EXPRESSION_SUPPORTED +# define PNG_COMPOSE_DIV_65535(v)\ + (v = ((v + (v>>16) + (v>>31) + 32768U) >> 16)) +# else +# define PNG_COMPOSE_DIV_65535(v)\ + (v += v >> 16, v += v >> 31, v += 32768U, v >>= 16) +# endif +#endif + +static void +png_do_background_alpha_GA(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 3U/*safety*/; + const unsigned int background = tr->st.background.gray; + const int copy = (sp != dp); + const int compose = tr->st.compose_background; + + affirm(tc->bit_depth == 16U && tc->format == PNG_FORMAT_GA && + tr->st.background_bit_depth == 16U); + + /* If gamma transforms are eliminated this might fail: */ + debug(tr->st.background_gamma == tc->gamma || + tr->st.background_gamma == 0 || + tc->sBIT_G == 1); + + tc->sp = tc->dp; /* nothing else changes */ + + do + { + const png_uint_32 alpha = (sp[2] << 8) + sp[3]; + + switch (alpha) + { + case 0U: /* transparent */ + memset(dp, 0U, 4U); + break; + + default: + { + png_uint_32 v = ((sp[0] << 8) + sp[1]) * alpha + + background * (65535U - alpha); + + PNG_COMPOSE_DIV_65535(v); + debug(v <= 65535U); + dp[0] = PNG_BYTE(v >> 8); + dp[1] = PNG_BYTE(v); + } + + if (compose) + dp[3] = dp[2] = 0xFFU; /* alpha; set to 1.0 */ + + else if (copy) + { + dp[2] = PNG_BYTE(alpha >> 8); + dp[3] = PNG_BYTE(alpha); + } + break; + + case 65535U: /* opaque */ + if (copy) + memcpy(dp, sp, 4U); break; } -# define CORRECT(v, c)\ - gamma_correct_background((v)*mult, bit_depth,\ - &png_ptr->background.c, &png_ptr->background_1.c,\ - gamma_correct, gamma_to_1);\ - if (bit_depth > required_bit_depth)\ - png_ptr->background.c =\ - (png_uint_16)PNG_DIV257(png_ptr->background.c) + sp += 4U; + dp += 4U; + } + while (sp < ep); - /* The multiplier 'mult' scales the values to 'required_depth', - * 'bit_depth' is the depth of the resultant values. - */ - while (bit_depth < required_bit_depth) - mult += mult << bit_depth, bit_depth <<= 1; + debug(sp == ep+3U); +# undef png_ptr +} - /* In the event that this still leaves the background bit depth greater - * than the libpng required depth scale the values back to the 8-bit - * range, the test below verifies that this is correct. - */ - if (bit_depth > required_bit_depth) +static void +png_do_background_alpha_RGBA(png_transformp *transform, + png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 7U/*safety*/; + const unsigned int bred = tr->st.background.red; + const unsigned int bgreen = tr->st.background.green; + const unsigned int bblue = tr->st.background.blue; + const int copy = (sp != dp); + const int compose = tr->st.compose_background; + + affirm(tc->bit_depth == 16U && tc->format == PNG_FORMAT_RGBA && + tr->st.background_bit_depth == 16U); + + debug(tr->st.background_gamma == tc->gamma || + tr->st.background_gamma == 0 || + (tc->sBIT_R == 1 && tc->sBIT_G == 1 && tc->sBIT_B == 1)); + + tc->sp = tc->dp; /* nothing else changes */ + + do + { + const png_uint_32 alpha = (sp[6] << 8) + sp[7]; + + switch (alpha) { - if (bit_depth != 16 || required_bit_depth != 8) - png_error(png_ptr, "internal error handling background depth"); + case 0U: /* transparent */ + memset(dp, 0U, 8U); + break; + + default: + { + const png_uint_32 balpha = (65535U - alpha); + png_uint_32 r = ((sp[0] << 8) + sp[1]) * alpha + bred * balpha; + png_uint_32 g = ((sp[2] << 8) + sp[3]) * alpha + bgreen * balpha; + png_uint_32 b = ((sp[4] << 8) + sp[5]) * alpha + bblue * balpha; + + PNG_COMPOSE_DIV_65535(r); + PNG_COMPOSE_DIV_65535(g); + PNG_COMPOSE_DIV_65535(b); + debug(r <= 65535U && g <= 65535U && b <= 65535U); + dp[0] = PNG_BYTE(r >> 8); + dp[1] = PNG_BYTE(r); + dp[2] = PNG_BYTE(g >> 8); + dp[3] = PNG_BYTE(g); + dp[4] = PNG_BYTE(b >> 8); + dp[5] = PNG_BYTE(b); + } + + if (compose) + dp[7] = dp[6] = 0xFFU; + + else if (copy) + { + dp[6] = PNG_BYTE(alpha >> 8); + dp[7] = PNG_BYTE(alpha); + } + break; + + case 65535U: /* opaque */ + if (copy) + memcpy(dp, sp, 8U); + break; } - if ((color_type & PNG_COLOR_MASK_COLOR) != 0) - { - png_ptr->flags &= ~PNG_FLAG_BACKGROUND_IS_GRAY; /* checked below */ + sp += 8U; + dp += 8U; + } + while (sp < ep); - /* If need_expand was passed to png_set_background the background value - * was in the file format, therefore if the file is a palette file the - * background will have been an index into the palette. Notice that if - * need_expand was false then the color is RGB even if the output still - * has a palette. + debug(sp == ep+7U); +# undef png_ptr +} + +static void +png_init_background_alpha_end(png_transformp *transform, + png_transform_controlp tc) + /* This is just the last part of png_init_background_alpha (below) */ +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + + debug(tc->init == PNG_TC_INIT_FINAL); + + /* Repeat the tests at the end of png_init_background_alpha: */ + affirm(tc->bit_depth == 16U && (tc->format & PNG_FORMAT_FLAG_ALPHA) != 0); + debug(tc->gamma == 0 || + !png_gamma_significant(png_ptr, tc->gamma, tc_sBIT(tc))); + + /* tr->st.background_is_gray was filled in by resolve_background_color and + * records if either the background was a gray value or it was a color + * value with all the channels equal. + */ + if (!tr->st.background_is_gray && !(tc->format & PNG_FORMAT_FLAG_COLOR)) + { +# ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + /* Color background with gray data: this happens when there is a + * gray to RGB transform in the pipeline but it hasn't happened + * yet. Unfortunately it has to happen now to be able to do the + * compose against the colored background. */ - if (expand && (color_type & PNG_COLOR_MASK_PALETTE) != 0) - { - unsigned int background_index = png_ptr->background.index; + png_push_gray_to_rgb_byte_ops(transform, tc); + affirm((tc->format & PNG_FORMAT_FLAG_COLOR) != 0); + return; +# else /* !GRAY_TO_RGB */ + impossible("gray to RGB"); /* how can this happen? */ +# endif /* !GRAY_TO_RGB */ + } - if (background_index < png_ptr->num_palette && - png_ptr->palette != NULL) + /* The transform happens in two parts, a part to do the arithmetic on + * pixels where it is required followed by a part to replace transparent + * pixels. These two parts require different versions of the background + * pixel. Set up the second part first. + * + * This only happens with background composition, otherwise the + * transparent pixels are already 0 and nothing needs to be done. + */ + if (tr->st.compose_background) + { + /* The transparent pixel handling happens *after* the data has been + * re-encoded to the output gamma: + */ + png_transform_background *tr_alpha = + png_transform_cast(png_transform_background, + png_add_transform(png_ptr, sizeof (png_transform_background), + png_init_background_transparent, PNG_TR_GAMMA_ENCODE+0xF0U)); + + /* Copy the current state into the new png_transform_background: */ + tr_alpha->st = tr->st; + tr_alpha->tr.args = tr->tr.args; + } + + /* Now it is possible to overwrite tr->st.background with the linear version. + */ + gamma_correct_background(tr, tc); + + /* sBIT informationmust also be invalidated here, because a gamma + * transform may run before the transparent pixel handling. + */ + tc->invalid_info |= PNG_INFO_sBIT; + tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A = + png_check_byte(png_ptr, tc->bit_depth); + + /* And select an appropriate function; there are only two choices: */ + switch (tc->format) + { + case PNG_FORMAT_GA: + /* If the background format is color this indicates that there is a + * gray to RGB transform missing and we need it to happen before + * this point! + */ + affirm(tr->st.background_is_gray); + tr->tr.fn = png_do_background_alpha_GA; + break; + + case PNG_FORMAT_RGBA: + if (tr->st.background_is_gray) + tr->st.background.blue = tr->st.background.green = + tr->st.background.red = tr->st.background.gray; + tr->tr.fn = png_do_background_alpha_RGBA; + break; + + default: + NOT_REACHED; + } +# undef png_ptr +} + +static void +png_init_background_alpha(png_transformp *transform, png_transform_controlp tc) + /* This is used when alpha composition is required because the alpha channel + * may contain values that are between 0 and 1. Because doing alpha + * composition requires linear arithmetic the data is converted to 16-bit + * linear, however this means that the background pixel gets converted too + * and, for 16-bit output, this tends to smash the value. Consequently the + * algorithm used here is to skip those pixels and use the 'transparent + * alpha' routines to replace them after the gamma correction step. + */ +{ +# define png_ptr (tc->png_ptr) + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + + debug(tc->init == PNG_TC_INIT_FINAL); + /* png_init_background ensures this is true: */ + debug((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0); + + /* Always push gamma transforms; don't try to optimize the case when they + * aren't needed because that would be an attempt to duplicate the tests in + * png_init_gamma and it might now work reliably. + * + * Need to push the to-linear transform *before* this transform and add gamma + * correction afterward to get back to the screen format. Do the afterward + * bit first to avoid complexity over *transform: + */ + { + png_transform_gamma *tr_end = add_gamma_transform(png_ptr, + PNG_TR_GAMMA_ENCODE, tc->gamma, 0U/*bit depth*/, 0/*default*/); + + /* Encoding the alpha channel happens in the last step, so this needs to + * be set here. Notice that in C++ terms we are very friendly with + * png_transform_gamma. + */ + tr_end->encode_alpha = tr->st.encode_alpha; + tr_end->optimize_alpha = tr->st.optimize_alpha; + } + + { + /* Now add tr_gamma before this transform, expect it to go in at + * *transform or the whole thing won't work: + */ + png_transform_gamma *tr_gamma = png_transform_cast(png_transform_gamma, + png_push_transform(png_ptr, sizeof (png_transform_gamma), + png_init_gamma, transform, NULL/*don't run init*/)); + + /* This must happen before we run png_gamma_init: */ + tr_gamma->to_gamma = PNG_FP_1; + tr_gamma->to_bit_depth = 16U; + + /* Now run the this transform; it was pushed before this one, so it gets + * to do its init first and this function must return as the caller will + * immediately call here again. + */ + debug(*transform == &tr_gamma->tr); + png_init_gamma(transform, tc); + affirm(tc->bit_depth == 16U && + (tc->format & PNG_FORMAT_FLAG_ALPHA) != 0); + /* This is only a 'debug' because it needs to replicate the test in + * png_init_gamma and that is easy to get wrong (a harmless mistake). + */ + debug(tc->gamma == 0 || + !png_gamma_significant(png_ptr, tc->gamma, tc_sBIT(tc))); + } + + /* A transform was pushed, so this transform init will be run again: */ + tr->tr.fn = png_init_background_alpha_end; +# undef png_ptr +} + +/* Handle alpha and tRNS via a background color */ +static void +png_init_background(png_transformp *transform, png_transform_controlp tc) +{ + /* This init function is called right at the start, this means it can get at + * the tRNS values if appropriate. If not the RGB to gray transform comes + * next followed by PNG_TR_COMPOSE_ALPHA, which actually does the non-tRNS + * work. + */ + png_structp png_ptr = tc->png_ptr; + png_transform_background *tr = + png_transform_cast(png_transform_background, *transform); + + if (tc->init == PNG_TC_INIT_FORMAT) + { + /* Background composition removes the alpha channel, so the other + * operations become irrelevant: + */ + if (tr->st.compose_background) + tr->st.associate_alpha = tr->st.encode_alpha = tr->st.optimize_alpha = + 0U; + + else if (!tr->st.associate_alpha) + { + /* There is nothing to do, delete the whole transform. */ + tr->tr.fn = NULL; + return; + } + + /* Else alpha association ('pre-multiplication') which is achieved by + * composing on a 0 background. The background color will be black (all + * zeros) and the background gamma will be zero. + */ + + /* Because we are in PNG_TC_INIT_FORMAT no other transforms will have been + * inserted between this one and an rgb-to-gray transform, so we can find + * out if rgb-to-gray has been requested: + */ + tr->st.rgb_to_gray = tr->tr.next != NULL && + tr->tr.next->order == PNG_TR_RGB_TO_GRAY; + + if ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0) + { + /* Associated alpha does not strip the alpha channel! */ + if (tr->st.compose_background) + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA); + } + + else if (!tc->palette && + png_ptr->num_trans == 1 && !(tc->invalid_info & PNG_INFO_tRNS)) + { + /* tRNS will be expanded, or handled */ + tc->invalid_info |= PNG_INFO_tRNS; + if (!tr->st.compose_background) + { + tc->format |= PNG_FORMAT_FLAG_ALPHA; + /* And in this case, only, because we are adding an alpha channel we + * need to have a channel depth of at least 8: + */ + if (tc->bit_depth < 8U) + tc->bit_depth = 8U; + } + } + + else /* no transparent pixels to change */ + tr->tr.fn = NULL; + } + + else /* PNG_TC_INIT_FINAL */ + { + png_fixed_point correction; + + debug(tc->init == PNG_TC_INIT_FINAL && + ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0 || + (!tc->palette && png_ptr->num_trans == 1 && + !(tc->invalid_info & PNG_INFO_tRNS)))); + + /* The screen gamma is known, so the background gamma can be found, note + * that both the gamma values used below will be 0 if no gamma information + * was in the PNG and no gamma information has been provided by + * png_set_gamma or png_set_alpha_mode. + */ + switch (tr->st.background_gamma) + { + case PNG_BACKGROUND_GAMMA_FILE: + /* png_init_transform_control has already found the file gamma, + * and because this is the first arithmetic transformation + * nothing has changed it. + */ + tr->st.background_gamma = tc->gamma; + break; + + case PNG_BACKGROUND_GAMMA_SCREEN: + tr->st.background_gamma = png_ptr->row_gamma; + break; + + default: + /* already set */ + break; + } + + /* Work out what the background color is, this only depends on 'tc' for + * palette information, so it can be done now before we know the actual + * bit_depth/format that will be required: + */ + resolve_background_color(tr, tc); + + /* Is this format compatible with the current row data? If it is then it + * is possible to avoid the arithmetic if no alpha processing is required. + * This is a useful optimization because PNG files with just transparent + * pixels and no alpha are common. + * + * NOTE: if an RGB-to-gray transform is present this is fine so long as + * the background is gray, otherwise (non-gray background) there is a + * following gray-to-RGB transform and the now gray image must be + * composited on a color background. + */ + if (tr->st.compose_background /* alpha channel stripped */ && + (tr->st.background_is_gray || + ((tc->format & PNG_FORMAT_FLAG_COLOR) != 0 && !tr->st.rgb_to_gray)) + /* color compatible */ && + tc->bit_depth >= tr->st.background_bit_depth + /* bit depth compatible */ && + (tc->transparent_alpha || + (!tc->palette && png_ptr->num_trans == 1 && + !(tc->invalid_info & PNG_INFO_tRNS))) + /* no alpha processing */ && + png_gamma_equal(png_ptr, tc->gamma, png_ptr->row_gamma, &correction, + tc->bit_depth) /* gamma compatible (so no gamma processing) */) + { + /* How the operation gets performed depends on whether the current data + * has an alpha channel or not. + */ + if ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0) + { + affirm(tc->transparent_alpha); + /* This init routine does the sBIT handling: */ + png_init_background_transparent(transform, tc); + } + + else if (!tc->palette && png_ptr->num_trans == 1 && + !(tc->invalid_info & PNG_INFO_tRNS)) + { + /* The background pixel needs to be filled in now; no more init + * routines are called in this case. It is important to delay this + * as late as possible because it needs to know the actual tc format + * that must be used. + */ + fill_background_pixel(tr, tc); + + debug(!(png_ptr->color_type & PNG_COLOR_MASK_PALETTE)); + + /* The pixel depth should not have been changed yet: */ + debug(PNG_PIXEL_DEPTH(*png_ptr) == PNG_TC_PIXEL_DEPTH(*tc)); + + /* The transparent_pixel value needs to be filled in. */ + affirm(tr->st.ntrans == + fill_transparent_pixel(png_ptr, tr->st.transparent_pixel)); + + /* The whole operation is a no-op if the transparent pixel and the + * background pixel match, even in the associated alpha case where + * both will be 0 throughout. + * + * NOTE: for palette images this test happens in the caching + * operation, so the answer is still correct. + * + * NOTE: for low bit depth gray both 'transparent_pixel' and + * 'background_pixel' have been expanded to fill a byte, so this + * works. + */ + if (memcmp(tr->st.transparent_pixel, tr->st.background_pixel, + tr->st.ntrans) == 0) + tr->tr.fn = NULL; + + /* Then the processing function depends on the pixel size: */ + else if (tr->st.ntrans > 1U) + tr->tr.fn = png_do_replace_tRNS_multi; + + else if (tc->bit_depth == 8U) + tr->tr.fn = png_do_replace_tRNS_8; + + else if (tc->bit_depth == 1U) { - /* In fact 'mult' is always 1 at present in this case */ - CORRECT(png_ptr->palette[background_index].red, red); - CORRECT(png_ptr->palette[background_index].green, green); - CORRECT(png_ptr->palette[background_index].blue, blue); + /* This is the silly case: the replacement pixel does not match + * the transparent pixel (handled above) so either all the '0' + * bits are replaced by '1' or all the '1' bits are replaced by + * '0': + */ + png_uint_32 args = tr->st.background_pixel[0]; + + args <<= 24; + args |= PNG_INFO_tRNS | PNG_INFO_sRGB; + tr->tr.args = args; + tr->tr.fn = png_do_set_row; } else - { - png_app_error(png_ptr, "out of range background index"); - memset(&png_ptr->background, 0, sizeof png_ptr->background); - memset(&png_ptr->background_1, 0, sizeof png_ptr->background_1); - } + tr->tr.fn = png_do_replace_tRNS_lbd; + + tc->invalid_info |= PNG_INFO_tRNS | PNG_INFO_sBIT; + tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A = + png_check_byte(png_ptr, tc->bit_depth); } else { - CORRECT(png_ptr->background.red, red); - CORRECT(png_ptr->background.green, green); - CORRECT(png_ptr->background.blue, blue); + /* Nothing to do; should have been eliminated before! */ + tr->tr.fn = NULL; + NOT_REACHED; } + } - if (png_ptr->background.red == png_ptr->background.blue && - png_ptr->background.red == png_ptr->background.green) + else /* alpha, or maybe gamma, processing required */ + { + /* Alpha case, add an appropriate transform; this has to be done + * *after* the RGB-to-gray case so move the transform info there: + */ + png_transform_background *tr_alpha = + png_transform_cast(png_transform_background, + png_add_transform(png_ptr, sizeof (png_transform_background), + png_init_background_alpha, PNG_TR_COMPOSE_ALPHA)); + + /* Copy the current state into the new png_transform_background: */ + tr_alpha->st = tr->st; + tr_alpha->tr.args = tr->tr.args; + + /* The rest of the init occurs later; this transform is no longer + * needed. + */ + tr->tr.fn = NULL; + + /* Ensure that png_init_background_alpha gets an alpha channel, this + * needs to happen here because otherwise intervening transforms can + * invalidate tRNS. + */ + tc->expand_tRNS = 1U; + if (tr->st.compose_background) + tc->strip_alpha = 0U; + + /* And push the expand: */ + (void)push_gamma_expand(transform, tc, 1/*need alpha*/); + + /* Regardless of whether anything got pushed the following should now + * be true: + */ + affirm((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0 && + tc->bit_depth >= 8U); + } + } +} + +void PNGFAPI +png_set_background_fixed(png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, png_fixed_point background_gamma) +{ + if (png_ptr != NULL) + { + if (background_color != NULL) + { + png_transform_background *tr = + png_transform_cast(png_transform_background, + png_add_transform(png_ptr, sizeof (png_transform_background), + png_init_background, PNG_TR_COMPOSE)); + + /* This silently overwrites the information if png_set_background is + * called more than once. + */ + tr->st.background = *background_color; + tr->st.need_expand = need_expand != 0; + tr->st.compose_background = 1U; /* png_set_background called */ + switch (background_gamma_code) { - png_ptr->flags |= PNG_FLAG_BACKGROUND_IS_GRAY; - png_ptr->background.gray = png_ptr->background.red; - png_ptr->background_1.gray = png_ptr->background_1.red; - } + case PNG_BACKGROUND_GAMMA_SCREEN: + case PNG_BACKGROUND_GAMMA_FILE: + tr->st.background_gamma = background_gamma_code; + break; - else - png_ptr->background.gray = png_ptr->background_1.gray = 0; + case PNG_BACKGROUND_GAMMA_UNIQUE: + if (background_gamma >= 16 && background_gamma <= 625000000) + { + tr->st.background_gamma = background_gamma; + break; + } + + png_app_error(png_ptr, "gamma value out of range"); + /* FALL THROUGH */ + default: + png_app_error(png_ptr, "invalid gamma information"); + tr->st.background_gamma = (need_expand ? + PNG_BACKGROUND_GAMMA_FILE : PNG_BACKGROUND_GAMMA_SCREEN); + break; + } } else - { - png_ptr->flags |= PNG_FLAG_BACKGROUND_IS_GRAY; - - CORRECT(png_ptr->background.gray, gray); - - png_ptr->background.red = - png_ptr->background.green = - png_ptr->background.blue = png_ptr->background.gray; - - png_ptr->background_1.red = - png_ptr->background_1.green = - png_ptr->background_1.blue = png_ptr->background_1.gray; - } -# undef CORRECT - } -} -#endif /* READ_BACKGROUND || READ_ALPHA_MODE */ - -static void /* PRIVATE */ -png_init_palette_transformations(png_structrp png_ptr) -{ - int input_has_alpha = 0; - int input_has_transparency = 0; - - if (png_ptr->num_trans > 0) - { - int i; - - /* Ignore if all the entries are opaque (unlikely!) */ - for (i=0; inum_trans; ++i) - { - if (png_ptr->trans_alpha[i] == 255) - continue; - else if (png_ptr->trans_alpha[i] == 0) - input_has_transparency = 1; - else - { - input_has_transparency = 1; - input_has_alpha = 1; - break; - } - } - } - - /* If no alpha we can optimize. */ - if (input_has_alpha == 0) - { - /* Any alpha means background and associative alpha processing is - * required, however if the alpha is 0 or 1 throughout OPTIMIZE_ALPHA - * and ENCODE_ALPHA are irrelevant. - */ - png_ptr->transformations &= ~PNG_ENCODE_ALPHA; - png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; - - if (input_has_transparency == 0) - png_ptr->transformations &= ~PNG_COMPOSE; + png_app_error(png_ptr, "missing background color"); } } -static void /* PRIVATE */ -png_init_rgb_transformations(png_structrp png_ptr) +# ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_background(png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma) { - /* Added to libpng-1.5.4: check the color type to determine whether there - * is any alpha or transparency in the image and simply cancel the - * background and alpha mode stuff if there isn't. - */ - int input_has_alpha = (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0; - int input_has_transparency = png_ptr->num_trans > 0; - - /* If no alpha we can optimize. */ - if (input_has_alpha == 0) - { - /* Any alpha means background and associative alpha processing is - * required, however if the alpha is 0 or 1 throughout OPTIMIZE_ALPHA - * and ENCODE_ALPHA are irrelevant. - */ -# ifdef PNG_READ_ALPHA_MODE_SUPPORTED - png_ptr->transformations &= ~PNG_ENCODE_ALPHA; - png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; -# endif - - if (input_has_transparency == 0) - png_ptr->transformations &= ~PNG_COMPOSE; - } + png_set_background_fixed(png_ptr, background_color, background_gamma_code, + need_expand, png_fixed(png_ptr, background_gamma, "png_set_background")); } - -void /* PRIVATE */ -png_init_read_transformations(png_structrp png_ptr) -{ - png_debug(1, "in png_init_read_transformations"); - - /* This internal function is called from png_read_start_row in pngrutil.c - * and it is called before the 'rowbytes' calculation is done, so the code - * in here can change or update the transformations flags. - * - * First do updates that do not depend on the details of the PNG image data - * being processed. - */ - -#ifdef PNG_READ_GAMMA_SUPPORTED - /* Prior to 1.5.4 these tests were performed from png_set_gamma, 1.5.4 adds - * png_set_alpha_mode and this is another source for a default file gamma so - * the test needs to be performed later - here. In addition prior to 1.5.4 - * the tests were repeated for the PALETTE color type here - this is no - * longer necessary (and doesn't seem to have been necessary before.) - */ - { - /* The following temporary indicates if overall gamma correction is - * required. - */ - int gamma_correction = 0; - - if (png_ptr->colorspace.gamma != 0) /* has been set */ - { - if (png_ptr->screen_gamma != 0) /* screen set too */ - gamma_correction = png_gamma_threshold(png_ptr->colorspace.gamma, - png_ptr->screen_gamma); - - else - /* Assume the output matches the input; a long time default behavior - * of libpng, although the standard has nothing to say about this. - */ - png_ptr->screen_gamma = png_reciprocal(png_ptr->colorspace.gamma); - } - - else if (png_ptr->screen_gamma != 0) - /* The converse - assume the file matches the screen, note that this - * perhaps undesireable default can (from 1.5.4) be changed by calling - * png_set_alpha_mode (even if the alpha handling mode isn't required - * or isn't changed from the default.) - */ - png_ptr->colorspace.gamma = png_reciprocal(png_ptr->screen_gamma); - - else /* neither are set */ - /* Just in case the following prevents any processing - file and screen - * are both assumed to be linear and there is no way to introduce a - * third gamma value other than png_set_background with 'UNIQUE', and, - * prior to 1.5.4 - */ - png_ptr->screen_gamma = png_ptr->colorspace.gamma = PNG_FP_1; - - /* We have a gamma value now. */ - png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; - - /* Now turn the gamma transformation on or off as appropriate. Notice - * that PNG_GAMMA just refers to the file->screen correction. Alpha - * composition may independently cause gamma correction because it needs - * linear data (e.g. if the file has a gAMA chunk but the screen gamma - * hasn't been specified.) In any case this flag may get turned off in - * the code immediately below if the transform can be handled outside the - * row loop. - */ - if (gamma_correction != 0) - png_ptr->transformations |= PNG_GAMMA; - - else - png_ptr->transformations &= ~PNG_GAMMA; - } -#endif - - /* Certain transformations have the effect of preventing other - * transformations that happen afterward in png_do_read_transformations, - * resolve the interdependencies here. From the code of - * png_do_read_transformations the order is: - * - * 1) PNG_EXPAND (including PNG_EXPAND_tRNS) - * 2) PNG_STRIP_ALPHA (if no compose) - * 3) PNG_RGB_TO_GRAY - * 4) PNG_GRAY_TO_RGB iff !PNG_FLAG_BACKGROUND_IS_GRAY - * 5) PNG_COMPOSE - * 6) PNG_GAMMA - * 7) PNG_STRIP_ALPHA (if compose) - * 8) PNG_ENCODE_ALPHA - * 9) PNG_SCALE_16_TO_8 - * 10) PNG_16_TO_8 - * 11) PNG_QUANTIZE (converts to palette) - * 12) PNG_EXPAND_16 - * 13) PNG_GRAY_TO_RGB iff PNG_FLAG_BACKGROUND_IS_GRAY - * 14) PNG_INVERT_MONO - * 15) PNG_INVERT_ALPHA - * 16) PNG_SHIFT - * 17) PNG_PACK - * 18) PNG_BGR - * 19) PNG_PACKSWAP - * 20) PNG_FILLER (includes PNG_ADD_ALPHA) - * 21) PNG_SWAP_ALPHA - * 22) PNG_SWAP_BYTES - * 23) PNG_USER_TRANSFORM [must be last] - */ -#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED - if (png_ptr->transformations & PNG_STRIP_ALPHA) - { - if (!(png_ptr->transformations & PNG_FILLER)) - png_ptr->transformations &= ~(PNG_INVERT_ALPHA|PNG_SWAP_ALPHA); - - if (!(png_ptr->transformations & PNG_COMPOSE)) - { - /* Stripping the alpha channel happens immediately after the 'expand' - * transformations, before all other transformations, so it cancels out - * the alpha handling. It has the side effect negating the effect of - * PNG_EXPAND_tRNS too: - */ - png_ptr->transformations &= ~(PNG_ENCODE_ALPHA | PNG_EXPAND_tRNS); - png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; - - /* Kill the tRNS chunk itself too. Prior to 1.5.4 this did not happen - * so transparency information would remain just so long as it wasn't - * expanded. This produces unexpected API changes if the set of things - * that do PNG_EXPAND_tRNS changes (perfectly possible given the - * documentation - which says ask for what you want, accept what you - * get.) This makes the behavior consistent from 1.5.4: - */ - png_ptr->num_trans = 0; - } - } -#endif /* STRIP_ALPHA supported, no COMPOSE */ +# endif /* FLOATING_POINT */ +#endif /* READ_BACKGROUND */ #ifdef PNG_READ_ALPHA_MODE_SUPPORTED - /* If the screen gamma is about 1.0 then the OPTIMIZE_ALPHA and ENCODE_ALPHA - * settings will have no effect. - */ - if (!png_gamma_significant(png_ptr->screen_gamma)) +void PNGFAPI +png_set_alpha_mode_fixed(png_structrp png_ptr, int mode, + png_fixed_point output_gamma) +{ + if (png_ptr != NULL) { - png_ptr->transformations &= ~PNG_ENCODE_ALPHA; - png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; - } -#endif + /* Check the passed in output_gamma value; it must be valid and it must be + * converted to the reciprocal for use below: + */ + output_gamma = translate_gamma_flags(png_ptr, output_gamma, 1/*screen*/); -#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED - /* Make sure the coefficients for the rgb to gray conversion are set - * appropriately. - */ - if (png_ptr->transformations & PNG_RGB_TO_GRAY) - png_colorspace_set_rgb_coefficients(png_ptr); -#endif - - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) - png_init_palette_transformations(png_ptr); - - else - png_init_rgb_transformations(png_ptr); - -#ifdef PNG_READ_BACKGROUND_SUPPORTED - /* Set up the background information if required. It is only used if - * PNG_COMPOSE is specified. - */ - if (png_ptr->transformations & PNG_COMPOSE) - png_init_background_transformations(png_ptr); -#endif - - /* For indexed PNG data (PNG_COLOR_TYPE_PALETTE) many of the transformations - * can be performed directly on the palette, and some (such as rgb to gray) - * can be optimized inside the palette. This is particularly true of the - * composite (background and alpha) stuff, which can be pretty much all done - * in the palette even if the result is expanded to RGB or gray afterward. - * - * NOTE: this is Not Yet Implemented, the code behaves as in 1.5.1 and - * earlier and the palette stuff is actually handled on the first row. This - * leads to the reported bug that the palette returned by png_get_PLTE is not - * updated. - */ -#if 0 /* NYI */ - png_do_palette_transformations(png_ptr); -#endif - -#ifdef PNG_READ_GAMMA_SUPPORTED - /* This needs to change - in the palette image case a whole set of tables are - * built when it would be quicker to just calculate the correct value for - * each palette entry directly. Also, the test is too tricky - why check - * PNG_RGB_TO_GRAY if PNG_GAMMA is not set? The answer seems to be that - * PNG_GAMMA is cancelled even if the gamma is known? The test excludes the - * PNG_COMPOSE case, so apparently if there is no *overall* gamma correction - * the gamma tables will not be built even if composition is required on a - * gamma encoded value. - * - * In 1.5.4 this is addressed below by an additional check on the individual - * file gamma - if it is not 1.0 both RGB_TO_GRAY and COMPOSE need the - * tables. - */ - if ((png_ptr->transformations & PNG_GAMMA) - || ((png_ptr->transformations & PNG_RGB_TO_GRAY) - && (png_gamma_significant(png_ptr->colorspace.gamma) || - png_gamma_significant(png_ptr->screen_gamma))) - || ((png_ptr->transformations & PNG_COMPOSE) - && (png_gamma_significant(png_ptr->colorspace.gamma) - || png_gamma_significant(png_ptr->screen_gamma) -# ifdef PNG_READ_BACKGROUND_SUPPORTED - || (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_UNIQUE - && png_gamma_significant(png_ptr->background_gamma)) -# endif - )) || ((png_ptr->transformations & PNG_ENCODE_ALPHA) - && png_gamma_significant(png_ptr->screen_gamma)) - ) - { - png_build_gamma_tables(png_ptr, png_ptr->bit_depth); - -#ifdef PNG_READ_BACKGROUND_SUPPORTED - if (png_ptr->transformations & PNG_COMPOSE) + if (output_gamma > 0) /* Else an app_error has been signalled. */ { - /* Issue a warning about this combination: because RGB_TO_GRAY is - * optimized to do the gamma transform if present yet do_background has - * to do the same thing if both options are set a - * double-gamma-correction happens. This is true in all versions of - * libpng to date. + /* Only set the colorspace gamma if it has not already been set (this + * has the side effect that the gamma in a second call to + * png_set_alpha_mode will be ignored.) */ - if (png_ptr->transformations & PNG_RGB_TO_GRAY) - png_warning(png_ptr, - "libpng does not support gamma+background+rgb_to_gray"); - - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + if ((png_ptr->colorspace.flags & + (PNG_COLORSPACE_INVALID | PNG_COLORSPACE_HAVE_GAMMA)) != + PNG_COLORSPACE_HAVE_GAMMA) { - unsigned int i, num_palette = png_ptr->num_palette; - png_color back; - png_color_16 back_1 = png_ptr->background_1; - png_colorp palette = png_ptr->palette; + /* The default file gamma is the output gamma encoding: */ + png_ptr->colorspace.gamma = output_gamma; + if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) + png_ptr->colorspace.flags = PNG_COLORSPACE_HAVE_GAMMA; + else + png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; + } - back.red = (png_byte)png_ptr->background.red; - back.green = (png_byte)png_ptr->background.green; - back.blue = (png_byte)png_ptr->background.blue; + /* Always set the output gamma, note that it may be changed to PNG_FP_1 + * for the associated alpha support. This means that the last call to + * png_set_gamma[_fixed] or png_set_alpha_mode sets the output gamma, + * which is probably what is expected. + */ + { + png_transform_gamma *tr_gamma = add_gamma_transform(png_ptr, + PNG_TR_GAMMA_ENCODE, + mode == PNG_ALPHA_ASSOCIATED ? PNG_FP_1 : output_gamma, 0U, + 1/*force*/); - for (i = 0; i < num_palette; i++) + /* Get a background transform and set the appropriate fields. + * + * png_set_background removes the alpha channel so it effectively + * disbles png_set_alpha_mode however png_set_alpha_mode is still + * useful to set a default gamma value. + */ + png_transform_background *tr = + png_transform_cast(png_transform_background, + png_add_transform(png_ptr, sizeof (png_transform_background), + png_init_background, PNG_TR_COMPOSE)); + + /* There are really 8 possibilities here, composed of any + * combination of: + * + * premultiply the color channels + * do not encode non-opaque pixels (leave as linear) + * encode the alpha as well as the color channels + * + * The differences disappear if the input/output ('screen') gamma is + * 1.0, because then the encoding is a no-op and there is only the + * choice of premultiplying the color channels or not. + */ + switch (mode) { - if (i < png_ptr->num_trans && png_ptr->trans_alpha[i] != 0xff) + case PNG_ALPHA_PNG: /* default: png standard */ + /* No compose, but it may be set by png_set_background! This + * is the only mode that doesn't interfere with what + * png_set_background does. + */ + tr->st.associate_alpha = 0U; + tr_gamma->encode_alpha = tr->st.encode_alpha = 0U; + tr_gamma->optimize_alpha = tr->st.optimize_alpha = 0U; + break; + + case PNG_ALPHA_ASSOCIATED: /* color channels premultiplied */ + tr->st.associate_alpha = 1U; + tr_gamma->encode_alpha = tr->st.encode_alpha = 0U; + tr_gamma->optimize_alpha = tr->st.optimize_alpha = 0U; + break; + + case PNG_ALPHA_OPTIMIZED: + /* associated with opaque pixels having the given gamma and + * non-opaque pixels being linear. + */ + tr->st.associate_alpha = 1U; + tr_gamma->encode_alpha = tr->st.encode_alpha = 0U; + tr_gamma->optimize_alpha = tr->st.optimize_alpha = 1U; + /* output_gamma records the encoding of opaque pixels! */ + break; + + case PNG_ALPHA_BROKEN: + /* associated+non-linear+alpha encoded */ + tr->st.associate_alpha = 1U; + tr_gamma->encode_alpha = tr->st.encode_alpha = 1U; + tr_gamma->optimize_alpha = tr->st.optimize_alpha = 0U; + break; + + default: + png_app_error(png_ptr, "invalid alpha mode"); + /* A return at this point is safe; if a background transform + * was created the init routine will remove it because + * nothing is set. + */ + break; + } /* alpha mode switch */ + } /* add gamma and background transforms */ + } /* valid output gamma */ + } /* png_ptr != NULL */ +} + +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_alpha_mode(png_structrp png_ptr, int mode, double output_gamma) +{ + png_set_alpha_mode_fixed(png_ptr, mode, convert_gamma_value(png_ptr, + output_gamma)); +} +#endif /* FLOATING_POINT */ +#endif /* READ_ALPHA_MODE */ + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +typedef struct +{ + png_transform tr; + png_transform_control tc; + union + { + png_uint_32 u32[1]; /* ensure alignment */ + png_uint_16 u16[1]; + png_byte b8[1]; + } cache; +} png_transform_cache; + +#define png_transform_cache_size(size)\ + (offsetof(png_transform_cache, cache)+(size)) +#define png_transform_cache_cast(pointer,size)\ + png_voidcast(png_transform_cache*,\ + png_transform_cast_check(png_ptr, PNG_SRC_LINE, (pointer),\ + png_transform_cache_size(size))) + /* This is like png_transform_cast except that 'size' is the size of the + * cache part in the above structure and the type returned is always + * 'png_transform_cache*'. + */ + +/* Functions to handle the cache operation. These don't do any initialization; + * that happens below when PNG_TC_INIT_FINAL is being run on the whole list. + * These functions are only implemented for read so the transform control + * source and destination are always aligned. + * + * First some utility functions: + */ +static void +png_transform_control_cp(png_transform_controlp tcDest, + png_const_transform_controlp tcSrc) +{ + /* Copy tcSrc over tcDest without overwriting the information specific to the + * row being transformed. + */ + png_structp png_ptr = tcDest->png_ptr; + png_const_voidp sp = tcDest->sp; + png_voidp dp = tcDest->dp; + png_uint_32 width = tcDest->width; + unsigned int init = tcDest->init; + + *tcDest = *tcSrc; + + tcDest->png_ptr = png_ptr; + tcDest->sp = sp; + tcDest->dp = dp; + tcDest->width = width; + tcDest->init = png_check_bits(tcDest->png_ptr, init, 2); +} + +#if !PNG_RELEASE_BUILD +static int +png_transform_control_eq(png_const_transform_controlp tc1, + png_const_transform_controlp tc2) +{ + /* Say if *tc1 == *tc2, ignoring differences in uncopied fields and 'cost': + */ + return +# ifdef PNG_READ_GAMMA_SUPPORTED + tc1->gamma == tc2->gamma && +# endif + tc1->format == tc2->format && + tc1->range == tc2->range && + tc1->bit_depth == tc2->bit_depth && + tc1->caching == tc2->caching && + tc1->palette == tc2->palette; + /* invalid_info, cost, interchannel and channel_add are only set during + * init, so don't do the compare. + */ +} +#endif /* !RELEASE_BUILD */ + +/* Now the routines that actually perform the transform. There are two basic + * cases: + * + * 1) A cached transform that does not change the pixel size and where the pixel + * size 8 bits or less. This can be done by a 256-entry single byte lookup + * table, regardless of the bit depth. Two versions of the code exist, one + * which just transforms the row, the other which transforms and records the + * maximum pixel depth. + * + * 2) A cached transform that increases pixel depth. The destination pixel + * depth will always be a multiple of 8 bits, the source pixel will be less + * than or equal to 8 bits and will be in the PNG native (big endian) layout. + */ +#define png_ptr (tc->png_ptr) /* Used in all functions below */ +/* (1): single-byte cached transforms: */ +static void +do_transform_cache_byte(png_transformp *trIn, png_transform_controlp tc) +{ + png_transform_cache *tr = png_transform_cache_cast(*trIn, 256U); + + /* Copy the bytes through the 256-byte LUT: */ + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep ep = dp + PNG_TC_ROWBYTES(*tc); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + + tc->sp = dp; + + do + *dp++ = tr->cache.b8[*sp++]; + while (dp < ep); + + png_transform_control_cp(tc, &tr->tc); +} + +/* (2) A cached transform that increases pixel depth. + * + * There are six output depth possibilites, all a whole number of bytes: + * + * 1 byte, 8 bits: palette or grayscale + * 2 bytes, 16 bits: 16-bit grayscale or 8-bit gray+alpa + * 3 bytes, 24 bits: 8-bit RGB + * 4 bytes, 32 bits: 16-bit gray+alpha or 8-bit RGBA + * 6 bytes, 48 bits: 16-bit RGB + * 8 bytes, 64 bits: 16-bit RGBA + * + * The input must be 1, 2, 4 or 8-bit gray or palette. The first 1-byte case is + * handled for 8-bit gray/palette above, so there are 22 possibilities. The + * function names below are: + * + * do_transform_cache__ + */ +#define transform_cache_size(ipd,opd) ((((1U << (ipd)) * (opd))+7U) >> 3) +static void +do_transform_cache_(png_transformp *trIn, png_transform_controlp tc, + unsigned int ipd, unsigned int opd) + /* This is the implementation for unknown ipd, opd, below it is called with + * fixed values. The purpose of this is to allow the compiler/system builder + * to decide how to optimize for size vs space vs speed. Note that this + * implementation, while it would work for 8 bit ipd, is not used in that + * case. + */ +{ + png_transform_cache *tr = + png_transform_cache_cast(*trIn, transform_cache_size(ipd, opd)); + + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep ep = dp; + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + unsigned int s, shift, mask; + + sp += PNG_TC_ROWBYTES(*tc); /* One byte beyond the end */ + + png_transform_control_cp(tc, &tr->tc); + dp += PNG_TC_ROWBYTES(*tc); + + shift = 7U & -(tc->width * ipd); + /* MSB: shift right required to get last pixel */ + mask = (1U << ipd) - 1U; + /* Mask to extract a single pixel from the low bits of a byte */ + opd >>= 3; + /* Output pixel size in bytes */ + s = *--sp; + /* The first byte; the last byte of the input row */ + + for (;;) + { + png_const_bytep opixel = (((s >> shift) & mask)+1U) * opd + tr->cache.b8; + /* Points to the byte after last byte of the output value */ + unsigned int i; + + for (i=0; isp); + tc->sp = ep; /* start of row, safe even if the above fails */ +} + +#define do_transform_cache(ipd,opd)\ +static void \ +do_transform_cache_##ipd##_##opd(png_transformp *tr, png_transform_controlp tc)\ +{\ + do_transform_cache_(tr, tc, ipd, opd);\ +} + +#define TCLOW(opd)\ +do_transform_cache(1,opd)\ +do_transform_cache(2,opd)\ +do_transform_cache(4,opd) + +TCLOW(8) +TCLOW(16) +TCLOW(24) +TCLOW(32) +TCLOW(48) +TCLOW(64) + +#undef TCLOW +#undef do_transform_cache + +static void +do_transform_cache_8_(png_transformp *trIn, png_transform_controlp tc, + unsigned int opd) + /* This is the 8-bit input implementation. */ +{ + png_transform_cache *tr = + png_transform_cache_cast(*trIn, transform_cache_size(8, opd)); + + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep ep = dp; + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + + sp += PNG_TC_ROWBYTES(*tc); /* One byte beyond the end */ + + png_transform_control_cp(tc, &tr->tc); + dp += PNG_TC_ROWBYTES(*tc); + + opd >>= 3; /* Output pixel size in bytes */ + do + { + png_const_bytep opixel = (*--sp + 1U) * opd + tr->cache.b8; + /* Points to the byte after last byte of the output value */ + unsigned int i; + + for (i=0; i ep); + + debug(dp == ep && sp == tc->sp); + tc->sp = ep; /* start of row, safe even if the above fails */ +} + +#define do_transform_cache(opd)\ +static void \ +do_transform_cache_8_##opd(png_transformp *tr, png_transform_controlp tc)\ +{\ + do_transform_cache_8_(tr, tc, opd);\ +} + +/* The 8-bit to 8-bit case uses the byte transform code */ +do_transform_cache(16) +do_transform_cache(24) +do_transform_cache(32) +do_transform_cache(48) +do_transform_cache(64) + +#undef do_transform_cache + +#define do_transform_cache(ipd,opd) do_transform_cache_##ipd##_##opd + +#undef png_ptr + +typedef struct +{ + png_transformp *start; + /* This is a pointer to the pointer to the start of the list being cached, + * i.e. *start is the first transform in the list. + */ + png_transform_control tstart; + /* This is the transform control at the start; i.e. before (*start)->fn is + * called. Note that for palette data it will contain the original + * palette format/bit-depth, not that passed to (*start)->fn which will + * represent the palette. + */ + png_transformp *end; + png_transform_control tend; + /* The same data from the end of the run to be cached, i.e. after the + * function of the transform which *contains* '*end' (end points to + * tr->next). + */ +} png_cache_params, *png_cache_paramsp; + +static void +init_caching(png_structp png_ptr, png_transform_controlp tend) + /* Given an already initialized tend turn on caching if appropriate. */ +{ + /* Handle the colormap case, where a cache is always required: */ + if (tend->format & PNG_FORMAT_FLAG_COLORMAP) + { + /* This turns starts the palette caching with the next transform: */ + tend->palette = tend->caching = 1U; +# ifdef PNG_READ_tRNS_SUPPORTED + tend->transparent_alpha = png_ptr->transparent_palette; +# else /* !READ_tRNS */ + tend->transparent_alpha = 0; + PNG_UNUSED(png_ptr) +# endif /* !READ_tRNS */ + tend->format = PNG_FORMAT_FLAG_COLOR; +# ifdef PNG_READ_tRNS_SUPPORTED + if (png_ptr->num_trans > 0 && !(tend->invalid_info & PNG_INFO_tRNS)) + { + tend->format |= PNG_FORMAT_FLAG_ALPHA; + } +# endif /* READ_tRNS */ + tend->bit_depth = 8U; + } + + else if (PNG_TC_PIXEL_DEPTH(*tend) <= 8) + { + /* Cacheable pixel transforms; the pixel is less than 8 bits in size so + * the cache makes sense. + * + * TODO: check the cost estimate and the image size to avoid expensive + * caches of very small images. + */ + tend->caching = 1U; + } + + /* TODO: handle handle 8-bit GA/RGB/RGBA */ +} + +static void +add_cache_transform(png_structp png_ptr, unsigned int order, + png_transform_fn fn, png_cache_paramsp cp, + png_const_bytep cache, unsigned int size) + /* Add a transform from the input format cp->tstart to the output format + * stored in cp->tend. + */ +{ + affirm(size <= 2048U); /* 256 8-byte pixels at most */ + { + png_transform_cache *tr = png_transform_cache_cast( + png_add_transform(png_ptr, png_transform_cache_size(size), fn, order), + size); + + /* This must have replaced the transform in *cp->start: */ + affirm(&tr->tr == *cp->start); + + /* Fill in the respective members: */ + tr->tc = cp->tend; + memcpy(tr->cache.b8, cache, size); + + /* Skip this transform, because the calling routine has already executed + * the cache (it could be executed again, just to verify that it works; + * cp->tstart should be correct.) + */ + cp->start = &tr->tr.next; + } +} + +static unsigned int +setup_palette_cache(png_structp png_ptr, png_byte cache[8*256]) + /* This returns the number of entries in the cache; the width */ +{ + const unsigned int num_palette = png_ptr->num_palette; +# ifdef PNG_READ_tRNS_SUPPORTED + unsigned int num_trans = png_ptr->num_trans; +# endif /* READ_tRNS */ + const png_colorp palette = png_ptr->palette; + png_bytep p; + unsigned int i; +# ifdef PNG_READ_tRNS_SUPPORTED + const png_bytep trans_alpha = png_ptr->trans_alpha; +# endif /* READ_tRNS */ + + for (i=0, p=cache; i 0) + { + if (i < num_trans) + *p++ = trans_alpha[i]; + + else + *p++ = 0xFFU; + } +# endif /* READ_tRNS */ + } + + return num_palette; +} + +static void +png_remove_PLTE_and_tRNS(png_structrp png_ptr) +{ + if (png_ptr->palette != NULL) + png_free(png_ptr, png_ptr->palette); + + png_ptr->palette = NULL; + png_ptr->num_palette = 0; + +# ifdef PNG_READ_tRNS_SUPPORTED + if (png_ptr->trans_alpha != NULL) + png_free(png_ptr, png_ptr->trans_alpha); + + png_ptr->trans_alpha = NULL; + png_ptr->num_trans = 0; +# endif /* READ_tRNS */ + + png_ptr->palette_updated = 1U; +} + +static void +update_palette(png_structp png_ptr, png_cache_paramsp cp, + unsigned int max_depth) +{ + union + { + png_uint_32 u32[1]; + png_uint_16 u16[1]; /* For alignment */ + png_byte b8[8*256]; /* For 16-bit RGBA intermediate */ + } cache; + + /* The caller only calls this function if the initial transform control had + * the palette flag set, implying that the original 'format' was a COLORMAP + * one. Also this can only happen (at present) when starting the transform + * list, so: + */ + affirm((cp->tstart.format & PNG_FORMAT_FLAG_COLORMAP) != 0); /* required */ + + /* Run the whole of the given list on the palette data. PNG_TC_INIT_FINAL + * has already been run; this is a full run (with init == 0). + */ + { + unsigned int check_depth; + only_deb(png_transform_control orig = cp->tend;) + + cp->tend = cp->tstart; + init_caching(png_ptr, &cp->tend); + /* And set up tend to actually work out the palette: */ + cp->tend.init = 0U; + cp->tend.width = setup_palette_cache(png_ptr, cache.b8); + cp->tend.sp = cache.b8; + cp->tend.dp = cache.b8; + + check_depth = + png_run_this_transform_list_forwards(&cp->tend, cp->start, *cp->end); + + /* If we get here these two things must be true or there are been some + * buggy difference of opinion between the INIT code and the actual run: + */ + affirm(check_depth == max_depth && cp->tend.palette); + + /* This should match the passed in final format obtained before, this + * debug statement detects discrepancies between the init code and the + * run code: + */ + debug(png_transform_control_eq(&cp->tend, &orig)); + + /* Also, expect the palette to still be valid: */ + debug((cp->tend.invalid_info & PNG_INFO_PLTE) == 0); + } + + /* The result must be compatible with a PNG palette with respect to bit + * depth; specifically the expand-16 transform has no effect on palette data. + * + * The colormap setting must not have been re-introduced here either; there + * may be some quantize interactions here, neither can unexpected flags be + * handled; just COLOR and ALPHA. + */ + affirm(cp->tend.bit_depth == 8 && + (cp->tend.format & PNG_FORMAT_FLAG_COLORMAP) == 0); + + /* Remove all the transforms between start(inclusive) and end(exclusive); + * they have been processed. The effect they had on the transform control + * is irrelevant because the caller re-instates the settings from tstart. + */ + { + png_transformp list = *cp->start; /* list to free */ + + *cp->start = *cp->end; /* part of list not to be freed */ + *cp->end = NULL; /* terminate the list to be freed */ + cp->end = cp->start; /* else cp->end points to the end of the list! */ + + png_transform_free(png_ptr, &list); + } + + /* Adjust the PNG palette and, if required, the tRNS entries. Note that + * if the transforms stripped the alpha channel from the palette num_trans + * will get set to 0 here. + * + * This is the point where the gamma gets frozen too. The alternative + * design is to pass palette, tRNS and gamma up the transform chain, but + * that doesn't work because the palette change would, apparently, have to + * be repeated on each row. This seems simpler at the cost of a little + * obscurity; the answer to the question, "Where does the palette get + * updated?", is "Here!" + * + * API CHANGE: (fix): previously the init code would silently overwrite + * the palette information shared with png_info, breaking the API for + * png_read_update_info, which doesn't update the info if it isn't called, + * by changing the palette and maybe tRNS when the first row was read! + * + * NOTE: PNG_FORMAT_FLAG_RANGE is lost at this point, even if the palette + * entries were shifted or inverted. This could be fixed, but it would + * complicate the libpng API to expose the information. + */ + /* Write the transformed palette: */ + { + png_colorp palette = png_voidcast(png_colorp, png_calloc(png_ptr, + sizeof (png_color[PNG_MAX_PALETTE_LENGTH]))); + png_const_bytep p; + const int is_color = (cp->tend.format & PNG_FORMAT_FLAG_COLOR) != 0; + unsigned int i; +# ifdef PNG_READ_tRNS_SUPPORTED + unsigned int num_trans = 0; + const int do_trans = (cp->tend.format & PNG_FORMAT_FLAG_ALPHA) != 0; + png_byte trans_alpha[PNG_MAX_PALETTE_LENGTH]; +# endif /* READ_tRNS */ + + memset(palette, 0xFFU, sizeof (png_color[PNG_MAX_PALETTE_LENGTH])); + png_free(png_ptr, png_ptr->palette); + png_ptr->palette = palette; + + for (i=0, p=cache.b8; itend.width; ++i) + { + if (is_color) + { + palette[i].red = *p++; + palette[i].green = *p++; + palette[i].blue = *p++; + } + + else + palette[i].blue = palette[i].green = palette[i].red = *p++; + +# ifdef PNG_READ_tRNS_SUPPORTED + if (do_trans) + { + png_byte a = *p++; + trans_alpha[i] = a; + + /* Strip opaque entries from the end: */ + if (a < 0xFFU) + num_trans = i+1; + } +# endif /* READ_tRNS */ + } + + png_ptr->num_palette = png_check_bits(png_ptr, cp->tend.width, 9); + +# ifdef PNG_READ_tRNS_SUPPORTED + if (num_trans > 0) + { + png_bytep tRNS = png_voidcast(png_bytep, png_malloc(png_ptr, + PNG_MAX_PALETTE_LENGTH)); + + memset(tRNS, 0xFFU, PNG_MAX_PALETTE_LENGTH); + + if (png_ptr->trans_alpha != NULL) + png_free(png_ptr, png_ptr->trans_alpha); + + png_ptr->trans_alpha = tRNS; + + memcpy(tRNS, trans_alpha, num_trans); + png_ptr->num_trans = png_check_bits(png_ptr, num_trans, 9); + } +# endif /* READ_tRNS */ + } + + /* NOTE: the caller sets cp->start to cp->end and cp->tend to cp->tstart, + * this causes processing to continue with the palette format and the + * first unprocessed transform. The reset of the transform control loses the + * gamma information as well, of course, as any information about the palette + * and tRNS changes (such as the RANGE flags). + * + * The following ensures that png_read_update_info knows to update the + * palette in png_info (which is no longer shared). + */ + png_ptr->palette_updated = 1U; +} + +/* These structure and the save/restore routines that follow it exist to save + * data from a png_transform_control that is specific to the sample encoding of + * the PNG data, rather than the row format itself. + */ +typedef struct +{ +# ifdef PNG_READ_GAMMA_SUPPORTED + png_fixed_point gamma; +# endif + png_byte sBIT_R; + png_byte sBIT_G; + png_byte sBIT_B; + png_byte sBIT_A; /* Signnificant bits in the row channels. */ + unsigned int invalid_info; /* PNG_INFO_* for invalidated chunks */ +} png_tc_channel_data; + +static void +save_cp_channel_data(png_tc_channel_data *save, png_const_transform_controlp tc) +{ +# ifdef PNG_READ_GAMMA_SUPPORTED + save->gamma = tc->gamma; +# endif /* READ_GAMMA */ + + /* The sBIT information and the list of invalidated chunks must also be + * preserved: + */ + save->sBIT_R = tc->sBIT_R; + save->sBIT_G = tc->sBIT_G; + save->sBIT_B = tc->sBIT_B; + save->sBIT_A = tc->sBIT_A; + save->invalid_info = tc->invalid_info; +} + +static void +restore_cp_channel_data(png_transform_controlp tc, + const png_tc_channel_data *save) + /* Reverse the above */ +{ +# ifdef PNG_READ_GAMMA_SUPPORTED + tc->gamma = save->gamma; +# endif /* READ_GAMMA */ + + tc->sBIT_R = save->sBIT_R; + tc->sBIT_G = save->sBIT_G; + tc->sBIT_B = save->sBIT_B; + tc->sBIT_A = save->sBIT_A; + tc->invalid_info = save->invalid_info; +} + +static void +make_cache(png_structp png_ptr, png_cache_paramsp cp, unsigned int max_depth) +{ + /* At present the cache is just a byte lookup table. We need the original + * pixel depth to work out how big the working buffer needs to be. + */ + const unsigned int ipd = PNG_TC_PIXEL_DEPTH(cp->tstart); + const unsigned int opd = PNG_TC_PIXEL_DEPTH(cp->tend); + unsigned int order; /* records position of start transform */ + unsigned int width; /* width of cache in pixels */ + png_tc_channel_data save; /* Record of the final channel info */ + union + { + png_uint_32 u32[1]; + png_uint_16 u16[1]; /* For alignment */ + png_byte b8[8*256]; /* For 16-bit RGBA */ + } cache; + + debug(cp->tend.init == PNG_TC_INIT_FINAL); + affirm(opd <= 64 && max_depth <= 64); /* or the cache is not big enough */ + affirm(ipd == opd || (opd & 0x7U) == 0); + + if ((cp->tstart.format & PNG_FORMAT_FLAG_COLORMAP) != 0) + width = setup_palette_cache(png_ptr, cache.b8); + + else switch (ipd) + { + /* The input to the cache is the full range of possible pixel values: */ + case 1: + /* 2 1-bit pixels, MSB first */ + cache.b8[0] = 0x40U; + width = 2; + break; + + case 2: + /* 4 2-bit pixels, MSB first */ + cache.b8[0] = 0x1BU; + width = 4; + break; + + case 4: + /* 16 4-bit pixels, MSB first */ + cache.b8[0] = 0x01U; + cache.b8[1] = 0x23U; + cache.b8[2] = 0x45U; + cache.b8[3] = 0x67U; + cache.b8[4] = 0x89U; + cache.b8[5] = 0xABU; + cache.b8[6] = 0xCDU; + cache.b8[7] = 0xEFU; + width = 16; + break; + + case 8: + /* 256 8-bit pixels */ + { + unsigned int i; + + for (i=0; i<256; ++i) + cache.b8[i] = PNG_BYTE(i); + } + width = 256; + break; + + default: + impossible("cache input bit depth"); + } + + /* Reset the transform control to run the transforms on this data, but save + * the channel info because the row processing functions do not always + * write it. + */ + save_cp_channel_data(&save, &cp->tend); + cp->tend = cp->tstart; + init_caching(png_ptr, &cp->tend); + /* And set tend to work out the result of transforming each possible pixel + * value: + */ + cp->tend.init = 0U; + cp->tend.width = width; + cp->tend.sp = cache.b8; + cp->tend.dp = cache.b8; + + { + unsigned int check_depth = + png_run_this_transform_list_forwards(&cp->tend, cp->start, *cp->end); + + /* This must not change: */ + affirm(PNG_TC_PIXEL_DEPTH(cp->tend) == opd && check_depth == max_depth); + } + + /* Restore the potentially lost channel data. */ + restore_cp_channel_data(&cp->tend, &save); + + /* This is all the information required to cache the set of transforms + * between 'start' and 'end'. We take the transformed pixels and make a + * cache transform of them. The cache transform skips the work, transforms + * the row, and sets the tranform_control to (a copy of) cp->tend. + * + * Remove all the transforms between start(inclusive) and end(exclusive); + * they have been processed. The effect they had on the transform control + * is irrelevant because the caller re-instates the settings from tstart. + */ + { + png_transformp list = *cp->start; /* list to free */ + + *cp->start = *cp->end; /* part of list not to be freed */ + *cp->end = NULL; /* terminate the list to be freed */ + cp->end = NULL; /* reset below */ + + order = list->order; /* used below when adding the cache transform */ + png_transform_free(png_ptr, &list); + } + + /* Make the required cache, as enumerated above there are 22 possibilities, + * this selects between them, fixes up the cache for the 'byte' cases (where + * multiple pixels can be handled byte-by-byte) and selects the correct + * transform function. + */ + if (ipd == opd) + { + /* We already know that ipd is <= 8 bits, so we can expand this case to + * the byte transform. The complexity is that for ipd < 8 bits we only + * have information for individual pixel values and these may be + * pixel-swapped within the byte. + */ + if (ipd < 8) + { + const int lsb = (cp->tend.format & PNG_FORMAT_FLAG_SWAPPED) != 0; + unsigned int ishift, b; + png_byte bcache[256]; + + switch (ipd) + { + case 1: ishift = 3U; break; + case 2: ishift = 2U; break; + case 4: ishift = 1U; break; + default: impossible("ipd"); + } + + /* Work out the right answer for each byte of pixels: */ + for (b=0U; b<256U; ++b) + { + unsigned int o = 0U; /* output byte */ + unsigned int p = 8U; /* right shift to find input pixel */ + + do + { + unsigned int q = ((1U<> (p-=ipd)); + /* The input pixel. For a palette this value might be outside + * the range of palette indices, in which case simply insert + * '0': + */ + if (q < width) { - if (png_ptr->trans_alpha[i] == 0) - { - palette[i] = back; - } - else /* if (png_ptr->trans_alpha[i] != 0xff) */ - { - png_uint_16 v, w; - unsigned int alpha = png_ptr->trans_alpha[i] * 257U; - unsigned int shift = png_ptr->gamma_shift; - unsigned int add = (shift > 0 ? 1U<<(shift-1) : 0); - - v = png_ptr->gamma_to_1[palette[i].red]; - png_composite_16(w, v, alpha, back_1.red); - palette[i].red = png_ptr->gamma_from_1[(w+add)>>shift]; - - v = png_ptr->gamma_to_1[palette[i].green]; - png_composite_16(w, v, alpha, back_1.green); - palette[i].green = png_ptr->gamma_from_1[(w+add)>>shift]; - - v = png_ptr->gamma_to_1[palette[i].blue]; - png_composite_16(w, v, alpha, back_1.blue); - palette[i].blue = png_ptr->gamma_from_1[(w+add)>>shift]; - } + unsigned int r = cache.b8[q >> ishift]; + r >>= ((lsb ? q : ~q) & ((1U<gamma_table[palette[i].red]; - palette[i].green = png_ptr->gamma_table[palette[i].green]; - palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + UNTESTED } } + while (p != 0U); - /* Prevent the transformations being done again. - * - * NOTE: this is highly dubious; it removes the transformations in - * place. This seems inconsistent with the general treatment of the - * transformations elsewhere. - */ - png_ptr->transformations &= ~(PNG_COMPOSE | PNG_GAMMA); - } /* color_type == PNG_COLOR_TYPE_PALETTE */ - }/* png_ptr->transformations & PNG_BACKGROUND */ + bcache[b] = png_check_byte(png_ptr, o); + } + + /* This is a byte transform, with the optional check-for-invalid-index + * functionality. + */ + add_cache_transform(png_ptr, order, do_transform_cache_byte, cp, + bcache, 256U); + } + + else /* ipd == 8 */ + add_cache_transform(png_ptr, order, do_transform_cache_byte, cp, + cache.b8, 256U); + } + + else + { + /* opd is a whole number of bytes, ipd is 1, 2, 4 or 8 and not equal to + * opd. + */ + png_transform_fn fn; + +# define C(ipd,opd) ((ipd) + 8*(opd)) + switch (C(ipd,opd)) + { +# define CASE(ipd,opd)\ + case C(ipd,opd): fn = do_transform_cache(ipd,opd); break + + CASE(1,8); + CASE(2,8); + CASE(4,8); + /* No 8,8 */ + +# define CASES(opd)\ + CASE(1,opd);\ + CASE(2,opd);\ + CASE(4,opd);\ + CASE(8,opd) + + CASES(16); + CASES(24); + CASES(32); + CASES(48); + CASES(64); +# undef CASES +# undef CASE + + default: + impossible("cache bit depths"); + } +# undef C + + /* In the event that the cache is not the full width implied by ipd zero + * the remaining bytes for security; otherwise they get copied into the + * cache transform and might get used. (Specifically if there is an + * out-of-range palette index they do get used!) + */ + { + unsigned int size = transform_cache_size(ipd, opd); + png_alloc_size_t cachebytes = PNG_TC_ROWBYTES(cp->tend); + + affirm(cachebytes <= sizeof cache.b8); + + if (cachebytes < size) + memset(cache.b8+cachebytes, 0, size - cachebytes); + + add_cache_transform(png_ptr, order, fn, cp, cache.b8, size); + } + } + + /* Because a transform was inserted cp->end needs to be set to the new + * pointer to the original end. add_cache_transform sets cp->start to this, + * so: + */ + cp->end = cp->start; + + /* This invalidates the palette if that is what was cached because the + * palette and, if present, tRNS chunk did not get updated above. + */ + if (cp->tstart.palette) + png_remove_PLTE_and_tRNS(png_ptr); +} + +static void restore_cp(png_cache_paramsp cp) +{ + /* A utility to restore cp->tstart by copying it into cp->tend. This is used + * both in the palette case when restoring the transform control for the + * indexed data and in the case where no transforms were cached. It + * preserves the color-channel-specific data from cp->tend because in either + * case it is possible for this data to be modified without preserving any + * transforms, e.g. if only the gamma is changed but no gamma transform is + * retained because the change was not significant. + */ + png_tc_channel_data save; + + save_cp_channel_data(&save, &cp->tend); + cp->tend = cp->tstart; + restore_cp_channel_data(&cp->tend, &save); +} + +static void +handle_cache(png_structp png_ptr, png_cache_paramsp cp, unsigned int max_depth) +{ + /* There is nothing to do if there are no transforms between 'start' and + * 'end': + */ + if (cp->start != cp->end) + { + only_deb(png_transformp tr_check = *cp->end;) + + /* libpng doesn't currently implement any pixel size of more than 64 bits + * so: + */ + affirm(max_depth <= 64); + + if (cp->tend.palette) + { + /* The transforms being cached apply to the palette, the following + * transforms will apply to the original index data and the transformed + * data must be used to update the palette: + */ + if (cp->tend.init == PNG_TC_INIT_FINAL) + update_palette(png_ptr, cp, max_depth); + + cp->start = cp->end; + restore_cp(cp); /* reset to palette data */ + } else - /* Transformation does not include PNG_BACKGROUND */ -#endif /* PNG_READ_BACKGROUND_SUPPORTED */ - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE -#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED - /* RGB_TO_GRAY needs to have non-gamma-corrected values! */ - && ((png_ptr->transformations & PNG_EXPAND) == 0 || - (png_ptr->transformations & PNG_RGB_TO_GRAY) == 0) -#endif - ) { - png_colorp palette = png_ptr->palette; - int num_palette = png_ptr->num_palette; - int i; - - /* NOTE: there are other transformations that should probably be in - * here too. + /* Continue with the transform control in cp.tend; even if there was + * palette data in cp.tstart it has been expanded. */ - for (i = 0; i < num_palette; i++) - { - palette[i].red = png_ptr->gamma_table[palette[i].red]; - palette[i].green = png_ptr->gamma_table[palette[i].green]; - palette[i].blue = png_ptr->gamma_table[palette[i].blue]; - } + if (cp->tend.init == PNG_TC_INIT_FINAL) + make_cache(png_ptr, cp, max_depth); - /* Done the gamma correction. */ - png_ptr->transformations &= ~PNG_GAMMA; - } /* color_type == PALETTE && !PNG_BACKGROUND transformation */ + cp->tstart = cp->tend; /* keep current context */ + } + + debug(tr_check == *cp->end); } -#ifdef PNG_READ_BACKGROUND_SUPPORTED - else -#endif -#endif /* PNG_READ_GAMMA_SUPPORTED */ -#ifdef PNG_READ_BACKGROUND_SUPPORTED - /* No GAMMA transformation (see the hanging else 4 lines above) */ - if ((png_ptr->transformations & PNG_COMPOSE) && - (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)) + else /* no transforms cached */ + restore_cp(cp); /* removes any palette caching info */ +} + +#ifdef PNG_READ_tRNS_SUPPORTED +static void +check_tRNS_for_alpha(png_structrp png_ptr) +{ + unsigned int num_trans = png_ptr->num_trans; + + debug(png_ptr->color_type == PNG_COLOR_TYPE_PALETTE); + + while (num_trans > 0) { - int i; - int istop = (int)png_ptr->num_trans; - png_color back; - png_colorp palette = png_ptr->palette; - - back.red = (png_byte)png_ptr->background.red; - back.green = (png_byte)png_ptr->background.green; - back.blue = (png_byte)png_ptr->background.blue; - - for (i = 0; i < istop; i++) { - if (png_ptr->trans_alpha[i] == 0) + const png_byte trans = png_ptr->trans_alpha[--num_trans]; + + if (trans == 0xFFU) + continue; + + if (trans > 0U) + return; /* Palette has at least one entry >0, <0xff */ + } + + /* There is some point to the tRNS chunk; it has a non-opaque entry, this + * code could truncate it but there is no obvious performance advantage to + * doing this. + */ + while (num_trans > 0) + { + const png_byte trans = png_ptr->trans_alpha[--num_trans]; + + if (trans > 0U && trans < 0xFFU) + return; + } + + /* Here if the above did not find an entry >0 && <0xFFU but did find a + * transparent entry (0u). Record this. + */ + png_ptr->transparent_palette = 1U; + return; + } + + /* All entries opaque; remove the tRNS data: */ + png_ptr->num_trans = 0U; +} +#endif /* READ_tRNS */ + +unsigned int /* PRIVATE */ +png_read_init_transform_mech(png_structp png_ptr, png_transform_controlp tc) + /* This is called once for each init stage (PNG_TC_INIT_FORMAT and + * PNG_TC_INIT_FINAL) to run the transform list forwards, returning the + * maximum depth required to process the row. It handles caching of the + * transforms and the processing of the palette for color-mapped PNG data. + */ +{ + png_transformp *list = &png_ptr->transform_list; + unsigned int max_depth, cache_start_depth; + png_cache_params cp; + + /* PNG color-mapped data must be handled here so that the palette is updated + * correctly. png_set_palette_to_rgb causes the palette flag to be removed + * from the transform control but does no other change. png_set_quantize + * causes 8-bit RGB, RGBA or palette data to be converted into palette + * indices, setting the palette flag. + */ +# ifdef PNG_READ_tRNS_SUPPORTED + /* This happens once at the start to find out if the tRNS chunk consisted + * entirely of opaque (255) and/or transparent (0) entries. + */ + if (tc->init == PNG_TC_INIT_FORMAT && + png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + check_tRNS_for_alpha(png_ptr); +# endif /* READ_tRNS */ + cp.end = cp.start = list; + cp.tend = cp.tstart = *tc; + max_depth = cache_start_depth = PNG_TC_PIXEL_DEPTH(cp.tend); + + while (*cp.end != NULL) + { + png_transformp tr = *cp.end; + + /* The user transform cannot be cached. */ + if (tr->order >= PNG_TR_USER) + break; + + /* If caching is not on and this transform is after PNG_TR_START_CACHE + * try to turn it on. + */ + if (tr->order > PNG_TR_START_CACHE && !cp.tend.caching) + { + cp.start = cp.end; + cp.tstart = cp.tend; + init_caching(png_ptr, &cp.tend); + + if (cp.tend.caching) { - palette[i] = back; - } - - else if (png_ptr->trans_alpha[i] != 0xff) - { - /* The png_composite() macro is defined in png.h */ - png_composite(palette[i].red, palette[i].red, - png_ptr->trans_alpha[i], back.red); - - png_composite(palette[i].green, palette[i].green, - png_ptr->trans_alpha[i], back.green); - - png_composite(palette[i].blue, palette[i].blue, - png_ptr->trans_alpha[i], back.blue); + cache_start_depth = max_depth; + max_depth = PNG_TC_PIXEL_DEPTH(cp.tend); } } - png_ptr->transformations &= ~PNG_COMPOSE; - } -#endif /* PNG_READ_BACKGROUND_SUPPORTED */ - -#ifdef PNG_READ_SHIFT_SUPPORTED - if ((png_ptr->transformations & PNG_SHIFT) && - !(png_ptr->transformations & PNG_EXPAND) && - (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)) - { - int i; - int istop = png_ptr->num_palette; - int shift = 8 - png_ptr->sig_bit.red; - - png_ptr->transformations &= ~PNG_SHIFT; - - /* significant bits can be in the range 1 to 7 for a meaninful result, if - * the number of significant bits is 0 then no shift is done (this is an - * error condition which is silently ignored.) + /* If the 'palette' flag is set and the next transform has order + * PNG_TR_ENCODING or later cache the results so far and continue with the + * original palette data (cp.tstart). */ - if (shift > 0 && shift < 8) - for (i=0; ipalette[i].red; + if (cp.tend.palette && tr->order >= PNG_TR_ENCODING) + { + handle_cache(png_ptr, &cp, max_depth); - component >>= shift; - png_ptr->palette[i].red = (png_byte)component; - } + /* The cache handling function must maintain cp.end; */ + affirm(tr == *cp.end); + max_depth = PNG_TC_PIXEL_DEPTH(cp.tend); + if (max_depth < cache_start_depth) + max_depth = cache_start_depth; + } - shift = 8 - png_ptr->sig_bit.green; - if (shift > 0 && shift < 8) - for (i=0; ipalette[i].green; + /* Now run the transform list entry: */ + if (tr->fn != NULL) + { + tr->fn(cp.end, &cp.tend); + tr = *cp.end; /* in case something was inserted */ + } - component >>= shift; - png_ptr->palette[i].green = (png_byte)component; - } + if (tr->fn == NULL) /* delete this transform */ + png_remove_transform(png_ptr, cp.end); - shift = 8 - png_ptr->sig_bit.blue; - if (shift > 0 && shift < 8) - for (i=0; ipalette[i].blue; + else + { + /* Handle the initialization of the maximum pixel depth. */ + unsigned int tc_depth = PNG_TC_PIXEL_DEPTH(cp.tend); - component >>= shift; - png_ptr->palette[i].blue = (png_byte)component; - } + if (tc_depth > max_depth) + max_depth = tc_depth; + + /* Advance to the next transform. */ + cp.end = &tr->next; + } } -#endif /* PNG_READ_SHIFT_SUPPORTED */ + + /* At the end if still caching record the cache information (this is common; + * this is generally the case for an expanded palette.) + */ + if (cp.tend.caching) + { + png_transformp tr = *cp.end; + handle_cache(png_ptr, &cp, max_depth); + affirm(tr == *cp.end); + max_depth = PNG_TC_PIXEL_DEPTH(cp.tend); + if (max_depth < cache_start_depth) + max_depth = cache_start_depth; + } + + /* At the end run the init on the user transform: */ + if (*cp.end != NULL) + { + png_transformp tr = *cp.end; + affirm(tr->order == PNG_TR_USER); + if (tr->fn != NULL) + tr->fn(cp.end, &cp.tend); + /* This cannot insert anything, so: */ + affirm(tr == *cp.end && tr->next == NULL); + + if (tr->fn == NULL) /* delete this transform */ + png_remove_transform(png_ptr, cp.end); + + else + { + unsigned int tc_depth = PNG_TC_PIXEL_DEPTH(cp.tend); + + if (tc_depth > max_depth) + max_depth = tc_depth; + } + } + + /* And write the input transform control: */ + *tc = cp.tend; + + return max_depth; } /* Modify the info structure to reflect the transformations. The @@ -1822,3064 +6749,73 @@ png_read_transform_info(png_structrp png_ptr, png_inforp info_ptr) { png_debug(1, "in png_read_transform_info"); -#ifdef PNG_READ_EXPAND_SUPPORTED - if (png_ptr->transformations & PNG_EXPAND) - { - if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) - { - /* This check must match what actually happens in - * png_do_expand_palette; if it ever checks the tRNS chunk to see if - * it is all opaque we must do the same (at present it does not.) + /* WARNING: this is very basic at present. It just updates the format + * information. It should update the palette (and will eventually) as well + * as invalidating chunks that the transforms break. + */ +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + info_ptr->format = png_ptr->row_format; + info_ptr->bit_depth = png_ptr->row_bit_depth; +# ifdef PNG_READ_GAMMA_SUPPORTED + /* If an info struct is used with a different png_ptr in a call to + * png_set_gAMA then the png_struct information won't be updated, this + * doesn't matter on write, but don't zap the value in the info on read + * unless it is known: + * + * TODO: review this whole mess. */ - if (png_ptr->num_trans > 0) - info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA; - - else - info_ptr->color_type = PNG_COLOR_TYPE_RGB; - - info_ptr->bit_depth = 8; - info_ptr->num_trans = 0; - - if (png_ptr->palette == NULL) - png_error (png_ptr, "Palette is NULL in indexed image"); - } - else - { - if (png_ptr->num_trans) - { - if (png_ptr->transformations & PNG_EXPAND_tRNS) - info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; - } - if (info_ptr->bit_depth < 8) - info_ptr->bit_depth = 8; - - info_ptr->num_trans = 0; - } - } -#endif - -#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) - /* The following is almost certainly wrong unless the background value is in - * the screen space! - */ - if (png_ptr->transformations & PNG_COMPOSE) - info_ptr->background = png_ptr->background; -#endif - -#ifdef PNG_READ_GAMMA_SUPPORTED - /* The following used to be conditional on PNG_GAMMA (prior to 1.5.4), - * however it seems that the code in png_init_read_transformations, which has - * been called before this from png_read_update_info->png_read_start_row - * sometimes does the gamma transform and cancels the flag. - * - * TODO: this looks wrong; the info_ptr should end up with a gamma equal to - * the screen_gamma value. The following probably results in weirdness if - * the info_ptr is used by the app after the rows have been read. - */ - info_ptr->colorspace.gamma = png_ptr->colorspace.gamma; -#endif - - if (info_ptr->bit_depth == 16) - { -# ifdef PNG_READ_16BIT_SUPPORTED -# ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED - if (png_ptr->transformations & PNG_SCALE_16_TO_8) - info_ptr->bit_depth = 8; + if (png_ptr->row_gamma > 0) + info_ptr->colorspace.gamma = png_ptr->row_gamma; # endif -# ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED - if (png_ptr->transformations & PNG_16_TO_8) - info_ptr->bit_depth = 8; -# endif + /* Invalidate chunks marked as invalid: */ +# ifdef PNG_READ_TRANSFORMS_SUPPORTED + info_ptr->valid &= ~png_ptr->invalid_info; -# else - /* No 16 bit support: force chopping 16-bit input down to 8, in this case - * the app program can chose if both APIs are available by setting the - * correct scaling to use. - */ -# ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED - /* For compatibility with previous versions use the strip method by - * default. This code works because if PNG_SCALE_16_TO_8 is already - * set the code below will do that in preference to the chop. + /* If the palette or tRNS chunk was changed copy them over to the info + * structure; this may actually re-validate the PLTE or tRNS chunks, + * but only if png_ptr has a new version, otherwise the invalid_info + * settings from above can still invalidate the chunk. */ - png_ptr->transformations |= PNG_16_TO_8; - info_ptr->bit_depth = 8; -# else - -# ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED - png_ptr->transformations |= PNG_SCALE_16_TO_8; - info_ptr->bit_depth = 8; -# else - - CONFIGURATION ERROR: you must enable at least one 16 to 8 method -# endif -# endif -#endif /* !READ_16BIT_SUPPORTED */ - } - -#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED - if (png_ptr->transformations & PNG_GRAY_TO_RGB) - info_ptr->color_type = (png_byte)(info_ptr->color_type | - PNG_COLOR_MASK_COLOR); -#endif - -#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED - if (png_ptr->transformations & PNG_RGB_TO_GRAY) - info_ptr->color_type = (png_byte)(info_ptr->color_type & - ~PNG_COLOR_MASK_COLOR); -#endif - -#ifdef PNG_READ_QUANTIZE_SUPPORTED - if (png_ptr->transformations & PNG_QUANTIZE) - { - if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || - (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) && - png_ptr->palette_lookup && info_ptr->bit_depth == 8) - { - info_ptr->color_type = PNG_COLOR_TYPE_PALETTE; - } - } -#endif - -#ifdef PNG_READ_EXPAND_16_SUPPORTED - if (png_ptr->transformations & PNG_EXPAND_16 && info_ptr->bit_depth == 8 && - info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) - { - info_ptr->bit_depth = 16; - } -#endif - -#ifdef PNG_READ_PACK_SUPPORTED - if ((png_ptr->transformations & PNG_PACK) && (info_ptr->bit_depth < 8)) - info_ptr->bit_depth = 8; -#endif - - if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) - info_ptr->channels = 1; - - else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) - info_ptr->channels = 3; - - else - info_ptr->channels = 1; - -#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED - if (png_ptr->transformations & PNG_STRIP_ALPHA) - { - info_ptr->color_type = (png_byte)(info_ptr->color_type & - ~PNG_COLOR_MASK_ALPHA); - info_ptr->num_trans = 0; - } -#endif - - if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) - info_ptr->channels++; - -#ifdef PNG_READ_FILLER_SUPPORTED - /* STRIP_ALPHA and FILLER allowed: MASK_ALPHA bit stripped above */ - if ((png_ptr->transformations & PNG_FILLER) && - ((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || - (info_ptr->color_type == PNG_COLOR_TYPE_GRAY))) - { - info_ptr->channels++; - /* If adding a true alpha channel not just filler */ - if (png_ptr->transformations & PNG_ADD_ALPHA) - info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; - } -#endif - -#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) && \ -defined(PNG_READ_USER_TRANSFORM_SUPPORTED) - if (png_ptr->transformations & PNG_USER_TRANSFORM) - { - if (info_ptr->bit_depth < png_ptr->user_transform_depth) - info_ptr->bit_depth = png_ptr->user_transform_depth; - - if (info_ptr->channels < png_ptr->user_transform_channels) - info_ptr->channels = png_ptr->user_transform_channels; - } -#endif - - info_ptr->pixel_depth = (png_byte)(info_ptr->channels * - info_ptr->bit_depth); - - info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, info_ptr->width); - - /* Adding in 1.5.4: cache the above value in png_struct so that we can later - * check in png_rowbytes that the user buffer won't get overwritten. Note - * that the field is not always set - if png_read_update_info isn't called - * the application has to either not do any transforms or get the calculation - * right itself. - */ - png_ptr->info_rowbytes = info_ptr->rowbytes; - -#ifndef PNG_READ_EXPAND_SUPPORTED - if (png_ptr != NULL) - return; -#endif -} - -#ifdef PNG_READ_PACK_SUPPORTED -/* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel, - * without changing the actual values. Thus, if you had a row with - * a bit depth of 1, you would end up with bytes that only contained - * the numbers 0 or 1. If you would rather they contain 0 and 255, use - * png_do_shift() after this. - */ -static void -png_do_unpack(png_row_infop row_info, png_bytep row) -{ - png_debug(1, "in png_do_unpack"); - - if (row_info->bit_depth < 8) - { - png_uint_32 i; - png_uint_32 row_width=row_info->width; - - switch (row_info->bit_depth) - { - case 1: + if (png_ptr->palette_updated) { - png_bytep sp = row + (png_size_t)((row_width - 1) >> 3); - png_bytep dp = row + (png_size_t)row_width - 1; - png_uint_32 shift = 7 - (int)((row_width + 7) & 0x07); - for (i = 0; i < row_width; i++) - { - *dp = (png_byte)((*sp >> shift) & 0x01); - - if (shift == 7) - { - shift = 0; - sp--; - } - - else - shift++; - - dp--; - } - break; - } - - case 2: - { - - png_bytep sp = row + (png_size_t)((row_width - 1) >> 2); - png_bytep dp = row + (png_size_t)row_width - 1; - png_uint_32 shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); - for (i = 0; i < row_width; i++) - { - *dp = (png_byte)((*sp >> shift) & 0x03); - - if (shift == 6) - { - shift = 0; - sp--; - } - - else - shift += 2; - - dp--; - } - break; - } - - case 4: - { - png_bytep sp = row + (png_size_t)((row_width - 1) >> 1); - png_bytep dp = row + (png_size_t)row_width - 1; - png_uint_32 shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); - for (i = 0; i < row_width; i++) - { - *dp = (png_byte)((*sp >> shift) & 0x0f); - - if (shift == 4) - { - shift = 0; - sp--; - } - - else - shift = 4; - - dp--; - } - break; - } - - default: - break; - } - row_info->bit_depth = 8; - row_info->pixel_depth = (png_byte)(8 * row_info->channels); - row_info->rowbytes = row_width * row_info->channels; - } -} -#endif - -#ifdef PNG_READ_SHIFT_SUPPORTED -/* Reverse the effects of png_do_shift. This routine merely shifts the - * pixels back to their significant bits values. Thus, if you have - * a row of bit depth 8, but only 5 are significant, this will shift - * the values back to 0 through 31. - */ -static void -png_do_unshift(png_row_infop row_info, png_bytep row, - png_const_color_8p sig_bits) -{ - int color_type; - - png_debug(1, "in png_do_unshift"); - - /* The palette case has already been handled in the _init routine. */ - color_type = row_info->color_type; - - if (color_type != PNG_COLOR_TYPE_PALETTE) - { - int shift[4]; - int channels = 0; - int bit_depth = row_info->bit_depth; - - if (color_type & PNG_COLOR_MASK_COLOR) - { - shift[channels++] = bit_depth - sig_bits->red; - shift[channels++] = bit_depth - sig_bits->green; - shift[channels++] = bit_depth - sig_bits->blue; - } - - else - { - shift[channels++] = bit_depth - sig_bits->gray; - } - - if (color_type & PNG_COLOR_MASK_ALPHA) - { - shift[channels++] = bit_depth - sig_bits->alpha; - } - - { - int c, have_shift; - - for (c = have_shift = 0; c < channels; ++c) - { - /* A shift of more than the bit depth is an error condition but it - * gets ignored here. - */ - if (shift[c] <= 0 || shift[c] >= bit_depth) - shift[c] = 0; + if (png_ptr->num_palette > 0) + png_set_PLTE(png_ptr, info_ptr, png_ptr->palette, + png_ptr->num_palette); else - have_shift = 1; - } - - if (have_shift == 0) - return; - } - - switch (bit_depth) - { - default: - /* Must be 1bpp gray: should not be here! */ - /* NOTREACHED */ - break; - - case 2: - /* Must be 2bpp gray */ - /* assert(channels == 1 && shift[0] == 1) */ - { - png_bytep bp = row; - png_bytep bp_end = bp + row_info->rowbytes; - - while (bp < bp_end) { - int b = (*bp >> 1) & 0x55; - *bp++ = (png_byte)b; - } - break; - } - - case 4: - /* Must be 4bpp gray */ - /* assert(channels == 1) */ - { - png_bytep bp = row; - png_bytep bp_end = bp + row_info->rowbytes; - int gray_shift = shift[0]; - int mask = 0xf >> gray_shift; - - mask |= mask << 4; - - while (bp < bp_end) - { - int b = (*bp >> gray_shift) & mask; - *bp++ = (png_byte)b; - } - break; - } - - case 8: - /* Single byte components, G, GA, RGB, RGBA */ - { - png_bytep bp = row; - png_bytep bp_end = bp + row_info->rowbytes; - int channel = 0; - - while (bp < bp_end) - { - int b = *bp >> shift[channel]; - if (++channel >= channels) - channel = 0; - *bp++ = (png_byte)b; - } - break; - } - -#ifdef PNG_READ_16BIT_SUPPORTED - case 16: - /* Double byte components, G, GA, RGB, RGBA */ - { - png_bytep bp = row; - png_bytep bp_end = bp + row_info->rowbytes; - int channel = 0; - - while (bp < bp_end) - { - int value = (bp[0] << 8) + bp[1]; - - value >>= shift[channel]; - if (++channel >= channels) - channel = 0; - *bp++ = (png_byte)(value >> 8); - *bp++ = (png_byte)(value & 0xff); - } - break; - } -#endif - } - } -} -#endif - -#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED -/* Scale rows of bit depth 16 down to 8 accurately */ -static void -png_do_scale_16_to_8(png_row_infop row_info, png_bytep row) -{ - png_debug(1, "in png_do_scale_16_to_8"); - - if (row_info->bit_depth == 16) - { - png_bytep sp = row; /* source */ - png_bytep dp = row; /* destination */ - png_bytep ep = sp + row_info->rowbytes; /* end+1 */ - - while (sp < ep) - { - /* The input is an array of 16 bit components, these must be scaled to - * 8 bits each. For a 16 bit value V the required value (from the PNG - * specification) is: - * - * (V * 255) / 65535 - * - * This reduces to round(V / 257), or floor((V + 128.5)/257) - * - * Represent V as the two byte value vhi.vlo. Make a guess that the - * result is the top byte of V, vhi, then the correction to this value - * is: - * - * error = floor(((V-vhi.vhi) + 128.5) / 257) - * = floor(((vlo-vhi) + 128.5) / 257) - * - * This can be approximated using integer arithmetic (and a signed - * shift): - * - * error = (vlo-vhi+128) >> 8; - * - * The approximate differs from the exact answer only when (vlo-vhi) is - * 128; it then gives a correction of +1 when the exact correction is - * 0. This gives 128 errors. The exact answer (correct for all 16 bit - * input values) is: - * - * error = (vlo-vhi+128)*65535 >> 24; - * - * An alternative arithmetic calculation which also gives no errors is: - * - * (V * 255 + 32895) >> 16 - */ - - png_int_32 tmp = *sp++; /* must be signed! */ - tmp += (((int)*sp++ - tmp + 128) * 65535) >> 24; - *dp++ = (png_byte)tmp; - } - - row_info->bit_depth = 8; - row_info->pixel_depth = (png_byte)(8 * row_info->channels); - row_info->rowbytes = row_info->width * row_info->channels; - } -} -#endif - -#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED -static void -/* Simply discard the low byte. This was the default behavior prior - * to libpng-1.5.4. - */ -png_do_chop(png_row_infop row_info, png_bytep row) -{ - png_debug(1, "in png_do_chop"); - - if (row_info->bit_depth == 16) - { - png_bytep sp = row; /* source */ - png_bytep dp = row; /* destination */ - png_bytep ep = sp + row_info->rowbytes; /* end+1 */ - - while (sp < ep) - { - *dp++ = *sp; - sp += 2; /* skip low byte */ - } - - row_info->bit_depth = 8; - row_info->pixel_depth = (png_byte)(8 * row_info->channels); - row_info->rowbytes = row_info->width * row_info->channels; - } -} -#endif - -#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED -static void -png_do_read_swap_alpha(png_row_infop row_info, png_bytep row) -{ - png_debug(1, "in png_do_read_swap_alpha"); - - { - png_uint_32 row_width = row_info->width; - if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - { - /* This converts from RGBA to ARGB */ - if (row_info->bit_depth == 8) - { - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_byte save; - png_uint_32 i; - - for (i = 0; i < row_width; i++) - { - save = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = save; - } - } - -#ifdef PNG_READ_16BIT_SUPPORTED - /* This converts from RRGGBBAA to AARRGGBB */ - else - { - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_byte save[2]; - png_uint_32 i; - - for (i = 0; i < row_width; i++) - { - save[0] = *(--sp); - save[1] = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = save[0]; - *(--dp) = save[1]; - } - } -#endif - } - - else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - { - /* This converts from GA to AG */ - if (row_info->bit_depth == 8) - { - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_byte save; - png_uint_32 i; - - for (i = 0; i < row_width; i++) - { - save = *(--sp); - *(--dp) = *(--sp); - *(--dp) = save; - } - } - -#ifdef PNG_READ_16BIT_SUPPORTED - /* This converts from GGAA to AAGG */ - else - { - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_byte save[2]; - png_uint_32 i; - - for (i = 0; i < row_width; i++) - { - save[0] = *(--sp); - save[1] = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = save[0]; - *(--dp) = save[1]; - } - } -#endif - } - } -} -#endif - -#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED -static void -png_do_read_invert_alpha(png_row_infop row_info, png_bytep row) -{ - png_uint_32 row_width; - png_debug(1, "in png_do_read_invert_alpha"); - - row_width = row_info->width; - if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - { - if (row_info->bit_depth == 8) - { - /* This inverts the alpha channel in RGBA */ - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_uint_32 i; - - for (i = 0; i < row_width; i++) - { - *(--dp) = (png_byte)(255 - *(--sp)); - -/* This does nothing: - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - We can replace it with: -*/ - sp-=3; - dp=sp; - } - } - -#ifdef PNG_READ_16BIT_SUPPORTED - /* This inverts the alpha channel in RRGGBBAA */ - else - { - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_uint_32 i; - - for (i = 0; i < row_width; i++) - { - *(--dp) = (png_byte)(255 - *(--sp)); - *(--dp) = (png_byte)(255 - *(--sp)); - -/* This does nothing: - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - We can replace it with: -*/ - sp-=6; - dp=sp; - } - } -#endif - } - else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - { - if (row_info->bit_depth == 8) - { - /* This inverts the alpha channel in GA */ - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_uint_32 i; - - for (i = 0; i < row_width; i++) - { - *(--dp) = (png_byte)(255 - *(--sp)); - *(--dp) = *(--sp); - } - } - -#ifdef PNG_READ_16BIT_SUPPORTED - else - { - /* This inverts the alpha channel in GGAA */ - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_uint_32 i; - - for (i = 0; i < row_width; i++) - { - *(--dp) = (png_byte)(255 - *(--sp)); - *(--dp) = (png_byte)(255 - *(--sp)); -/* - *(--dp) = *(--sp); - *(--dp) = *(--sp); -*/ - sp-=2; - dp=sp; - } - } -#endif - } -} -#endif - -#ifdef PNG_READ_FILLER_SUPPORTED -/* Add filler channel if we have RGB color */ -static void -png_do_read_filler(png_row_infop row_info, png_bytep row, - png_uint_32 filler, png_uint_32 flags) -{ - png_uint_32 i; - png_uint_32 row_width = row_info->width; - -#ifdef PNG_READ_16BIT_SUPPORTED - png_byte hi_filler = (png_byte)((filler>>8) & 0xff); -#endif - png_byte lo_filler = (png_byte)(filler & 0xff); - - png_debug(1, "in png_do_read_filler"); - - if ( - row_info->color_type == PNG_COLOR_TYPE_GRAY) - { - if (row_info->bit_depth == 8) - { - if (flags & PNG_FLAG_FILLER_AFTER) - { - /* This changes the data from G to GX */ - png_bytep sp = row + (png_size_t)row_width; - png_bytep dp = sp + (png_size_t)row_width; - for (i = 1; i < row_width; i++) - { - *(--dp) = lo_filler; - *(--dp) = *(--sp); - } - *(--dp) = lo_filler; - row_info->channels = 2; - row_info->pixel_depth = 16; - row_info->rowbytes = row_width * 2; - } - - else - { - /* This changes the data from G to XG */ - png_bytep sp = row + (png_size_t)row_width; - png_bytep dp = sp + (png_size_t)row_width; - for (i = 0; i < row_width; i++) - { - *(--dp) = *(--sp); - *(--dp) = lo_filler; - } - row_info->channels = 2; - row_info->pixel_depth = 16; - row_info->rowbytes = row_width * 2; - } - } - -#ifdef PNG_READ_16BIT_SUPPORTED - else if (row_info->bit_depth == 16) - { - if (flags & PNG_FLAG_FILLER_AFTER) - { - /* This changes the data from GG to GGXX */ - png_bytep sp = row + (png_size_t)row_width * 2; - png_bytep dp = sp + (png_size_t)row_width * 2; - for (i = 1; i < row_width; i++) - { - *(--dp) = hi_filler; - *(--dp) = lo_filler; - *(--dp) = *(--sp); - *(--dp) = *(--sp); - } - *(--dp) = hi_filler; - *(--dp) = lo_filler; - row_info->channels = 2; - row_info->pixel_depth = 32; - row_info->rowbytes = row_width * 4; - } - - else - { - /* This changes the data from GG to XXGG */ - png_bytep sp = row + (png_size_t)row_width * 2; - png_bytep dp = sp + (png_size_t)row_width * 2; - for (i = 0; i < row_width; i++) - { - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = hi_filler; - *(--dp) = lo_filler; - } - row_info->channels = 2; - row_info->pixel_depth = 32; - row_info->rowbytes = row_width * 4; - } - } -#endif - } /* COLOR_TYPE == GRAY */ - else if (row_info->color_type == PNG_COLOR_TYPE_RGB) - { - if (row_info->bit_depth == 8) - { - if (flags & PNG_FLAG_FILLER_AFTER) - { - /* This changes the data from RGB to RGBX */ - png_bytep sp = row + (png_size_t)row_width * 3; - png_bytep dp = sp + (png_size_t)row_width; - for (i = 1; i < row_width; i++) - { - *(--dp) = lo_filler; - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - } - *(--dp) = lo_filler; - row_info->channels = 4; - row_info->pixel_depth = 32; - row_info->rowbytes = row_width * 4; - } - - else - { - /* This changes the data from RGB to XRGB */ - png_bytep sp = row + (png_size_t)row_width * 3; - png_bytep dp = sp + (png_size_t)row_width; - for (i = 0; i < row_width; i++) - { - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = lo_filler; - } - row_info->channels = 4; - row_info->pixel_depth = 32; - row_info->rowbytes = row_width * 4; - } - } - -#ifdef PNG_READ_16BIT_SUPPORTED - else if (row_info->bit_depth == 16) - { - if (flags & PNG_FLAG_FILLER_AFTER) - { - /* This changes the data from RRGGBB to RRGGBBXX */ - png_bytep sp = row + (png_size_t)row_width * 6; - png_bytep dp = sp + (png_size_t)row_width * 2; - for (i = 1; i < row_width; i++) - { - *(--dp) = hi_filler; - *(--dp) = lo_filler; - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - } - *(--dp) = hi_filler; - *(--dp) = lo_filler; - row_info->channels = 4; - row_info->pixel_depth = 64; - row_info->rowbytes = row_width * 8; - } - - else - { - /* This changes the data from RRGGBB to XXRRGGBB */ - png_bytep sp = row + (png_size_t)row_width * 6; - png_bytep dp = sp + (png_size_t)row_width * 2; - for (i = 0; i < row_width; i++) - { - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = hi_filler; - *(--dp) = lo_filler; + png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0); + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_PLTE); } - row_info->channels = 4; - row_info->pixel_depth = 64; - row_info->rowbytes = row_width * 8; - } - } -#endif - } /* COLOR_TYPE == RGB */ -} -#endif - -#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED -/* Expand grayscale files to RGB, with or without alpha */ -static void -png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) -{ - png_uint_32 i; - png_uint_32 row_width = row_info->width; - - png_debug(1, "in png_do_gray_to_rgb"); - - if (row_info->bit_depth >= 8 && - !(row_info->color_type & PNG_COLOR_MASK_COLOR)) - { - if (row_info->color_type == PNG_COLOR_TYPE_GRAY) - { - if (row_info->bit_depth == 8) - { - /* This changes G to RGB */ - png_bytep sp = row + (png_size_t)row_width - 1; - png_bytep dp = sp + (png_size_t)row_width * 2; - for (i = 0; i < row_width; i++) - { - *(dp--) = *sp; - *(dp--) = *sp; - *(dp--) = *(sp--); - } - } - - else - { - /* This changes GG to RRGGBB */ - png_bytep sp = row + (png_size_t)row_width * 2 - 1; - png_bytep dp = sp + (png_size_t)row_width * 4; - for (i = 0; i < row_width; i++) - { - *(dp--) = *sp; - *(dp--) = *(sp - 1); - *(dp--) = *sp; - *(dp--) = *(sp - 1); - *(dp--) = *(sp--); - *(dp--) = *(sp--); - } - } - } - - else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - { - if (row_info->bit_depth == 8) - { - /* This changes GA to RGBA */ - png_bytep sp = row + (png_size_t)row_width * 2 - 1; - png_bytep dp = sp + (png_size_t)row_width * 2; - for (i = 0; i < row_width; i++) - { - *(dp--) = *(sp--); - *(dp--) = *sp; - *(dp--) = *sp; - *(dp--) = *(sp--); - } - } - - else - { - /* This changes GGAA to RRGGBBAA */ - png_bytep sp = row + (png_size_t)row_width * 4 - 1; - png_bytep dp = sp + (png_size_t)row_width * 4; - for (i = 0; i < row_width; i++) - { - *(dp--) = *(sp--); - *(dp--) = *(sp--); - *(dp--) = *sp; - *(dp--) = *(sp - 1); - *(dp--) = *sp; - *(dp--) = *(sp - 1); - *(dp--) = *(sp--); - *(dp--) = *(sp--); - } - } - } - row_info->channels = (png_byte)(row_info->channels + 2); - row_info->color_type |= PNG_COLOR_MASK_COLOR; - row_info->pixel_depth = (png_byte)(row_info->channels * - row_info->bit_depth); - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); - } -} -#endif - -#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED -/* Reduce RGB files to grayscale, with or without alpha - * using the equation given in Poynton's ColorFAQ of 1998-01-04 at - * (THIS LINK IS DEAD June 2008 but - * versions dated 1998 through November 2002 have been archived at - * http://web.archive.org/web/20000816232553/http://www.inforamp.net/ - * ~poynton/notes/colour_and_gamma/ColorFAQ.txt ) - * Charles Poynton poynton at poynton.com - * - * Y = 0.212671 * R + 0.715160 * G + 0.072169 * B - * - * which can be expressed with integers as - * - * Y = (6969 * R + 23434 * G + 2365 * B)/32768 - * - * Poynton's current link (as of January 2003 through July 2011): - * - * has changed the numbers slightly: - * - * Y = 0.2126*R + 0.7152*G + 0.0722*B - * - * which can be expressed with integers as - * - * Y = (6966 * R + 23436 * G + 2366 * B)/32768 - * - * Historically, however, libpng uses numbers derived from the ITU-R Rec 709 - * end point chromaticities and the D65 white point. Depending on the - * precision used for the D65 white point this produces a variety of different - * numbers, however if the four decimal place value used in ITU-R Rec 709 is - * used (0.3127,0.3290) the Y calculation would be: - * - * Y = (6968 * R + 23435 * G + 2366 * B)/32768 - * - * While this is correct the rounding results in an overflow for white, because - * the sum of the rounded coefficients is 32769, not 32768. Consequently - * libpng uses, instead, the closest non-overflowing approximation: - * - * Y = (6968 * R + 23434 * G + 2366 * B)/32768 - * - * Starting with libpng-1.5.5, if the image being converted has a cHRM chunk - * (including an sRGB chunk) then the chromaticities are used to calculate the - * coefficients. See the chunk handling in pngrutil.c for more information. - * - * In all cases the calculation is to be done in a linear colorspace. If no - * gamma information is available to correct the encoding of the original RGB - * values this results in an implicit assumption that the original PNG RGB - * values were linear. - * - * Other integer coefficents can be used via png_set_rgb_to_gray(). Because - * the API takes just red and green coefficients the blue coefficient is - * calculated to make the sum 32768. This will result in different rounding - * to that used above. - */ -static int -png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row) - -{ - int rgb_error = 0; - - png_debug(1, "in png_do_rgb_to_gray"); - - if (!(row_info->color_type & PNG_COLOR_MASK_PALETTE) && - (row_info->color_type & PNG_COLOR_MASK_COLOR)) - { - PNG_CONST png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff; - PNG_CONST png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff; - PNG_CONST png_uint_32 bc = 32768 - rc - gc; - PNG_CONST png_uint_32 row_width = row_info->width; - PNG_CONST int have_alpha = - (row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0; - - if (row_info->bit_depth == 8) - { - /* Notice that gamma to/from 1 are not necessarily inverses (if - * there is an overall gamma correction). Prior to 1.5.5 this code - * checked the linearized values for equality; this doesn't match - * the documentation, the original values must be checked. - */ - if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) - { - PNG_CONST unsigned int shift = 15 + png_ptr->gamma_shift; - PNG_CONST png_uint_32 add = 1U << (shift-1); - png_bytep sp = row; - png_bytep dp = row; - png_uint_32 i; - - for (i = 0; i < row_width; i++) - { - unsigned int red = *(sp++); - unsigned int green = *(sp++); - unsigned int blue = *(sp++); - - if (red != green || red != blue) - { - red = png_ptr->gamma_to_1[red]; - green = png_ptr->gamma_to_1[green]; - blue = png_ptr->gamma_to_1[blue]; - - rgb_error |= 1; - *(dp++) = png_ptr->gamma_from_1[ - (rc*red + gc*green + bc*blue + add)>>shift]; - } - - else - { - /* If there is no overall correction the table will not be - * set. - */ - if (png_ptr->gamma_table != NULL) - red = png_ptr->gamma_table[red]; - - *(dp++) = (png_byte)red; - } - - if (have_alpha != 0) - *(dp++) = *(sp++); - } - } - - else - { - png_bytep sp = row; - png_bytep dp = row; - png_uint_32 i; - - for (i = 0; i < row_width; i++) - { - png_byte red = *(sp++); - png_byte green = *(sp++); - png_byte blue = *(sp++); - - if (red != green || red != blue) - { - rgb_error |= 1; - *(dp++) = (png_byte)((rc*red+gc*green+bc*blue+16384)>>15); - } - - else - *(dp++) = red; - - if (have_alpha != 0) - *(dp++) = *(sp++); - } - } - } - - else /* RGB bit_depth == 16 */ - { - if (png_ptr->gamma_16_to_1 != NULL && png_ptr->gamma_16_from_1 != NULL) - { - unsigned int shift = png_ptr->gamma_shift; - unsigned int add = (shift > 0 ? (1U << (shift-1)) : 0); - png_bytep sp = row; - png_bytep dp = row; - png_uint_32 i; - - for (i = 0; i < row_width; i++) - { - png_uint_16 red, green, blue, w; - - red = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; - green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; - blue = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; - - if (red == green && red == blue) - { - if (png_ptr->gamma_16_table != NULL) - w = png_ptr->gamma_16_table[(red+add) >> shift]; - - else - w = red; - } - - else - { - png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red+add) - >> shift]; - png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green+add) - >> shift]; - png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue+add) - >> shift]; - png_uint_16 gray16 = (png_uint_16)((rc*red_1 + gc*green_1 - + bc*blue_1 + 16384)>>15); - w = png_ptr->gamma_16_from_1[(gray16+add) >> shift]; - rgb_error |= 1; - } - - *(dp++) = (png_byte)((w>>8) & 0xff); - *(dp++) = (png_byte)(w & 0xff); - - if (have_alpha != 0) - { - *(dp++) = *(sp++); - *(dp++) = *(sp++); - } - } - } - - else - { - png_bytep sp = row; - png_bytep dp = row; - png_uint_32 i; - - for (i = 0; i < row_width; i++) - { - png_uint_16 red, green, blue, gray16; - - red = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; - green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; - blue = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; - - if (red != green || red != blue) - rgb_error |= 1; - - /* From 1.5.5 in the 16 bit case do the accurate conversion even - * in the 'fast' case - this is because this is where the code - * ends up when handling linear 16 bit data. +# ifdef PNG_READ_tRNS + /* If the output format is not a palette format the tRNS + * information was a single color which is now invalid + * (probably), otherwise the array of tRNS values must be + * updated. */ - gray16 = (png_uint_16)((rc*red+gc*green+bc*blue+16384) >> 15); - *(dp++) = (png_byte)((gray16>>8) & 0xff); - *(dp++) = (png_byte)(gray16 & 0xff); - - if (have_alpha != 0) + if ((info_ptr->format & PNG_FORMAT_FLAG_COLORMAP) != 0) { - *(dp++) = *(sp++); - *(dp++) = *(sp++); - } - } - } - } - - row_info->channels = (png_byte)(row_info->channels - 2); - row_info->color_type = (png_byte)(row_info->color_type & - ~PNG_COLOR_MASK_COLOR); - row_info->pixel_depth = (png_byte)(row_info->channels * - row_info->bit_depth); - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); - } - return rgb_error; -} -#endif - -#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) -/* Replace any alpha or transparency with the supplied background color. - * "background" is already in the screen gamma, while "background_1" is - * at a gamma of 1.0. Paletted files have already been taken care of. - */ -static void -png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) -{ - png_const_bytep gamma_table = png_ptr->gamma_table; - png_const_bytep gamma_from_1 = png_ptr->gamma_from_1; - png_const_uint_16p gamma_to_1 = png_ptr->gamma_to_1; - png_const_uint_16p gamma_16 = png_ptr->gamma_16_table; - png_const_uint_16p gamma_16_from_1 = png_ptr->gamma_16_from_1; - png_const_uint_16p gamma_16_to_1 = png_ptr->gamma_16_to_1; - PNG_CONST unsigned int shift = png_ptr->gamma_shift; - PNG_CONST unsigned int add = (shift > 0 ? 1U<<(shift-1) : 0); - PNG_CONST int optimize = (png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0; - - png_bytep sp; - png_uint_32 i; - png_uint_32 row_width = row_info->width; - - png_debug(1, "in png_do_compose"); - - { - switch (row_info->color_type) - { - case PNG_COLOR_TYPE_GRAY: - { - switch (row_info->bit_depth) - { - case 1: - { - int bit_shift = 7; - sp = row; - for (i = 0; i < row_width; i++) - { - if ((png_uint_16)((*sp >> bit_shift) & 0x01) - == png_ptr->trans_color.gray) - { - unsigned int tmp = *sp & (0x7f7f >> (7 - bit_shift)); - tmp |= png_ptr->background.gray << bit_shift; - *sp = (png_byte)(tmp & 0xff); - } - - if (bit_shift == 0) - { - bit_shift = 7; - sp++; - } - - else - bit_shift--; - } - break; - } - - case 2: - { -#if 0 - if (gamma_table != NULL) - { - int bit_shift = 6; - sp = row; - for (i = 0; i < row_width; i++) - { - if ((png_uint_16)((*sp >> bit_shift) & 0x03) - == png_ptr->trans_color.gray) - { - unsigned int tmp = *sp & (0x3f3f >> (6 - bit_shift)); - tmp |= png_ptr->background.gray << bit_shift; - *sp = (png_byte)(tmp & 0xff); - } - - else - { - unsigned int p = (*sp >> bit_shift) & 0x03; - unsigned int g = (gamma_table [p | (p << 2) | - (p << 4) | (p << 6)] >> 6) & 0x03; - unsigned int tmp = *sp & (0x3f3f >> (6 - bit_shift)); - tmp |= g << bit_shift; - *sp = (png_byte)(tmp & 0xff); - } - - if (bit_shift == 0) - { - bit_shift = 6; - sp++; - } - - else - bit_shift -= 2; - } - } - - else -#endif - { - int bit_shift = 6; - sp = row; - for (i = 0; i < row_width; i++) - { - if ((png_uint_16)((*sp >> bit_shift) & 0x03) - == png_ptr->trans_color.gray) - { - unsigned int tmp = *sp & (0x3f3f >> (6 - bit_shift)); - tmp |= png_ptr->background.gray << bit_shift; - *sp = (png_byte)(tmp & 0xff); - } - - if (bit_shift == 0) - { - bit_shift = 6; - sp++; - } - - else - bit_shift -= 2; - } - } - break; - } - - case 4: - { -#if 0 - if (gamma_table != NULL) - { - int bit_shift = 4; - sp = row; - for (i = 0; i < row_width; i++) - { - if ((png_uint_16)((*sp >> bit_shift) & 0x0f) - == png_ptr->trans_color.gray) - { - unsigned int tmp = *sp & (0xf0f >> (4 - bit_shift)); - tmp |= png_ptr->background.gray << bit_shift; - *sp = (png_byte)(tmp & 0xff); - } - - else - { - unsigned int p = (*sp >> bit_shift) & 0x0f; - unsigned int g = (gamma_table[p | (p << 4)] >> 4) & - 0x0f; - unsigned int tmp = *sp & (0xf0f >> (4 - bit_shift)); - tmp |= g << bit_shift; - *sp = (png_byte)(tmp & 0xff); - } - - if (bit_shift == 0) - { - bit_shift = 4; - sp++; - } - - else - bit_shift -= 4; - } - } - - else -#endif - { - int bit_shift = 4; - sp = row; - for (i = 0; i < row_width; i++) - { - if ((png_uint_16)((*sp >> bit_shift) & 0x0f) - == png_ptr->trans_color.gray) - { - unsigned int tmp = *sp & (0xf0f >> (4 - bit_shift)); - tmp |= png_ptr->background.gray << bit_shift; - *sp = (png_byte)(tmp & 0xff); - } - - if (bit_shift == 0) - { - bit_shift = 4; - sp++; - } - - else - bit_shift -= 4; - } - } - break; - } - - case 8: - { - if (gamma_table != NULL) - { - sp = row; - for (i = 0; i < row_width; i++, sp++) - { - if (*sp == png_ptr->trans_color.gray) - *sp = (png_byte)png_ptr->background.gray; - - else - *sp = gamma_table[*sp]; - } - } + if (png_ptr->num_trans > 0) + png_set_tRNS(png_ptr, info_ptr, png_ptr->trans_alpha, + png_ptr->num_trans, NULL/*trans color*/); else { - sp = row; - for (i = 0; i < row_width; i++, sp++) - { - if (*sp == png_ptr->trans_color.gray) - *sp = (png_byte)png_ptr->background.gray; - } - } - break; - } - - case 16: - { - if (gamma_16 != NULL) - { - sp = row; - for (i = 0; i < row_width; i++, sp += 2) - { - png_uint_16 v; - - v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); - - if (v == png_ptr->trans_color.gray) - { - /* Background is already in screen gamma */ - *sp = (png_byte)((png_ptr->background.gray >> 8) - & 0xff); - *(sp + 1) = (png_byte)(png_ptr->background.gray - & 0xff); - } - - else - { - v = gamma_16[(v+add) >> shift]; - *sp = (png_byte)((v >> 8) & 0xff); - *(sp + 1) = (png_byte)(v & 0xff); - } - } - } - - else - { - sp = row; - for (i = 0; i < row_width; i++, sp += 2) - { - png_uint_16 v; - - v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); - - if (v == png_ptr->trans_color.gray) - { - *sp = (png_byte)((png_ptr->background.gray >> 8) - & 0xff); - *(sp + 1) = (png_byte)(png_ptr->background.gray - & 0xff); - } - } - } - break; - } - - default: - break; - } - break; - } - - case PNG_COLOR_TYPE_RGB: - { - if (row_info->bit_depth == 8) - { - if (gamma_table != NULL) - { - sp = row; - for (i = 0; i < row_width; i++, sp += 3) - { - if (*sp == png_ptr->trans_color.red && - *(sp + 1) == png_ptr->trans_color.green && - *(sp + 2) == png_ptr->trans_color.blue) - { - *sp = (png_byte)png_ptr->background.red; - *(sp + 1) = (png_byte)png_ptr->background.green; - *(sp + 2) = (png_byte)png_ptr->background.blue; - } - - else - { - *sp = gamma_table[*sp]; - *(sp + 1) = gamma_table[*(sp + 1)]; - *(sp + 2) = gamma_table[*(sp + 2)]; - } + png_free_data(png_ptr, info_ptr, PNG_FREE_tRNS, 0); + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_tRNS); } } else - { - sp = row; - for (i = 0; i < row_width; i++, sp += 3) - { - if (*sp == png_ptr->trans_color.red && - *(sp + 1) == png_ptr->trans_color.green && - *(sp + 2) == png_ptr->trans_color.blue) - { - *sp = (png_byte)png_ptr->background.red; - *(sp + 1) = (png_byte)png_ptr->background.green; - *(sp + 2) = (png_byte)png_ptr->background.blue; - } - } - } - } - else /* if (row_info->bit_depth == 16) */ - { - if (gamma_16 != NULL) - { - sp = row; - for (i = 0; i < row_width; i++, sp += 6) - { - png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); - - png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) - + *(sp + 3)); - - png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) - + *(sp + 5)); - - if (r == png_ptr->trans_color.red && - g == png_ptr->trans_color.green && - b == png_ptr->trans_color.blue) - { - /* Background is already in screen gamma */ - *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); - *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); - *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) - & 0xff); - *(sp + 3) = (png_byte)(png_ptr->background.green - & 0xff); - *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) - & 0xff); - *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); - } - - else - { - png_uint_16 v = gamma_16[(r+add) >> shift]; - *sp = (png_byte)((v >> 8) & 0xff); - *(sp + 1) = (png_byte)(v & 0xff); - - v = gamma_16[(g+add) >> shift]; - *(sp + 2) = (png_byte)((v >> 8) & 0xff); - *(sp + 3) = (png_byte)(v & 0xff); - - v = gamma_16[(b+add) >> shift]; - *(sp + 4) = (png_byte)((v >> 8) & 0xff); - *(sp + 5) = (png_byte)(v & 0xff); - } - } - } - - else - { - sp = row; - for (i = 0; i < row_width; i++, sp += 6) - { - png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); - - png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) - + *(sp + 3)); - - png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) - + *(sp + 5)); - - if (r == png_ptr->trans_color.red && - g == png_ptr->trans_color.green && - b == png_ptr->trans_color.blue) - { - *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); - *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); - *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) - & 0xff); - *(sp + 3) = (png_byte)(png_ptr->background.green - & 0xff); - *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) - & 0xff); - *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); - } - } - } - } - break; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_tRNS); +# endif /* READ_tRNS */ } - - case PNG_COLOR_TYPE_GRAY_ALPHA: - { - if (row_info->bit_depth == 8) - { - if (gamma_to_1 != NULL && gamma_from_1 != NULL && - gamma_table != NULL) - { - sp = row; - for (i = 0; i < row_width; i++, sp += 2) - { - png_uint_16 a = *(sp + 1); - - if (a == 0xff) - *sp = gamma_table[*sp]; - - else if (a == 0) - { - /* Background is already in screen gamma */ - *sp = (png_byte)png_ptr->background.gray; - } - - else - { - unsigned int v, w; - - v = gamma_to_1[*sp]; - png_composite_16(w, v, 257*a, - png_ptr->background_1.gray); - - if (optimize == 0) - w = gamma_from_1[(w+add)>>shift]; - - else /* alpha pixels linear and approximate */ - w = PNG_DIV257(w); - - *sp = (png_byte)w; - } - } - } - - else - { - sp = row; - for (i = 0; i < row_width; i++, sp += 2) - { - png_byte a = *(sp + 1); - - if (a == 0) - *sp = (png_byte)png_ptr->background.gray; - - else if (a < 0xff) - png_composite(*sp, *sp, a, png_ptr->background.gray); - } - } - } - - else /* if (png_ptr->bit_depth == 16) */ - { - if (gamma_16 != NULL && gamma_16_from_1 != NULL && - gamma_16_to_1 != NULL) - { - sp = row; - for (i = 0; i < row_width; i++, sp += 4) - { - png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8) - + *(sp + 3)); - - if (a == (png_uint_16)0xffff) - { - png_uint_16 v; - - v = gamma_16[((sp[0]<<8)+sp[1]+add) >> shift]; - *sp = (png_byte)((v >> 8) & 0xff); - *(sp + 1) = (png_byte)(v & 0xff); - } - - else if (a == 0) - { - /* Background is already in screen gamma */ - *sp = (png_byte)((png_ptr->background.gray >> 8) - & 0xff); - *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff); - } - - else - { - png_uint_16 g, v, w; - - g = gamma_16_to_1[((sp[0]<<8)+sp[1]+add) >> shift]; - png_composite_16(v, g, a, png_ptr->background_1.gray); - - if (optimize == 0) - w = gamma_16_from_1[(v+add) >> shift]; - - else - w = v; - - *sp = (png_byte)((w >> 8) & 0xff); - *(sp + 1) = (png_byte)(w & 0xff); - } - } - } - - else - { - sp = row; - for (i = 0; i < row_width; i++, sp += 4) - { - png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8) - + *(sp + 3)); - - if (a == 0) - { - *sp = (png_byte)((png_ptr->background.gray >> 8) - & 0xff); - *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff); - } - - else if (a < 0xffff) - { - png_uint_16 g, v; - - g = (png_uint_16)(((*sp) << 8) + *(sp + 1)); - png_composite_16(v, g, a, png_ptr->background.gray); - *sp = (png_byte)((v >> 8) & 0xff); - *(sp + 1) = (png_byte)(v & 0xff); - } - } - } - } - break; - } - - case PNG_COLOR_TYPE_RGB_ALPHA: - { - if (row_info->bit_depth == 8) - { - if (gamma_to_1 != NULL && gamma_from_1 != NULL && - gamma_table != NULL) - { - sp = row; - for (i = 0; i < row_width; i++, sp += 4) - { - png_byte a = *(sp + 3); - - if (a == 0xff) - { - *sp = gamma_table[*sp]; - *(sp + 1) = gamma_table[*(sp + 1)]; - *(sp + 2) = gamma_table[*(sp + 2)]; - } - - else if (a == 0) - { - /* Background is already in screen gamma */ - *sp = (png_byte)png_ptr->background.red; - *(sp + 1) = (png_byte)png_ptr->background.green; - *(sp + 2) = (png_byte)png_ptr->background.blue; - } - - else - { - unsigned int v, w; - unsigned int alpha = a * 257U; - - v = gamma_to_1[*sp]; - png_composite_16(w, v, alpha, - png_ptr->background_1.red); - - if (optimize == 0) - w = gamma_from_1[(w+add)>>shift]; - - else - w = PNG_DIV257(w); - - *sp = (png_byte)w; - - v = gamma_to_1[*(sp + 1)]; - png_composite_16(w, v, alpha, - png_ptr->background_1.green); - - if (optimize == 0) - w = gamma_from_1[(w+add)>>shift]; - - else - w = PNG_DIV257(w); - - *(sp + 1) = (png_byte)w; - - v = gamma_to_1[*(sp + 2)]; - png_composite_16(w, v, alpha, - png_ptr->background_1.blue); - - if (optimize == 0) - w = gamma_from_1[(w+add)>>shift]; - - else - w = PNG_DIV257(w); - - *(sp + 2) = (png_byte)w; - } - } - } - - else - { - sp = row; - for (i = 0; i < row_width; i++, sp += 4) - { - png_byte a = *(sp + 3); - - if (a == 0) - { - *sp = (png_byte)png_ptr->background.red; - *(sp + 1) = (png_byte)png_ptr->background.green; - *(sp + 2) = (png_byte)png_ptr->background.blue; - } - - else if (a < 0xff) - { - png_composite(*sp, *sp, a, png_ptr->background.red); - - png_composite(*(sp + 1), *(sp + 1), a, - png_ptr->background.green); - - png_composite(*(sp + 2), *(sp + 2), a, - png_ptr->background.blue); - } - } - } - } - - else /* if (row_info->bit_depth == 16) */ - { - if (gamma_16 != NULL && gamma_16_from_1 != NULL && - gamma_16_to_1 != NULL) - { - sp = row; - for (i = 0; i < row_width; i++, sp += 8) - { - png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) - << 8) + (png_uint_16)(*(sp + 7))); - - if (a == (png_uint_16)0xffff) - { - png_uint_16 v; - - v = gamma_16[((sp[0]<<8)+sp[1]+add) >> shift]; - *sp = (png_byte)((v >> 8) & 0xff); - *(sp + 1) = (png_byte)(v & 0xff); - - v = gamma_16[((sp[2]<<8)+sp[3]+add) >> shift]; - *(sp + 2) = (png_byte)((v >> 8) & 0xff); - *(sp + 3) = (png_byte)(v & 0xff); - - v = gamma_16[((sp[4]<<8)+sp[5]+add) >> shift]; - *(sp + 4) = (png_byte)((v >> 8) & 0xff); - *(sp + 5) = (png_byte)(v & 0xff); - } - - else if (a == 0) - { - /* Background is already in screen gamma */ - *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); - *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); - *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) - & 0xff); - *(sp + 3) = (png_byte)(png_ptr->background.green - & 0xff); - *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) - & 0xff); - *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); - } - - else - { - png_uint_16 v, w; - - v = gamma_16_to_1[((sp[0]<<8)+sp[1]+add) >> shift]; - png_composite_16(w, v, a, png_ptr->background_1.red); - - if (optimize == 0) - w = gamma_16_from_1[(w+add) >> shift]; - - *sp = (png_byte)((w >> 8) & 0xff); - *(sp + 1) = (png_byte)(w & 0xff); - - v = gamma_16_to_1[((sp[2]<<8)+sp[3]+add) >> shift]; - png_composite_16(w, v, a, png_ptr->background_1.green); - - if (optimize == 0) - w = gamma_16_from_1[(w+add) >> shift]; - - *(sp + 2) = (png_byte)((w >> 8) & 0xff); - *(sp + 3) = (png_byte)(w & 0xff); - - v = gamma_16_to_1[((sp[4]<<8)+sp[5]+add) >> shift]; - png_composite_16(w, v, a, png_ptr->background_1.blue); - - if (optimize == 0) - w = gamma_16_from_1[(w+add) >> shift]; - - *(sp + 4) = (png_byte)((w >> 8) & 0xff); - *(sp + 5) = (png_byte)(w & 0xff); - } - } - } - - else - { - sp = row; - for (i = 0; i < row_width; i++, sp += 8) - { - png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) - << 8) + (png_uint_16)(*(sp + 7))); - - if (a == 0) - { - *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); - *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); - *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) - & 0xff); - *(sp + 3) = (png_byte)(png_ptr->background.green - & 0xff); - *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) - & 0xff); - *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); - } - - else if (a < 0xffff) - { - png_uint_16 v; - - png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); - png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) - + *(sp + 3)); - png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) - + *(sp + 5)); - - png_composite_16(v, r, a, png_ptr->background.red); - *sp = (png_byte)((v >> 8) & 0xff); - *(sp + 1) = (png_byte)(v & 0xff); - - png_composite_16(v, g, a, png_ptr->background.green); - *(sp + 2) = (png_byte)((v >> 8) & 0xff); - *(sp + 3) = (png_byte)(v & 0xff); - - png_composite_16(v, b, a, png_ptr->background.blue); - *(sp + 4) = (png_byte)((v >> 8) & 0xff); - *(sp + 5) = (png_byte)(v & 0xff); - } - } - } - } - break; - } - - default: - break; - } - } +# endif /* READ_TRANSFORMS */ +# else /* !TRANSFORM_MECH */ + PNG_UNUSED(png_ptr) + PNG_UNUSED(info_ptr) +# endif /* !TRANSFORM_MECH */ } -#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_READ_ALPHA_MODE_SUPPORTED */ - -#ifdef PNG_READ_GAMMA_SUPPORTED -/* Gamma correct the image, avoiding the alpha channel. Make sure - * you do this after you deal with the transparency issue on grayscale - * or RGB images. If your bit depth is 8, use gamma_table, if it - * is 16, use gamma_16_table and gamma_shift. Build these with - * build_gamma_table(). - */ -static void -png_do_gamma(png_row_infop row_info, png_bytep row, png_structrp png_ptr) -{ - png_const_bytep gamma_table = png_ptr->gamma_table; - png_const_uint_16p gamma_16_table = png_ptr->gamma_16_table; - int shift = png_ptr->gamma_shift; - int add = (shift > 0 ? 1U << (shift-1) : 0); - - png_bytep sp; - png_uint_32 i; - png_uint_32 row_width=row_info->width; - - png_debug(1, "in png_do_gamma"); - - /* Prior to libpng 1.7.0 this code would attempt to gamma correct 2 and 4 bit - * gray level values, the results are ridiculously inaccurate. In 1.7.0 the - * code is removed and a warning is introduced to catch cases where an - * application might actually try it. - */ - if (((row_info->bit_depth == 8 && gamma_table != NULL) || - (row_info->bit_depth == 16 && gamma_16_table != NULL))) - { - switch (row_info->color_type) - { - case PNG_COLOR_TYPE_RGB: - { - if (row_info->bit_depth == 8) - { - sp = row; - for (i = 0; i < row_width; i++) - { - *sp = gamma_table[*sp]; - sp++; - *sp = gamma_table[*sp]; - sp++; - *sp = gamma_table[*sp]; - sp++; - } - } - - else /* if (row_info->bit_depth == 16) */ - { - sp = row; - for (i = 0; i < row_width; i++) - { - png_uint_16 v; - - v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; - *sp = (png_byte)((v >> 8) & 0xff); - *(sp + 1) = (png_byte)(v & 0xff); - sp += 2; - - v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; - *sp = (png_byte)((v >> 8) & 0xff); - *(sp + 1) = (png_byte)(v & 0xff); - sp += 2; - - v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; - *sp = (png_byte)((v >> 8) & 0xff); - *(sp + 1) = (png_byte)(v & 0xff); - sp += 2; - } - } - break; - } - - case PNG_COLOR_TYPE_RGB_ALPHA: - { - if (row_info->bit_depth == 8) - { - sp = row; - for (i = 0; i < row_width; i++) - { - *sp = gamma_table[*sp]; - sp++; - - *sp = gamma_table[*sp]; - sp++; - - *sp = gamma_table[*sp]; - sp++; - - sp++; - } - } - - else /* if (row_info->bit_depth == 16) */ - { - sp = row; - for (i = 0; i < row_width; i++) - { - png_uint_16 v; - - v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; - *sp = (png_byte)((v >> 8) & 0xff); - *(sp + 1) = (png_byte)(v & 0xff); - sp += 2; - - v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; - *sp = (png_byte)((v >> 8) & 0xff); - *(sp + 1) = (png_byte)(v & 0xff); - sp += 2; - - v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; - *sp = (png_byte)((v >> 8) & 0xff); - *(sp + 1) = (png_byte)(v & 0xff); - sp += 4; - } - } - break; - } - - case PNG_COLOR_TYPE_GRAY_ALPHA: - { - if (row_info->bit_depth == 8) - { - sp = row; - for (i = 0; i < row_width; i++) - { - *sp = gamma_table[*sp]; - sp += 2; - } - } - - else /* if (row_info->bit_depth == 16) */ - { - sp = row; - for (i = 0; i < row_width; i++) - { - png_uint_16 v; - - v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; - *sp = (png_byte)((v >> 8) & 0xff); - *(sp + 1) = (png_byte)(v & 0xff); - sp += 4; - } - } - break; - } - - case PNG_COLOR_TYPE_GRAY: - { - if (row_info->bit_depth == 8) - { - sp = row; - for (i = 0; i < row_width; i++) - { - *sp = gamma_table[*sp]; - sp++; - } - } - - else /*row_info->bit_depth == 16 */ - { - sp = row; - for (i = 0; i < row_width; i++) - { - png_uint_16 v; - - v = gamma_16_table[((sp[0]<<8)+sp[1]+add) >> shift]; - *sp = (png_byte)((v >> 8) & 0xff); - *(sp + 1) = (png_byte)(v & 0xff); - sp += 2; - } - } - break; - } - - default: - break; - } - } -} -#endif - -#ifdef PNG_READ_ALPHA_MODE_SUPPORTED -/* Encode the alpha channel to the output gamma (the input channel is always - * linear.) Called only with color types that have an alpha channel. Needs the - * from_1 tables. - */ -static void -png_do_encode_alpha(png_row_infop row_info, png_bytep row, png_structrp png_ptr) -{ - png_uint_32 row_width = row_info->width; - - png_debug(1, "in png_do_encode_alpha"); - - if (row_info->color_type & PNG_COLOR_MASK_ALPHA) - { - PNG_CONST unsigned int shift = png_ptr->gamma_shift; - PNG_CONST unsigned int add = (shift > 0 ? 1U<<(shift-1) : 0); - - if (row_info->bit_depth == 8) - { - PNG_CONST png_bytep gamma_from_1 = png_ptr->gamma_from_1; - - if (gamma_from_1 != NULL) - { - PNG_CONST int step = - (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 4 : 2; - - /* The alpha channel is the last component: */ - row += step - 1; - - for (; row_width > 0; --row_width, row += step) - *row = gamma_from_1[(257U**row+add)>>shift]; - - return; - } - } - - else if (row_info->bit_depth == 16) - { - PNG_CONST png_uint_16p gamma_16_from_1 = png_ptr->gamma_16_from_1; - - if (gamma_16_from_1 != NULL) - { - PNG_CONST int step = - (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 8 : 4; - - /* The alpha channel is the last component: */ - row += step - 2; - - for (; row_width > 0; --row_width, row += step) - { - png_uint_16 v; - - v = gamma_16_from_1[((row[0]<<8)+row[1]+add) >> shift]; - *row = (png_byte)((v >> 8) & 0xff); - *(row + 1) = (png_byte)(v & 0xff); - } - - return; - } - } - } - - /* Only get to here if called with a weird row_info; no harm has been done, - * so just issue a warning. - */ - png_warning(png_ptr, "png_do_encode_alpha: unexpected call"); -} -#endif - -#ifdef PNG_READ_EXPAND_SUPPORTED -/* Expands a palette row to an RGB or RGBA row depending - * upon whether you supply trans and num_trans. - */ -static void -png_do_expand_palette(png_row_infop row_info, png_bytep row, - png_const_colorp palette, png_const_bytep trans_alpha, int num_trans) -{ - int shift, value; - png_bytep sp, dp; - png_uint_32 i; - png_uint_32 row_width=row_info->width; - - png_debug(1, "in png_do_expand_palette"); - - if (row_info->color_type == PNG_COLOR_TYPE_PALETTE) - { - if (row_info->bit_depth < 8) - { - switch (row_info->bit_depth) - { - case 1: - { - sp = row + (png_size_t)((row_width - 1) >> 3); - dp = row + (png_size_t)row_width - 1; - shift = 7 - (int)((row_width + 7) & 0x07); - for (i = 0; i < row_width; i++) - { - if ((*sp >> shift) & 0x01) - *dp = 1; - - else - *dp = 0; - - if (shift == 7) - { - shift = 0; - sp--; - } - - else - shift++; - - dp--; - } - break; - } - - case 2: - { - sp = row + (png_size_t)((row_width - 1) >> 2); - dp = row + (png_size_t)row_width - 1; - shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); - for (i = 0; i < row_width; i++) - { - value = (*sp >> shift) & 0x03; - *dp = (png_byte)value; - if (shift == 6) - { - shift = 0; - sp--; - } - - else - shift += 2; - - dp--; - } - break; - } - - case 4: - { - sp = row + (png_size_t)((row_width - 1) >> 1); - dp = row + (png_size_t)row_width - 1; - shift = (int)((row_width & 0x01) << 2); - for (i = 0; i < row_width; i++) - { - value = (*sp >> shift) & 0x0f; - *dp = (png_byte)value; - if (shift == 4) - { - shift = 0; - sp--; - } - - else - shift += 4; - - dp--; - } - break; - } - - default: - break; - } - row_info->bit_depth = 8; - row_info->pixel_depth = 8; - row_info->rowbytes = row_width; - } - - if (row_info->bit_depth == 8) - { - { - if (num_trans > 0) - { - sp = row + (png_size_t)row_width - 1; - dp = row + (png_size_t)(row_width << 2) - 1; - - for (i = 0; i < row_width; i++) - { - if ((int)(*sp) >= num_trans) - *dp-- = 0xff; - - else - *dp-- = trans_alpha[*sp]; - - *dp-- = palette[*sp].blue; - *dp-- = palette[*sp].green; - *dp-- = palette[*sp].red; - sp--; - } - row_info->bit_depth = 8; - row_info->pixel_depth = 32; - row_info->rowbytes = row_width * 4; - row_info->color_type = 6; - row_info->channels = 4; - } - - else - { - sp = row + (png_size_t)row_width - 1; - dp = row + (png_size_t)(row_width * 3) - 1; - - for (i = 0; i < row_width; i++) - { - *dp-- = palette[*sp].blue; - *dp-- = palette[*sp].green; - *dp-- = palette[*sp].red; - sp--; - } - - row_info->bit_depth = 8; - row_info->pixel_depth = 24; - row_info->rowbytes = row_width * 3; - row_info->color_type = 2; - row_info->channels = 3; - } - } - } - } -} - -/* If the bit depth < 8, it is expanded to 8. Also, if the already - * expanded transparency value is supplied, an alpha channel is built. - */ -static void -png_do_expand(png_row_infop row_info, png_bytep row, - png_const_color_16p trans_color) -{ - int shift, value; - png_bytep sp, dp; - png_uint_32 i; - png_uint_32 row_width=row_info->width; - - png_debug(1, "in png_do_expand"); - - { - if (row_info->color_type == PNG_COLOR_TYPE_GRAY) - { - unsigned int gray = trans_color ? trans_color->gray : 0; - - if (row_info->bit_depth < 8) - { - switch (row_info->bit_depth) - { - case 1: - { - gray = (gray & 0x01) * 0xff; - sp = row + (png_size_t)((row_width - 1) >> 3); - dp = row + (png_size_t)row_width - 1; - shift = 7 - (int)((row_width + 7) & 0x07); - for (i = 0; i < row_width; i++) - { - if ((*sp >> shift) & 0x01) - *dp = 0xff; - - else - *dp = 0; - - if (shift == 7) - { - shift = 0; - sp--; - } - - else - shift++; - - dp--; - } - break; - } - - case 2: - { - gray = (gray & 0x03) * 0x55; - sp = row + (png_size_t)((row_width - 1) >> 2); - dp = row + (png_size_t)row_width - 1; - shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); - for (i = 0; i < row_width; i++) - { - value = (*sp >> shift) & 0x03; - *dp = (png_byte)(value | (value << 2) | (value << 4) | - (value << 6)); - if (shift == 6) - { - shift = 0; - sp--; - } - - else - shift += 2; - - dp--; - } - break; - } - - case 4: - { - gray = (gray & 0x0f) * 0x11; - sp = row + (png_size_t)((row_width - 1) >> 1); - dp = row + (png_size_t)row_width - 1; - shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); - for (i = 0; i < row_width; i++) - { - value = (*sp >> shift) & 0x0f; - *dp = (png_byte)(value | (value << 4)); - if (shift == 4) - { - shift = 0; - sp--; - } - - else - shift = 4; - - dp--; - } - break; - } - - default: - break; - } - - row_info->bit_depth = 8; - row_info->pixel_depth = 8; - row_info->rowbytes = row_width; - } - - if (trans_color != NULL) - { - if (row_info->bit_depth == 8) - { - gray = gray & 0xff; - sp = row + (png_size_t)row_width - 1; - dp = row + (png_size_t)(row_width << 1) - 1; - - for (i = 0; i < row_width; i++) - { - if (*sp == gray) - *dp-- = 0; - - else - *dp-- = 0xff; - - *dp-- = *sp--; - } - } - - else if (row_info->bit_depth == 16) - { - unsigned int gray_high = (gray >> 8) & 0xff; - unsigned int gray_low = gray & 0xff; - sp = row + row_info->rowbytes - 1; - dp = row + (row_info->rowbytes << 1) - 1; - for (i = 0; i < row_width; i++) - { - if (*(sp - 1) == gray_high && *(sp) == gray_low) - { - *dp-- = 0; - *dp-- = 0; - } - - else - { - *dp-- = 0xff; - *dp-- = 0xff; - } - - *dp-- = *sp--; - *dp-- = *sp--; - } - } - - row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA; - row_info->channels = 2; - row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1); - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, - row_width); - } - } - else if (row_info->color_type == PNG_COLOR_TYPE_RGB && trans_color) - { - if (row_info->bit_depth == 8) - { - png_byte red = (png_byte)(trans_color->red & 0xff); - png_byte green = (png_byte)(trans_color->green & 0xff); - png_byte blue = (png_byte)(trans_color->blue & 0xff); - sp = row + (png_size_t)row_info->rowbytes - 1; - dp = row + (png_size_t)(row_width << 2) - 1; - for (i = 0; i < row_width; i++) - { - if (*(sp - 2) == red && *(sp - 1) == green && *(sp) == blue) - *dp-- = 0; - - else - *dp-- = 0xff; - - *dp-- = *sp--; - *dp-- = *sp--; - *dp-- = *sp--; - } - } - else if (row_info->bit_depth == 16) - { - png_byte red_high = (png_byte)((trans_color->red >> 8) & 0xff); - png_byte green_high = (png_byte)((trans_color->green >> 8) & 0xff); - png_byte blue_high = (png_byte)((trans_color->blue >> 8) & 0xff); - png_byte red_low = (png_byte)(trans_color->red & 0xff); - png_byte green_low = (png_byte)(trans_color->green & 0xff); - png_byte blue_low = (png_byte)(trans_color->blue & 0xff); - sp = row + row_info->rowbytes - 1; - dp = row + (png_size_t)(row_width << 3) - 1; - for (i = 0; i < row_width; i++) - { - if (*(sp - 5) == red_high && - *(sp - 4) == red_low && - *(sp - 3) == green_high && - *(sp - 2) == green_low && - *(sp - 1) == blue_high && - *(sp ) == blue_low) - { - *dp-- = 0; - *dp-- = 0; - } - - else - { - *dp-- = 0xff; - *dp-- = 0xff; - } - - *dp-- = *sp--; - *dp-- = *sp--; - *dp-- = *sp--; - *dp-- = *sp--; - *dp-- = *sp--; - *dp-- = *sp--; - } - } - row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA; - row_info->channels = 4; - row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2); - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); - } - } -} -#endif - -#ifdef PNG_READ_EXPAND_16_SUPPORTED -/* If the bit depth is 8 and the color type is not a palette type expand the - * whole row to 16 bits. Has no effect otherwise. - */ -static void -png_do_expand_16(png_row_infop row_info, png_bytep row) -{ - if (row_info->bit_depth == 8 && - row_info->color_type != PNG_COLOR_TYPE_PALETTE) - { - /* The row have a sequence of bytes containing [0..255] and we need - * to turn it into another row containing [0..65535], to do this we - * calculate: - * - * (input / 255) * 65535 - * - * Which happens to be exactly input * 257 and this can be achieved - * simply by byte replication in place (copying backwards). - */ - png_byte *sp = row + row_info->rowbytes; /* source, last byte + 1 */ - png_byte *dp = sp + row_info->rowbytes; /* destination, end + 1 */ - while (dp > sp) - dp[-2] = dp[-1] = *--sp, dp -= 2; - - row_info->rowbytes *= 2; - row_info->bit_depth = 16; - row_info->pixel_depth = (png_byte)(row_info->channels * 16); - } -} -#endif - -#ifdef PNG_READ_QUANTIZE_SUPPORTED -static void -png_do_quantize(png_row_infop row_info, png_bytep row, - png_const_bytep palette_lookup, png_const_bytep quantize_lookup) -{ - png_bytep sp, dp; - png_uint_32 i; - png_uint_32 row_width=row_info->width; - - png_debug(1, "in png_do_quantize"); - - if (row_info->bit_depth == 8) - { - if (row_info->color_type == PNG_COLOR_TYPE_RGB && palette_lookup) - { - int r, g, b, p; - sp = row; - dp = row; - for (i = 0; i < row_width; i++) - { - r = *sp++; - g = *sp++; - b = *sp++; - - /* This looks real messy, but the compiler will reduce - * it down to a reasonable formula. For example, with - * 5 bits per color, we get: - * p = (((r >> 3) & 0x1f) << 10) | - * (((g >> 3) & 0x1f) << 5) | - * ((b >> 3) & 0x1f); - */ - p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) & - ((1 << PNG_QUANTIZE_RED_BITS) - 1)) << - (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) | - (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) & - ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) << - (PNG_QUANTIZE_BLUE_BITS)) | - ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) & - ((1 << PNG_QUANTIZE_BLUE_BITS) - 1)); - - *dp++ = palette_lookup[p]; - } - - row_info->color_type = PNG_COLOR_TYPE_PALETTE; - row_info->channels = 1; - row_info->pixel_depth = row_info->bit_depth; - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); - } - - else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && - palette_lookup != NULL) - { - int r, g, b, p; - sp = row; - dp = row; - for (i = 0; i < row_width; i++) - { - r = *sp++; - g = *sp++; - b = *sp++; - sp++; - - p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) & - ((1 << PNG_QUANTIZE_RED_BITS) - 1)) << - (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) | - (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) & - ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) << - (PNG_QUANTIZE_BLUE_BITS)) | - ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) & - ((1 << PNG_QUANTIZE_BLUE_BITS) - 1)); - - *dp++ = palette_lookup[p]; - } - - row_info->color_type = PNG_COLOR_TYPE_PALETTE; - row_info->channels = 1; - row_info->pixel_depth = row_info->bit_depth; - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); - } - - else if (row_info->color_type == PNG_COLOR_TYPE_PALETTE && - quantize_lookup) - { - sp = row; - - for (i = 0; i < row_width; i++, sp++) - { - *sp = quantize_lookup[*sp]; - } - } - } -} -#endif /* PNG_READ_QUANTIZE_SUPPORTED */ - -/* Transform the row. The order of transformations is significant, - * and is very touchy. If you add a transformation, take care to - * decide how it fits in with the other transformations here. - */ -void /* PRIVATE */ -png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info) -{ - png_debug(1, "in png_do_read_transformations"); - - if (png_ptr->row_buf == NULL) - { - /* Prior to 1.5.4 this output row/pass where the NULL pointer is, but this - * error is incredibly rare and incredibly easy to debug without this - * information. - */ - png_error(png_ptr, "NULL row buffer"); - } - - /* The following is debugging; prior to 1.5.4 the code was never compiled in; - * in 1.5.4 PNG_FLAG_DETECT_UNINITIALIZED was added and the macro - * PNG_WARN_UNINITIALIZED_ROW removed. In 1.6 the new flag is set only for - * all transformations, however in practice the ROW_INIT always gets done on - * demand, if necessary. - */ - if ((png_ptr->flags & PNG_FLAG_DETECT_UNINITIALIZED) != 0 && - !(png_ptr->flags & PNG_FLAG_ROW_INIT)) - { - /* Application has failed to call either png_read_start_image() or - * png_read_update_info() after setting transforms that expand pixels. - * This check added to libpng-1.2.19 (but not enabled until 1.5.4). - */ - png_error(png_ptr, "Uninitialized row"); - } - -#ifdef PNG_READ_EXPAND_SUPPORTED - if (png_ptr->transformations & PNG_EXPAND) - { - if (row_info->color_type == PNG_COLOR_TYPE_PALETTE) - { - png_do_expand_palette(row_info, png_ptr->row_buf + 1, - png_ptr->palette, png_ptr->trans_alpha, png_ptr->num_trans); - } - - else - { - if (png_ptr->num_trans && - (png_ptr->transformations & PNG_EXPAND_tRNS)) - png_do_expand(row_info, png_ptr->row_buf + 1, - &(png_ptr->trans_color)); - - else - png_do_expand(row_info, png_ptr->row_buf + 1, - NULL); - } - } -#endif - -#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED - if ((png_ptr->transformations & PNG_STRIP_ALPHA) && - !(png_ptr->transformations & PNG_COMPOSE) && - (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA || - row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) - png_do_strip_channel(row_info, png_ptr->row_buf + 1, - 0 /* at_start == false, because SWAP_ALPHA happens later */); -#endif - -#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED - if (png_ptr->transformations & PNG_RGB_TO_GRAY) - { - int rgb_error = - png_do_rgb_to_gray(png_ptr, row_info, - png_ptr->row_buf + 1); - - if (rgb_error != 0) - { - png_ptr->rgb_to_gray_status=1; - if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == - PNG_RGB_TO_GRAY_WARN) - png_warning(png_ptr, "png_do_rgb_to_gray found nongray pixel"); - - if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == - PNG_RGB_TO_GRAY_ERR) - png_error(png_ptr, "png_do_rgb_to_gray found nongray pixel"); - } - } -#endif - -/* From Andreas Dilger e-mail to png-implement, 26 March 1998: - * - * In most cases, the "simple transparency" should be done prior to doing - * gray-to-RGB, or you will have to test 3x as many bytes to check if a - * pixel is transparent. You would also need to make sure that the - * transparency information is upgraded to RGB. - * - * To summarize, the current flow is: - * - Gray + simple transparency -> compare 1 or 2 gray bytes and composite - * with background "in place" if transparent, - * convert to RGB if necessary - * - Gray + alpha -> composite with gray background and remove alpha bytes, - * convert to RGB if necessary - * - * To support RGB backgrounds for gray images we need: - * - Gray + simple transparency -> convert to RGB + simple transparency, - * compare 3 or 6 bytes and composite with - * background "in place" if transparent - * (3x compare/pixel compared to doing - * composite with gray bkgrnd) - * - Gray + alpha -> convert to RGB + alpha, composite with background and - * remove alpha bytes (3x float - * operations/pixel compared with composite - * on gray background) - * - * Greg's change will do this. The reason it wasn't done before is for - * performance, as this increases the per-pixel operations. If we would check - * in advance if the background was gray or RGB, and position the gray-to-RGB - * transform appropriately, then it would save a lot of work/time. - */ - -#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED - /* If gray -> RGB, do so now only if background is non-gray; else do later - * for performance reasons - */ - if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && - !(png_ptr->flags & PNG_FLAG_BACKGROUND_IS_GRAY)) - png_do_gray_to_rgb(row_info, png_ptr->row_buf + 1); -#endif - -#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) - if (png_ptr->transformations & PNG_COMPOSE) - png_do_compose(row_info, png_ptr->row_buf + 1, png_ptr); -#endif - -#ifdef PNG_READ_GAMMA_SUPPORTED - if ((png_ptr->transformations & PNG_GAMMA) && -#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED - /* Because RGB_TO_GRAY does the gamma transform. */ - !(png_ptr->transformations & PNG_RGB_TO_GRAY) && -#endif -#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) - /* Because PNG_COMPOSE does the gamma transform if there is something to - * do (if there is an alpha channel or transparency.) - */ - !((png_ptr->transformations & PNG_COMPOSE) && - ((png_ptr->num_trans != 0) || - (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) && -#endif - /* Because png_init_read_transformations transforms the palette, unless - * RGB_TO_GRAY will do the transform. - */ - (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)) - png_do_gamma(row_info, png_ptr->row_buf + 1, png_ptr); -#endif - -#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED - if ((png_ptr->transformations & PNG_STRIP_ALPHA) && - (png_ptr->transformations & PNG_COMPOSE) && - (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA || - row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) - png_do_strip_channel(row_info, png_ptr->row_buf + 1, - 0 /* at_start == false, because SWAP_ALPHA happens later */); -#endif - -#ifdef PNG_READ_ALPHA_MODE_SUPPORTED - if ((png_ptr->transformations & PNG_ENCODE_ALPHA) && - (row_info->color_type & PNG_COLOR_MASK_ALPHA)) - png_do_encode_alpha(row_info, png_ptr->row_buf + 1, png_ptr); -#endif - -#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED - if (png_ptr->transformations & PNG_SCALE_16_TO_8) - png_do_scale_16_to_8(row_info, png_ptr->row_buf + 1); -#endif - -#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED - /* There is no harm in doing both of these because only one has any effect, - * by putting the 'scale' option first if the app asks for scale (either by - * calling the API or in a TRANSFORM flag) this is what happens. - */ - if (png_ptr->transformations & PNG_16_TO_8) - png_do_chop(row_info, png_ptr->row_buf + 1); -#endif - -#ifdef PNG_READ_QUANTIZE_SUPPORTED - if (png_ptr->transformations & PNG_QUANTIZE) - { - png_do_quantize(row_info, png_ptr->row_buf + 1, - png_ptr->palette_lookup, png_ptr->quantize_index); - - if (row_info->rowbytes == 0) - png_error(png_ptr, "png_do_quantize returned rowbytes=0"); - } -#endif /* PNG_READ_QUANTIZE_SUPPORTED */ - -#ifdef PNG_READ_EXPAND_16_SUPPORTED - /* Do the expansion now, after all the arithmetic has been done. Notice - * that previous transformations can handle the PNG_EXPAND_16 flag if this - * is efficient (particularly true in the case of gamma correction, where - * better accuracy results faster!) - */ - if (png_ptr->transformations & PNG_EXPAND_16) - png_do_expand_16(row_info, png_ptr->row_buf + 1); -#endif - -#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED - /* NOTE: moved here in 1.5.4 (from much later in this list.) */ - if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && - (png_ptr->flags & PNG_FLAG_BACKGROUND_IS_GRAY)) - png_do_gray_to_rgb(row_info, png_ptr->row_buf + 1); -#endif - -#ifdef PNG_READ_INVERT_SUPPORTED - if (png_ptr->transformations & PNG_INVERT_MONO) - png_do_invert(row_info, png_ptr->row_buf + 1); -#endif - -#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED - if (png_ptr->transformations & PNG_INVERT_ALPHA) - png_do_read_invert_alpha(row_info, png_ptr->row_buf + 1); -#endif - -#ifdef PNG_READ_SHIFT_SUPPORTED - if (png_ptr->transformations & PNG_SHIFT) - png_do_unshift(row_info, png_ptr->row_buf + 1, - &(png_ptr->shift)); -#endif - -#ifdef PNG_READ_PACK_SUPPORTED - if (png_ptr->transformations & PNG_PACK) - png_do_unpack(row_info, png_ptr->row_buf + 1); -#endif - -#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED - /* Added at libpng-1.5.10 */ - if (row_info->color_type == PNG_COLOR_TYPE_PALETTE && - png_ptr->num_palette_max >= 0) - png_do_check_palette_indexes(png_ptr, row_info); -#endif - -#ifdef PNG_READ_BGR_SUPPORTED - if (png_ptr->transformations & PNG_BGR) - png_do_bgr(row_info, png_ptr->row_buf + 1); -#endif - -#ifdef PNG_READ_PACKSWAP_SUPPORTED - if (png_ptr->transformations & PNG_PACKSWAP) - png_do_packswap(row_info, png_ptr->row_buf + 1); -#endif - -#ifdef PNG_READ_FILLER_SUPPORTED - if (png_ptr->transformations & PNG_FILLER) - png_do_read_filler(row_info, png_ptr->row_buf + 1, - (png_uint_32)png_ptr->filler, png_ptr->flags); -#endif - -#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED - if (png_ptr->transformations & PNG_SWAP_ALPHA) - png_do_read_swap_alpha(row_info, png_ptr->row_buf + 1); -#endif - -#ifdef PNG_READ_16BIT_SUPPORTED -#ifdef PNG_READ_SWAP_SUPPORTED - if (png_ptr->transformations & PNG_SWAP_BYTES) - png_do_swap(row_info, png_ptr->row_buf + 1); -#endif -#endif - -#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED - if (png_ptr->transformations & PNG_USER_TRANSFORM) - { - if (png_ptr->read_user_transform_fn != NULL) - (*(png_ptr->read_user_transform_fn)) /* User read transform function */ - (png_ptr, /* png_ptr */ - row_info, /* row_info: */ - /* png_uint_32 width; width of row */ - /* png_size_t rowbytes; number of bytes in row */ - /* png_byte color_type; color type of pixels */ - /* png_byte bit_depth; bit depth of samples */ - /* png_byte channels; number of channels (1-4) */ - /* png_byte pixel_depth; bits per pixel (depth*channels) */ - png_ptr->row_buf + 1); /* start of pixel data for row */ -#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED - if (png_ptr->user_transform_depth) - row_info->bit_depth = png_ptr->user_transform_depth; - - if (png_ptr->user_transform_channels) - row_info->channels = png_ptr->user_transform_channels; -#endif - row_info->pixel_depth = (png_byte)(row_info->bit_depth * - row_info->channels); - - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_info->width); - } -#endif -} -#endif /* PNG_READ_TRANSFORMS_SUPPORTED */ -#endif /* PNG_READ_SUPPORTED */ +#endif /* READ_TRANSFORMS */ diff --git a/ext/libpng17/pngrutil.c b/ext/libpng17/pngrutil.c index 2a9fc896bf..5f94385545 100644 --- a/ext/libpng17/pngrutil.c +++ b/ext/libpng17/pngrutil.c @@ -1,8 +1,12 @@ +#ifdef _MSC_VER +#pragma warning (disable:4018) +#pragma warning (disable:4146) +#endif /* pngrutil.c - utilities to read a PNG file * * Last changed in libpng 1.7.0 [(PENDING RELEASE)] - * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -15,6 +19,7 @@ */ #include "pngpriv.h" +#define PNG_SRC_FILE PNG_SRC_FILE_pngrutil #ifdef PNG_READ_SUPPORTED @@ -76,7 +81,7 @@ png_get_uint_16)(png_const_bytep buf) { return PNG_U16(buf[0], buf[1]); } -#endif /* PNG_READ_INT_FUNCTIONS_SUPPORTED */ +#endif /* READ_INT_FUNCTIONS */ /* This is an exported function however its error handling is too harsh for most * internal use. For example if it were used for reading the chunk parameters @@ -126,48 +131,9 @@ png_read_sig(png_structrp png_ptr, png_inforp info_ptr) png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; } -/* Read the chunk header (length + type name). - * Put the type name into png_ptr->chunk_name, and return the length. - */ -png_uint_32 /* PRIVATE */ -png_read_chunk_header(png_structrp png_ptr) -{ - png_byte buf[8]; - png_uint_32 length; - -#ifdef PNG_IO_STATE_SUPPORTED - png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_HDR; -#endif - - /* Read the length and the chunk name. - * This must be performed in a single I/O call. - */ - png_read_data(png_ptr, buf, 8); - length = png_get_uint_31(png_ptr, buf); - - /* Put the chunk name into png_ptr->chunk_name. */ - png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(buf+4); - - png_debug2(0, "Reading %lx chunk, length = %lu", - (unsigned long)png_ptr->chunk_name, (unsigned long)length); - - /* Reset the crc and run it over the chunk name. */ - png_reset_crc(png_ptr); - png_calculate_crc(png_ptr, buf + 4, 4); - - /* Check to see if chunk name is valid. */ - png_check_chunk_name(png_ptr, png_ptr->chunk_name); - -#ifdef PNG_IO_STATE_SUPPORTED - png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_DATA; -#endif - - return length; -} - /* Read data, and (optionally) run it through the CRC. */ void /* PRIVATE */ -png_crc_read(png_structrp png_ptr, png_bytep buf, png_uint_32 length) +png_crc_read(png_structrp png_ptr, png_voidp buf, png_uint_32 length) { if (png_ptr == NULL) return; @@ -176,10 +142,10 @@ png_crc_read(png_structrp png_ptr, png_bytep buf, png_uint_32 length) png_calculate_crc(png_ptr, buf, length); } -/* Optionally skip data and then check the CRC. Depending on whether we - * are reading an ancillary or critical chunk, and how the program has set - * things up, we may calculate the CRC on the data and print a message. - * Returns '1' if there was a CRC error, '0' otherwise. +/* Optionally skip data and then check the CRC. Depending on whether we are + * reading an ancillary or critical chunk, and how the program has set things + * up, we may calculate the CRC on the data and print a message. Returns true + * if the chunk should be discarded, otherwise false. */ int /* PRIVATE */ png_crc_finish(png_structrp png_ptr, png_uint_32 skip) @@ -200,62 +166,37 @@ png_crc_finish(png_structrp png_ptr, png_uint_32 skip) png_crc_read(png_ptr, tmpbuf, len); } - if (png_crc_error(png_ptr)) + /* Compare the CRC stored in the PNG file with that calculated by libpng from + * the data it has read thus far. Do any required error handling. The + * second parameter is to allow a critical chunk (specifically PLTE) to be + * treated as ancillary. + */ { - if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) ? - !(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) : - (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE)) + png_byte crc_bytes[4]; + +# ifdef PNG_IO_STATE_SUPPORTED + png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_CRC; +# endif + + png_read_data(png_ptr, crc_bytes, 4); + + if (png_ptr->current_crc != crc_quiet_use && + png_get_uint_32(crc_bytes) != png_ptr->crc) { - png_chunk_warning(png_ptr, "CRC error"); + if (png_ptr->current_crc == crc_error_quit) + png_chunk_error(png_ptr, "CRC"); + + else + png_chunk_warning(png_ptr, "CRC"); + + /* The only way to discard a chunk at present is to issue a warning. + * TODO: quiet_discard. + */ + return png_ptr->current_crc == crc_warn_discard; } - - else - png_chunk_error(png_ptr, "CRC error"); - - return (1); } - return (0); -} - -/* Compare the CRC stored in the PNG file with that calculated by libpng from - * the data it has read thus far. - */ -int /* PRIVATE */ -png_crc_error(png_structrp png_ptr) -{ - png_byte crc_bytes[4]; - png_uint_32 crc; - int need_crc = 1; - - if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name)) - { - if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == - (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) - need_crc = 0; - } - - else /* critical */ - { - if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) - need_crc = 0; - } - -#ifdef PNG_IO_STATE_SUPPORTED - png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_CRC; -#endif - - /* The chunk CRC must be serialized in a single I/O call. */ - png_read_data(png_ptr, crc_bytes, 4); - - if (need_crc != 0) - { - crc = png_get_uint_32(crc_bytes); - return ((int)(crc != png_ptr->crc)); - } - - else - return (0); + return 0; } #if defined(PNG_READ_iCCP_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) ||\ @@ -268,14 +209,13 @@ png_crc_error(png_structrp png_ptr) * it will call png_error (via png_malloc) on failure. (warn == 2 means * 'silent'). */ -static png_bytep +png_bytep /* PRIVATE */ png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size, int warn) { png_bytep buffer = png_ptr->read_buffer; if (buffer != NULL && new_size > png_ptr->read_buffer_size) { - png_ptr->read_buffer = NULL; png_ptr->read_buffer = NULL; png_ptr->read_buffer_size = 0; png_free(png_ptr, buffer); @@ -295,16 +235,16 @@ png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size, int warn) else if (warn < 2) /* else silent */ { if (warn != 0) - png_chunk_warning(png_ptr, "insufficient memory to read chunk"); + png_chunk_warning(png_ptr, "insufficient memory to read chunk"); else - png_chunk_error(png_ptr, "insufficient memory to read chunk"); + png_chunk_error(png_ptr, "insufficient memory to read chunk"); } } return buffer; } -#endif /* PNG_READ_iCCP|iTXt|pCAL|sCAL|sPLT|tEXt|zTXt|SEQUENTIAL_READ */ +#endif /* READ_iCCP|iTXt|pCAL|sCAL|sPLT|tEXt|zTXt|SEQUENTIAL_READ */ /* png_inflate_claim: claim the zstream for some nefarious purpose that involves * decompression. Returns Z_OK on success, else a zlib error code. It checks @@ -324,12 +264,12 @@ png_inflate_claim(png_structrp png_ptr, png_uint_32 owner) * are minimal. */ (void)png_safecat(msg, (sizeof msg), 4, " using zstream"); -# if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC - png_chunk_warning(png_ptr, msg); - png_ptr->zowner = 0; -# else - png_chunk_error(png_ptr, msg); -# endif +#if PNG_RELEASE_BUILD + png_chunk_warning(png_ptr, msg); + png_ptr->zowner = 0; +#else + png_chunk_error(png_ptr, msg); +#endif } /* Implementation note: unlike 'png_deflate_claim' this internal function @@ -347,22 +287,22 @@ png_inflate_claim(png_structrp png_ptr, png_uint_32 owner) */ { int ret; /* zlib return code */ -# if PNG_ZLIB_VERNUM >= 0x1240 +#if ZLIB_VERNUM >= 0x1240 + int window_bits = 0; -# if defined(PNG_SET_OPTION_SUPPORTED) && \ - defined(PNG_MAXIMUM_INFLATE_WINDOW) - int window_bits; +# if defined(PNG_SET_OPTION_SUPPORTED) && \ + defined(PNG_MAXIMUM_INFLATE_WINDOW) - if (((png_ptr->options >> PNG_MAXIMUM_INFLATE_WINDOW) & 3) == - PNG_OPTION_ON) + if (png_ptr->maximum_inflate_window) window_bits = 15; - else - window_bits = 0; -# else -# define window_bits 0 -# endif -# endif +# endif +#endif /* ZLIB_VERNUM >= 0x1240 */ + + /* Initialize the alloc/free callbacks every time: */ + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = png_ptr; /* Set this for safety, just in case the previous owner left pointers to * memory allocations. @@ -372,32 +312,44 @@ png_inflate_claim(png_structrp png_ptr, png_uint_32 owner) png_ptr->zstream.next_out = NULL; png_ptr->zstream.avail_out = 0; - if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) + /* If png_struct::zstream has been used before for decompression it does + * not need to be re-initialized, just reset. + */ + if (png_ptr->zstream.state != NULL) { -# if PNG_ZLIB_VERNUM < 0x1240 - ret = inflateReset(&png_ptr->zstream); -# else - ret = inflateReset2(&png_ptr->zstream, window_bits); -# endif +#if ZLIB_VERNUM >= 0x1240 + ret = inflateReset2(&png_ptr->zstream, window_bits); +#else + ret = inflateReset(&png_ptr->zstream); +#endif } else { -# if PNG_ZLIB_VERNUM < 0x1240 - ret = inflateInit(&png_ptr->zstream); -# else - ret = inflateInit2(&png_ptr->zstream, window_bits); -# endif - - if (ret == Z_OK) - png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED; +#if ZLIB_VERNUM >= 0x1240 + ret = inflateInit2(&png_ptr->zstream, window_bits); +#else + ret = inflateInit(&png_ptr->zstream); +#endif } - if (ret == Z_OK) +#if ZLIB_VERNUM >= 0x1240 + /* Turn off validation of the ADLER32 checksum */ + if (png_ptr->current_crc == crc_quiet_use) + ret = inflateReset2(&png_ptr->zstream, -window_bits); +#endif + + if (ret == Z_OK && png_ptr->zstream.state != NULL) + { png_ptr->zowner = owner; + png_ptr->zstream_ended = 0; + } else - png_zstream_error(png_ptr, ret); + { + png_zstream_error(&png_ptr->zstream, ret); + png_ptr->zstream_ended = 1; + } return ret; } @@ -407,28 +359,27 @@ png_inflate_claim(png_structrp png_ptr, png_uint_32 owner) # endif } -#ifdef PNG_READ_COMPRESSED_TEXT_SUPPORTED -/* png_inflate now returns zlib error codes including Z_OK and Z_STREAM_END to - * allow the caller to do multiple calls if required. If the 'finish' flag is - * set Z_FINISH will be passed to the final inflate() call and Z_STREAM_END must - * be returned or there has been a problem, otherwise Z_SYNC_FLUSH is used and - * Z_OK or Z_STREAM_END will be returned on success. +/* This is a wrapper for the zlib deflate call which will handle larger buffer + * sizes than uInt. The input is limited to png_uint_32, because invariably + * the input comes from a chunk which has a 31-bit length, the output can be + * anything that fits in a png_alloc_size_t. * - * The input and output sizes are updated to the actual amounts of data consumed - * or written, not the amount available (as in a z_stream). The data pointers - * are not changed, so the next input is (data+input_size) and the next - * available output is (output+output_size). + * This internal function sets png_struct::zstream_ended when the end of the + * decoded data has been encountered; this includes both a normal end and + * error conditions. */ static int -png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish, - /* INPUT: */ png_const_bytep input, png_uint_32p input_size_ptr, - /* OUTPUT: */ png_bytep output, png_alloc_size_t *output_size_ptr) +png_zlib_inflate(png_structrp png_ptr, png_uint_32 owner, int finish, + /* INPUT: */ png_const_bytep *next_in_ptr, png_uint_32p avail_in_ptr, + /* OUTPUT: */ png_bytep *next_out_ptr, png_alloc_size_t *avail_out_ptr) { if (png_ptr->zowner == owner) /* Else not claimed */ { int ret; - png_alloc_size_t avail_out = *output_size_ptr; - png_uint_32 avail_in = *input_size_ptr; + png_alloc_size_t avail_out = *avail_out_ptr; + png_uint_32 avail_in = *avail_in_ptr; + png_bytep output = *next_out_ptr; + png_const_bytep input = *next_in_ptr; /* zlib can't necessarily handle more than 65535 bytes at once (i.e. it * can't even necessarily handle 65536 bytes) because the type uInt is @@ -464,7 +415,6 @@ png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish, * every ZLIB_IO_MAX input bytes. */ avail_in += png_ptr->zstream.avail_in; /* not consumed last time */ - avail = ZLIB_IO_MAX; if (avail_in < avail) @@ -475,7 +425,6 @@ png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish, /* zlib OUTPUT BUFFER */ avail_out += png_ptr->zstream.avail_out; /* not written last time */ - avail = ZLIB_IO_MAX; /* maximum zlib can process */ if (output == NULL) @@ -501,7 +450,7 @@ png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish, * end of the output buffer. */ ret = inflate(&png_ptr->zstream, avail_out > 0 ? Z_NO_FLUSH : - (finish ? Z_FINISH : Z_SYNC_FLUSH)); + (finish ? Z_FINISH : Z_SYNC_FLUSH)); } while (ret == Z_OK); /* For safety kill the local buffer pointer now */ @@ -515,14 +464,21 @@ png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish, /* Update the input and output sizes; the updated values are the amount * consumed or written, effectively the inverse of what zlib uses. */ - if (avail_out > 0) - *output_size_ptr -= avail_out; + *avail_out_ptr = avail_out; + if (output != NULL) + *next_out_ptr = png_ptr->zstream.next_out; - if (avail_in > 0) - *input_size_ptr -= avail_in; + *avail_in_ptr = avail_in; + *next_in_ptr = png_ptr->zstream.next_in; - /* Ensure png_ptr->zstream.msg is set (even in the success case!) */ - png_zstream_error(png_ptr, ret); + /* Ensure png_ptr->zstream.msg is set, ret can't be Z_OK at this point. + */ + debug(ret != Z_OK); + + if (ret != Z_BUF_ERROR) + png_ptr->zstream_ended = 1; + + png_zstream_error(&png_ptr->zstream, ret); return ret; } @@ -530,15 +486,44 @@ png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish, { /* This is a bad internal error. The recovery assigns to the zstream msg * pointer, which is not owned by the caller, but this is safe; it's only - * used on errors! + * used on errors! (The {next,avail}_{in,out} values are not changed.) */ png_ptr->zstream.msg = PNGZ_MSG_CAST("zstream unclaimed"); return Z_STREAM_ERROR; } } -/* - * Decompress trailing data in a chunk. The assumption is that read_buffer +#ifdef PNG_READ_COMPRESSED_TEXT_SUPPORTED +/* png_inflate now returns zlib error codes including Z_OK and Z_STREAM_END to + * allow the caller to do multiple calls if required. If the 'finish' flag is + * set Z_FINISH will be passed to the final inflate() call and Z_STREAM_END must + * be returned or there has been a problem, otherwise Z_SYNC_FLUSH is used and + * Z_OK or Z_STREAM_END will be returned on success. + * + * The input and output sizes are updated to the actual amounts of data consumed + * or written, not the amount available (as in a z_stream). The data pointers + * are not changed, so the next input is (data+input_size) and the next + * available output is (output+output_size). + */ +static int +png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish, + /* INPUT: */ png_const_bytep input, png_uint_32p input_size_ptr, + /* OUTPUT: */ png_bytep output, png_alloc_size_t *output_size_ptr) +{ + png_uint_32 avail_in = *input_size_ptr; + png_alloc_size_t avail_out = *output_size_ptr; + int ret = png_zlib_inflate(png_ptr, owner, finish, + &input, &avail_in, &output, &avail_out); + + /* And implement the non-zlib semantics (the size values are updated to the + * amounts consumed and written, not the amount remaining.) + */ + *input_size_ptr -= avail_in; + *output_size_ptr -= avail_out; + return ret; +} + +/* Decompress trailing data in a chunk. The assumption is that read_buffer * points at an allocated area holding the contents of a chunk with a * trailing compressed part. What we get back is an allocated area * holding the original prefix part and an uncompressed version of the @@ -546,9 +531,9 @@ png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish, */ static int png_decompress_chunk(png_structrp png_ptr, - png_uint_32 chunklength, png_uint_32 prefix_size, - png_alloc_size_t *newlength /* must be initialized to the maximum! */, - int terminate /*add a '\0' to the end of the uncompressed data*/) + png_uint_32 chunklength, png_uint_32 prefix_size, + png_alloc_size_t *newlength /* must be initialized to the maximum! */, + int terminate /*add a '\0' to the end of the uncompressed data*/) { /* TODO: implement different limits for different types of chunk. * @@ -559,14 +544,14 @@ png_decompress_chunk(png_structrp png_ptr, */ png_alloc_size_t limit = PNG_SIZE_MAX; -# ifdef PNG_SET_USER_LIMITS_SUPPORTED - if (png_ptr->user_chunk_malloc_max > 0 && - png_ptr->user_chunk_malloc_max < limit) - limit = png_ptr->user_chunk_malloc_max; -# elif PNG_USER_CHUNK_MALLOC_MAX > 0 - if (PNG_USER_CHUNK_MALLOC_MAX < limit) - limit = PNG_USER_CHUNK_MALLOC_MAX; -# endif +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_malloc_max > 0 && + png_ptr->user_chunk_malloc_max < limit) + limit = png_ptr->user_chunk_malloc_max; +#elif PNG_USER_CHUNK_MALLOC_MAX > 0 + if (PNG_USER_CHUNK_MALLOC_MAX < limit) + limit = PNG_USER_CHUNK_MALLOC_MAX; +#endif if (limit >= prefix_size + (terminate != 0)) { @@ -585,8 +570,8 @@ png_decompress_chunk(png_structrp png_ptr, png_uint_32 lzsize = chunklength - prefix_size; ret = png_inflate(png_ptr, png_ptr->chunk_name, 1/*finish*/, - /* input: */ png_ptr->read_buffer + prefix_size, &lzsize, - /* output: */ NULL, newlength); + /* input: */ png_ptr->read_buffer + prefix_size, &lzsize, + /* output: */ NULL, newlength); if (ret == Z_STREAM_END) { @@ -606,15 +591,15 @@ png_decompress_chunk(png_structrp png_ptr, */ png_alloc_size_t new_size = *newlength; png_alloc_size_t buffer_size = prefix_size + new_size + - (terminate != 0); + (terminate != 0); png_bytep text = png_voidcast(png_bytep, png_malloc_base(png_ptr, - buffer_size)); + buffer_size)); if (text != NULL) { ret = png_inflate(png_ptr, png_ptr->chunk_name, 1/*finish*/, - png_ptr->read_buffer + prefix_size, &lzsize, - text + prefix_size, newlength); + png_ptr->read_buffer + prefix_size, &lzsize, + text + prefix_size, newlength); if (ret == Z_STREAM_END) { @@ -659,7 +644,7 @@ png_decompress_chunk(png_structrp png_ptr, * the extra space may otherwise be used as a Trojan Horse. */ if (ret == Z_STREAM_END && - chunklength - prefix_size != lzsize) + chunklength - prefix_size != lzsize) png_chunk_benign_error(png_ptr, "extra compressed data"); } @@ -667,14 +652,14 @@ png_decompress_chunk(png_structrp png_ptr, { /* Out of memory allocating the buffer */ ret = Z_MEM_ERROR; - png_zstream_error(png_ptr, Z_MEM_ERROR); + png_zstream_error(&png_ptr->zstream, Z_MEM_ERROR); } } else { /* inflateReset failed, store the error message */ - png_zstream_error(png_ptr, ret); + png_zstream_error(&png_ptr->zstream, ret); if (ret == Z_STREAM_END) ret = PNG_UNEXPECTED_ZLIB_RETURN; @@ -697,11 +682,11 @@ png_decompress_chunk(png_structrp png_ptr, else { /* Application/configuration limits exceeded */ - png_zstream_error(png_ptr, Z_MEM_ERROR); + png_zstream_error(&png_ptr->zstream, Z_MEM_ERROR); return Z_MEM_ERROR; } } -#endif /* PNG_READ_COMPRESSED_TEXT_SUPPORTED */ +#endif /* READ_COMPRESSED_TEXT */ #ifdef PNG_READ_iCCP_SUPPORTED /* Perform a partial read and decompress, producing 'avail_out' bytes and @@ -709,8 +694,8 @@ png_decompress_chunk(png_structrp png_ptr, */ static int png_inflate_read(png_structrp png_ptr, png_bytep read_buffer, uInt read_size, - png_uint_32p chunk_bytes, png_bytep next_out, png_alloc_size_t *out_size, - int finish) + png_uint_32p chunk_bytes, png_bytep next_out, png_alloc_size_t *out_size, + int finish) { if (png_ptr->zowner == png_ptr->chunk_name) { @@ -750,7 +735,8 @@ png_inflate_read(png_structrp png_ptr, png_bytep read_buffer, uInt read_size, * streams. */ ret = inflate(&png_ptr->zstream, - *chunk_bytes > 0 ? Z_NO_FLUSH : (finish ? Z_FINISH : Z_SYNC_FLUSH)); + *chunk_bytes > 0 ? Z_NO_FLUSH : (finish ? Z_FINISH : + Z_SYNC_FLUSH)); } while (ret == Z_OK && (*out_size > 0 || png_ptr->zstream.avail_out > 0)); @@ -758,7 +744,7 @@ png_inflate_read(png_structrp png_ptr, png_bytep read_buffer, uInt read_size, png_ptr->zstream.avail_out = 0; /* Should not be required, but is safe */ /* Ensure the error message pointer is always set: */ - png_zstream_error(png_ptr, ret); + png_zstream_error(&png_ptr->zstream, ret); return ret; } @@ -768,27 +754,63 @@ png_inflate_read(png_structrp png_ptr, png_bytep read_buffer, uInt read_size, return Z_STREAM_ERROR; } } -#endif +#endif /* READ_iCCP */ + +/* Chunk handling error handlers and utilities: */ +/* Utility to read the chunk data from the start without processing it; + * a skip function. + */ +static void +png_handle_skip(png_structrp png_ptr) + /* Skip the entire chunk after the name,length header has been read: */ +{ + png_crc_finish(png_ptr, png_ptr->chunk_length); +} + +static void +png_handle_error(png_structrp png_ptr +# ifdef PNG_ERROR_TEXT_SUPPORTED + , png_const_charp error +# else +# define png_handle_error(pp,e) png_handle_error(pp) +# endif + ) + /* Handle an error detected immediately after the chunk header has been + * read; this skips the rest of the chunk data and the CRC then signals + * a *benign* chunk error. + */ +{ + png_handle_skip(png_ptr); + png_chunk_benign_error(png_ptr, error); +} + +#if defined (PNG_READ_gAMA_SUPPORTED) || defined (PNG_READ_sBIT_SUPPORTED) ||\ + defined (PNG_READ_cHRM_SUPPORTED) || defined (PNG_READ_sRGB_SUPPORTED) ||\ + defined (PNG_READ_iCCP_SUPPORTED) || defined (PNG_READ_tRNS_SUPPORTED) ||\ + defined (PNG_READ_bKGD_SUPPORTED) || defined (PNG_READ_hIST_SUPPORTED) ||\ + defined (PNG_READ_pHYs_SUPPORTED) || defined (PNG_READ_oFFs_SUPPORTED) ||\ + defined (PNG_READ_sCAL_SUPPORTED) || defined (PNG_READ_tIME_SUPPORTED) +static void +png_handle_bad_length(png_structrp png_ptr) +{ + png_handle_error(png_ptr, "invalid length"); +} +#endif /* chunks that can generate length errors */ /* Read and check the IDHR chunk */ -void /* PRIVATE */ -png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr) { png_byte buf[13]; png_uint_32 width, height; - int bit_depth, color_type, compression_type, filter_type; - int interlace_type; + png_byte bit_depth, color_type, compression_type, filter_method; + png_byte interlace_type; png_debug(1, "in png_handle_IHDR"); - if (png_ptr->mode & PNG_HAVE_IHDR) - png_chunk_error(png_ptr, "out of place"); - - /* Check the length */ - if (length != 13) - png_chunk_error(png_ptr, "invalid"); - - png_ptr->mode |= PNG_HAVE_IHDR; + /* Check the length (this is a chunk error; not benign) */ + if (png_ptr->chunk_length != 13) + png_chunk_error(png_ptr, "invalid length"); png_crc_read(png_ptr, buf, 13); png_crc_finish(png_ptr, 0); @@ -798,99 +820,45 @@ png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) bit_depth = buf[8]; color_type = buf[9]; compression_type = buf[10]; - filter_type = buf[11]; + filter_method = buf[11]; interlace_type = buf[12]; /* Set internal variables */ png_ptr->width = width; png_ptr->height = height; - png_ptr->bit_depth = (png_byte)bit_depth; - png_ptr->interlaced = (png_byte)interlace_type; - png_ptr->color_type = (png_byte)color_type; -#ifdef PNG_MNG_FEATURES_SUPPORTED - png_ptr->filter_type = (png_byte)filter_type; -#endif - png_ptr->compression_type = (png_byte)compression_type; + png_ptr->bit_depth = bit_depth; + png_ptr->interlaced = interlace_type; + png_ptr->color_type = color_type; + png_ptr->filter_method = filter_method; - /* Find number of channels */ - switch (png_ptr->color_type) - { - default: /* invalid, png_set_IHDR calls png_error */ - case PNG_COLOR_TYPE_GRAY: - case PNG_COLOR_TYPE_PALETTE: - png_ptr->channels = 1; - break; - - case PNG_COLOR_TYPE_RGB: - png_ptr->channels = 3; - break; - - case PNG_COLOR_TYPE_GRAY_ALPHA: - png_ptr->channels = 2; - break; - - case PNG_COLOR_TYPE_RGB_ALPHA: - png_ptr->channels = 4; - break; - } - - /* Set up other useful info */ - png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth * - png_ptr->channels); - png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->width); - png_debug1(3, "bit_depth = %d", png_ptr->bit_depth); - png_debug1(3, "channels = %d", png_ptr->channels); - png_debug1(3, "rowbytes = %lu", (unsigned long)png_ptr->rowbytes); png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, - color_type, interlace_type, compression_type, filter_type); + color_type, interlace_type, compression_type, filter_method); } /* Read and check the palette */ -void /* PRIVATE */ -png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr) { png_color palette[PNG_MAX_PALETTE_LENGTH]; - int num, i; -#ifdef PNG_POINTER_INDEXING_SUPPORTED - png_colorp pal_ptr; -#endif + png_uint_32 length = png_ptr->chunk_length; + png_uint_32 max_palette_length, num, i; png_debug(1, "in png_handle_PLTE"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - /* Moved to before the 'after IDAT' check below because otherwise duplicate - * PLTE chunks are potentially ignored (the spec says there shall not be more - * than one PLTE, the error is not treated as benign, so this check trumps - * the requirement that PLTE appears before IDAT.) - */ - else if (png_ptr->mode & PNG_HAVE_PLTE) - png_chunk_error(png_ptr, "duplicate"); - - else if (png_ptr->mode & PNG_HAVE_IDAT) - { - /* This is benign because the non-benign error happened before, when an - * IDAT was encountered in a color-mapped image with no PLTE. - */ - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); + if (info_ptr == NULL) return; - } - - png_ptr->mode |= PNG_HAVE_PLTE; if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "ignored in grayscale PNG"); + png_handle_error(png_ptr, "ignored in grayscale PNG"); return; } #ifndef PNG_READ_OPT_PLTE_SUPPORTED if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) { - png_crc_finish(png_ptr, length); + /* Skip the whole chunk: */ + png_handle_skip(png_ptr); return; } #endif @@ -898,93 +866,52 @@ png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3) { png_crc_finish(png_ptr, length); - - if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) - png_chunk_benign_error(png_ptr, "invalid"); - - else - png_chunk_error(png_ptr, "invalid"); - + png_chunk_report(png_ptr, "invalid length", + ((png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) ? PNG_CHUNK_ERROR : + PNG_CHUNK_FATAL)); return; } /* The cast is safe because 'length' is less than 3*PNG_MAX_PALETTE_LENGTH */ - num = (int)length / 3; + num = length / 3U; -#ifdef PNG_POINTER_INDEXING_SUPPORTED - for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++) + /* If the palette has 256 or fewer entries but is too large for the bit + * depth, we don't issue an error, to preserve the behavior of previous + * libpng versions. We silently truncate the unused extra palette entries + * here. + */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + max_palette_length = (1U << png_ptr->bit_depth); + else + max_palette_length = PNG_MAX_PALETTE_LENGTH; + + if (num > max_palette_length) + num = max_palette_length; + + for (i = 0; i < num; ++i) { png_byte buf[3]; png_crc_read(png_ptr, buf, 3); - pal_ptr->red = buf[0]; - pal_ptr->green = buf[1]; - pal_ptr->blue = buf[2]; - } -#else - for (i = 0; i < num; i++) - { - png_byte buf[3]; - - png_crc_read(png_ptr, buf, 3); - /* Don't depend upon png_color being any order */ palette[i].red = buf[0]; palette[i].green = buf[1]; palette[i].blue = buf[2]; } -#endif - /* If we actually need the PLTE chunk (ie for a paletted image), we do - * whatever the normal CRC configuration tells us. However, if we - * have an RGB image, the PLTE can be considered ancillary, so - * we will act as though it is. - */ -#ifndef PNG_READ_OPT_PLTE_SUPPORTED - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) -#endif - { - png_crc_finish(png_ptr, 0); - } - -#ifndef PNG_READ_OPT_PLTE_SUPPORTED - else if (png_crc_error(png_ptr)) /* Only if we have a CRC error */ - { - /* If we don't want to use the data from an ancillary chunk, - * we have two options: an error abort, or a warning and we - * ignore the data in this chunk (which should be OK, since - * it's considered ancillary for a RGB or RGBA image). - * - * IMPLEMENTATION NOTE: this is only here because png_crc_finish uses the - * chunk type to determine whether to check the ancillary or the critical - * flags. - */ - if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE)) - { - if (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) - return; - - else - png_chunk_error(png_ptr, "CRC error"); - } - - /* Otherwise, we (optionally) emit a warning and use the chunk. */ - else if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) - { - png_chunk_warning(png_ptr, "CRC error"); - } - } -#endif - - /* TODO: png_set_PLTE has the side effect of setting png_ptr->palette to its - * own copy of the palette. This has the side effect that when png_start_row - * is called (this happens after any call to png_read_update_info) the - * info_ptr palette gets changed. This is extremely unexpected and - * confusing. - * - * Fix this by not sharing the palette in this way. - */ + png_crc_finish(png_ptr, length - num * 3U); png_set_PLTE(png_ptr, info_ptr, palette, num); + /* Ok, make our own copy since the set succeeded: */ + debug(png_ptr->palette == NULL); /* should only get set once */ + png_ptr->palette = png_voidcast(png_colorp, png_malloc(png_ptr, + sizeof (png_color[PNG_MAX_PALETTE_LENGTH]))); + /* This works because we know png_set_PLTE also expands the palette to the + * full size: + */ + memcpy(png_ptr->palette, info_ptr->palette, + sizeof (png_color[PNG_MAX_PALETTE_LENGTH])); + png_ptr->num_palette = info_ptr->num_palette; + /* The three chunks, bKGD, hIST and tRNS *must* appear after PLTE and before * IDAT. Prior to 1.6.0 this was not checked; instead the code merely * checked the apparent validity of a tRNS chunk inserted before PLTE on a @@ -996,73 +923,56 @@ png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) */ #ifdef PNG_READ_tRNS_SUPPORTED if (png_ptr->num_trans > 0 || - (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS) != 0)) + (info_ptr->valid & PNG_INFO_tRNS) != 0) { /* Cancel this because otherwise it would be used if the transforms * require it. Don't cancel the 'valid' flag because this would prevent * detection of duplicate chunks. */ png_ptr->num_trans = 0; - - if (info_ptr != NULL) - info_ptr->num_trans = 0; + info_ptr->num_trans = 0; png_chunk_benign_error(png_ptr, "tRNS must be after"); } -#endif +#endif /* READ_tRNS */ #ifdef PNG_READ_hIST_SUPPORTED - if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) != 0) + if ((info_ptr->valid & PNG_INFO_hIST) != 0) png_chunk_benign_error(png_ptr, "hIST must be after"); -#endif +#endif /* READ_hIST */ #ifdef PNG_READ_bKGD_SUPPORTED - if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) != 0) + if ((info_ptr->valid & PNG_INFO_bKGD) != 0) png_chunk_benign_error(png_ptr, "bKGD must be after"); -#endif +#endif /* READ_bKGD */ } -void /* PRIVATE */ -png_handle_IEND(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_IEND(png_structrp png_ptr, png_inforp info_ptr) { png_debug(1, "in png_handle_IEND"); - if (!(png_ptr->mode & PNG_HAVE_IHDR) || !(png_ptr->mode & PNG_HAVE_IDAT)) - png_chunk_error(png_ptr, "out of place"); + png_crc_finish(png_ptr, png_ptr->chunk_length); - png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND); - - png_crc_finish(png_ptr, length); - - if (length != 0) - png_chunk_benign_error(png_ptr, "invalid"); + /* Treat this as benign and terminate the PNG anyway: */ + if (png_ptr->chunk_length != 0) + png_chunk_benign_error(png_ptr, "invalid length"); PNG_UNUSED(info_ptr) } #ifdef PNG_READ_gAMA_SUPPORTED -void /* PRIVATE */ -png_handle_gAMA(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_gAMA(png_structrp png_ptr, png_inforp info_ptr) { png_fixed_point igamma; png_byte buf[4]; png_debug(1, "in png_handle_gAMA"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) + if (png_ptr->chunk_length != 4) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - if (length != 4) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } @@ -1076,11 +986,13 @@ png_handle_gAMA(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_colorspace_set_gamma(png_ptr, &png_ptr->colorspace, igamma); png_colorspace_sync(png_ptr, info_ptr); } -#endif +#else +# define png_handle_gAMA NULL +#endif /* READ_gAMA */ #ifdef PNG_READ_sBIT_SUPPORTED -void /* PRIVATE */ -png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr) { unsigned int truelen, i; png_byte sample_depth; @@ -1088,20 +1000,9 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_sBIT"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT)) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); + png_handle_error(png_ptr, "duplicate"); return; } @@ -1113,14 +1014,14 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) else { - truelen = png_ptr->channels; + truelen = PNG_CHANNELS(*png_ptr); sample_depth = png_ptr->bit_depth; + affirm(truelen <= 4); } - if (length != truelen || length > 4) + if (png_ptr->chunk_length != truelen) { - png_chunk_benign_error(png_ptr, "invalid"); - png_crc_finish(png_ptr, length); + png_handle_bad_length(png_ptr); return; } @@ -1156,31 +1057,22 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit)); } -#endif +#else +# define png_handle_sBIT NULL +#endif /* READ_sBIT */ #ifdef PNG_READ_cHRM_SUPPORTED -void /* PRIVATE */ -png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr) { png_byte buf[32]; png_xy xy; png_debug(1, "in png_handle_cHRM"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) + if (png_ptr->chunk_length != 32) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - if (length != 32) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } @@ -1207,7 +1099,7 @@ png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) xy.bluex == PNG_FIXED_ERROR || xy.bluey == PNG_FIXED_ERROR) { - png_chunk_benign_error(png_ptr, "invalid values"); + png_chunk_benign_error(png_ptr, "invalid"); return; } @@ -1225,33 +1117,24 @@ png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; (void)png_colorspace_set_chromaticities(png_ptr, &png_ptr->colorspace, &xy, - 1/*prefer cHRM values*/); + 1/*prefer cHRM values*/); png_colorspace_sync(png_ptr, info_ptr); } -#endif +#else +# define png_handle_cHRM NULL +#endif /* READ_cHRM */ #ifdef PNG_READ_sRGB_SUPPORTED -void /* PRIVATE */ -png_handle_sRGB(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_sRGB(png_structrp png_ptr, png_inforp info_ptr) { png_byte intent; png_debug(1, "in png_handle_sRGB"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) + if (png_ptr->chunk_length != 1) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - if (length != 1) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } @@ -1278,45 +1161,37 @@ png_handle_sRGB(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) (void)png_colorspace_set_sRGB(png_ptr, &png_ptr->colorspace, intent); png_colorspace_sync(png_ptr, info_ptr); } -#endif /* PNG_READ_sRGB_SUPPORTED */ +#else +# define png_handle_sRGB NULL +#endif /* READ_sRGB */ #ifdef PNG_READ_iCCP_SUPPORTED -void /* PRIVATE */ -png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr) /* Note: this does not properly handle profiles that are > 64K under DOS */ { png_const_charp errmsg = NULL; /* error message output, or no error */ + png_uint_32 length = png_ptr->chunk_length; int finished = 0; /* crc checked */ png_debug(1, "in png_handle_iCCP"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - /* Consistent with all the above colorspace handling an obviously *invalid* * chunk is just ignored, so does not invalidate the color space. An * alternative is to set the 'invalid' flags at the start of this routine * and only clear them in they were not set before and all the tests pass. - * The minimum 'deflate' stream is assumed to be just the 2 byte header and 4 - * byte checksum. The keyword must be one character and there is a - * terminator (0) byte and the compression method. + * The minimum 'deflate' stream is assumed to be just the 2 byte header and + * 4 byte checksum. The keyword must be at least one character and there is + * a terminator (0) byte and the compression method. */ if (length < 9) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "too short"); + png_handle_bad_length(png_ptr); return; } /* If a colorspace error has already been output skip this chunk */ - if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) + if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0) { png_crc_finish(png_ptr, length); return; @@ -1335,7 +1210,7 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) */ read_length = 81; /* maximum */ if (read_length > length) - read_length = (uInt)length; + read_length = (uInt)/*SAFE*/length; png_crc_read(png_ptr, (png_bytep)keyword, read_length); length -= read_length; @@ -1365,8 +1240,8 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_ptr->zstream.next_in = (Bytef*)keyword + (keyword_length+2); png_ptr->zstream.avail_in = read_length; (void)png_inflate_read(png_ptr, local_buffer, - (sizeof local_buffer), &length, profile_header, &size, - 0/*finish: don't, because the output is too small*/); + (sizeof local_buffer), &length, profile_header, &size, + 0/*finish: don't, because the output is too small*/); if (size == 0) { @@ -1376,14 +1251,14 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_get_uint_32(profile_header); if (png_icc_check_length(png_ptr, &png_ptr->colorspace, - keyword, profile_length)) + keyword, profile_length)) { /* The length is apparently ok, so we can check the 132 * byte header. */ if (png_icc_check_header(png_ptr, &png_ptr->colorspace, - keyword, profile_length, profile_header, - png_ptr->color_type)) + keyword, profile_length, profile_header, + (png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)) { /* Now read the tag table; a variable size buffer is * needed at this point, allocate one for the whole @@ -1391,20 +1266,20 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) * that none of these stuff will overflow. */ const png_uint_32 tag_count = png_get_uint_32( - profile_header+128); + profile_header+128); png_bytep profile = png_read_buffer(png_ptr, - profile_length, 2/*silent*/); + profile_length, 2/*silent*/); if (profile != NULL) { memcpy(profile, profile_header, - (sizeof profile_header)); + (sizeof profile_header)); size = 12 * tag_count; (void)png_inflate_read(png_ptr, local_buffer, - (sizeof local_buffer), &length, - profile + (sizeof profile_header), &size, 0); + (sizeof local_buffer), &length, + profile + (sizeof profile_header), &size, 0); /* Still expect a buffer error because we expect * there to be some tag data! @@ -1412,22 +1287,26 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (size == 0) { if (png_icc_check_tag_table(png_ptr, - &png_ptr->colorspace, keyword, profile_length, - profile)) + &png_ptr->colorspace, keyword, profile_length, + profile)) { /* The profile has been validated for basic * security issues, so read the whole thing in. */ size = profile_length - (sizeof profile_header) - - 12 * tag_count; + - 12 * tag_count; (void)png_inflate_read(png_ptr, local_buffer, - (sizeof local_buffer), &length, - profile + (sizeof profile_header) + - 12 * tag_count, &size, 1/*finish*/); + (sizeof local_buffer), &length, + profile + (sizeof profile_header) + + 12 * tag_count, &size, 1/*finish*/); - if (length > 0 && !(png_ptr->flags & - PNG_FLAG_BENIGN_ERRORS_WARN)) + if (length > 0 +# ifdef PNG_BENIGN_READ_ERRORS_SUPPORTED + && png_ptr->benign_error_action == + PNG_ERROR +# endif /* BENIGN_READ_ERRORS */ + ) errmsg = "extra compressed data"; /* But otherwise allow extra data: */ @@ -1439,34 +1318,32 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) * keep going. */ png_chunk_warning(png_ptr, - "extra compressed data"); + "extra compressed data"); } png_crc_finish(png_ptr, length); finished = 1; -# ifdef PNG_sRGB_SUPPORTED +# if defined(PNG_sRGB_SUPPORTED) && PNG_sRGB_PROFILE_CHECKS >= 0 /* Check for a match against sRGB */ png_icc_set_sRGB(png_ptr, - &png_ptr->colorspace, profile, - png_ptr->zstream.adler); -# endif + &png_ptr->colorspace, profile, + png_ptr->zstream.adler); +# endif /* Steal the profile for info_ptr. */ if (info_ptr != NULL) { png_free_data(png_ptr, info_ptr, - PNG_FREE_ICCP, 0); + PNG_FREE_ICCP, 0); info_ptr->iccp_name = png_voidcast(char*, - png_malloc_base(png_ptr, - keyword_length+1)); + png_malloc_base(png_ptr, + keyword_length+1)); if (info_ptr->iccp_name != NULL) { memcpy(info_ptr->iccp_name, keyword, - keyword_length+1); - info_ptr->iccp_proflen = - profile_length; + keyword_length+1); info_ptr->iccp_profile = profile; png_ptr->read_buffer = NULL; /*steal*/ info_ptr->free_me |= PNG_FREE_ICCP; @@ -1476,7 +1353,7 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) else { png_ptr->colorspace.flags |= - PNG_COLORSPACE_INVALID; + PNG_COLORSPACE_INVALID; errmsg = "out of memory"; } } @@ -1498,9 +1375,6 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) else if (size > 0) errmsg = "truncated"; - - else - errmsg = png_ptr->zstream.msg; } /* else png_icc_check_tag_table output an error */ @@ -1551,13 +1425,16 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (errmsg != NULL) /* else already output */ png_chunk_benign_error(png_ptr, errmsg); } -#endif /* PNG_READ_iCCP_SUPPORTED */ +#else +# define png_handle_iCCP NULL +#endif /* READ_iCCP */ #ifdef PNG_READ_sPLT_SUPPORTED -void /* PRIVATE */ -png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr) /* Note: this does not properly handle chunks that are > 64K under DOS */ { + png_uint_32 length = png_ptr->chunk_length; png_bytep entry_start, buffer; png_sPLT_t new_palette; png_sPLT_entryp pp; @@ -1581,31 +1458,12 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (--png_ptr->user_chunk_cache_max == 1) { /* Warn the first time */ - png_chunk_benign_error(png_ptr, "chunk cache full"); + png_chunk_benign_error(png_ptr, "no space in chunk cache"); png_crc_finish(png_ptr, length); return; } } -#endif - - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (png_ptr->mode & PNG_HAVE_IDAT) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - -#ifdef PNG_MAX_MALLOC_64K - if (length > 65535U) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "too large to fit in memory"); - return; - } -#endif +#endif /* USER_LIMITS */ buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); if (buffer == NULL) @@ -1615,7 +1473,6 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) return; } - /* WARNING: this may break if size_t is less than 32 bits; it is assumed * that the PNG_MAX_MALLOC_64K test is enabled in this case, but this is a * potential breakage point if the types in pngconf.h aren't exactly right. @@ -1633,9 +1490,9 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) ++entry_start; /* A sample depth should follow the separator, and we should be on it */ - if (entry_start > buffer + length - 2) + if (length < 2U || entry_start > buffer + (length - 2U)) { - png_warning(png_ptr, "malformed sPLT chunk"); + png_chunk_benign_error(png_ptr, "malformed"); return; } @@ -1649,7 +1506,7 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) /* Integrity-check the data length */ if (data_length % entry_size) { - png_warning(png_ptr, "sPLT chunk has bad length"); + png_chunk_benign_error(png_ptr, "invalid length"); return; } @@ -1658,22 +1515,21 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (dl > max_dl) { - png_warning(png_ptr, "sPLT chunk too long"); + png_chunk_benign_error(png_ptr, "exceeds system limits"); return; } new_palette.nentries = (png_int_32)(data_length / entry_size); - new_palette.entries = (png_sPLT_entryp)png_malloc_warn( - png_ptr, new_palette.nentries * (sizeof (png_sPLT_entry))); + new_palette.entries = png_voidcast(png_sPLT_entryp, png_malloc_base( + png_ptr, new_palette.nentries * (sizeof (png_sPLT_entry)))); if (new_palette.entries == NULL) { - png_warning(png_ptr, "sPLT chunk requires too much memory"); + png_chunk_benign_error(png_ptr, "out of memory"); return; } -#ifdef PNG_POINTER_INDEXING_SUPPORTED for (i = 0; i < new_palette.nentries; i++) { pp = new_palette.entries + i; @@ -1696,31 +1552,6 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) pp->frequency = png_get_uint_16(entry_start); entry_start += 2; } -#else - pp = new_palette.entries; - - for (i = 0; i < new_palette.nentries; i++) - { - - if (new_palette.depth == 8) - { - pp[i].red = *entry_start++; - pp[i].green = *entry_start++; - pp[i].blue = *entry_start++; - pp[i].alpha = *entry_start++; - } - - else - { - pp[i].red = png_get_uint_16(entry_start); entry_start += 2; - pp[i].green = png_get_uint_16(entry_start); entry_start += 2; - pp[i].blue = png_get_uint_16(entry_start); entry_start += 2; - pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2; - } - - pp[i].frequency = png_get_uint_16(entry_start); entry_start += 2; - } -#endif /* Discard all chunk data except the name and stash that */ new_palette.name = (png_charp)buffer; @@ -1729,30 +1560,30 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_free(png_ptr, new_palette.entries); } -#endif /* PNG_READ_sPLT_SUPPORTED */ +#else +# define png_handle_sPLT NULL +#endif /* READ_sPLT */ #ifdef PNG_READ_tRNS_SUPPORTED -void /* PRIVATE */ -png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr) { + png_uint_32 num_trans; png_byte readbuf[PNG_MAX_PALETTE_LENGTH]; png_debug(1, "in png_handle_tRNS"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); + png_ptr->num_trans = 0U; /* safety */ - else if (png_ptr->mode & PNG_HAVE_IDAT) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); + /* libpng 1.7.0: this used to be a benign error, but it doesn't look very + * benign because it has security implications; libpng ignores the second + * tRNS, so if you can find something that ignores the first instead you + * can choose which image the user sees depending on the PNG decoder. + */ + png_crc_finish(png_ptr, png_ptr->chunk_length); + png_chunk_error(png_ptr, "duplicate"); return; } @@ -1760,15 +1591,14 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte buf[2]; - if (length != 2) + if (png_ptr->chunk_length != 2) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } png_crc_read(png_ptr, buf, 2); - png_ptr->num_trans = 1; + num_trans = 1U; png_ptr->trans_color.gray = png_get_uint_16(buf); } @@ -1776,15 +1606,14 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte buf[6]; - if (length != 6) + if (png_ptr->chunk_length != 6) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } - png_crc_read(png_ptr, buf, length); - png_ptr->num_trans = 1; + png_crc_read(png_ptr, buf, 6); + num_trans = 1U; png_ptr->trans_color.red = png_get_uint_16(buf); png_ptr->trans_color.green = png_get_uint_16(buf + 2); png_ptr->trans_color.blue = png_get_uint_16(buf + 4); @@ -1792,53 +1621,51 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { - if (!(png_ptr->mode & PNG_HAVE_PLTE)) + /* png_find_chunk_op checks this: */ + debug(png_ptr->mode & PNG_HAVE_PLTE); + + num_trans = png_ptr->chunk_length; + + if (num_trans > png_ptr->num_palette || num_trans == 0) { - /* TODO: is this actually an error in the ISO spec? */ - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); + png_handle_bad_length(png_ptr); return; } - if (length > png_ptr->num_palette || length > PNG_MAX_PALETTE_LENGTH || - length == 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); - return; - } - - png_crc_read(png_ptr, readbuf, length); - png_ptr->num_trans = (png_uint_16)length; + png_crc_read(png_ptr, readbuf, num_trans); } else { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid with alpha channel"); + png_handle_error(png_ptr, "invalid"); return; } if (png_crc_finish(png_ptr, 0)) - { - png_ptr->num_trans = 0; return; + + /* Set it into the info_struct: */ + png_set_tRNS(png_ptr, info_ptr, readbuf, num_trans, &png_ptr->trans_color); + + /* Now make a copy of the buffer if one is required (palette images). */ + debug(png_ptr->trans_alpha == NULL); + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_ptr->trans_alpha = png_voidcast(png_bytep, + png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH)); + memset(png_ptr->trans_alpha, 0xFFU, PNG_MAX_PALETTE_LENGTH); + memcpy(png_ptr->trans_alpha, info_ptr->trans_alpha, num_trans); } - /* TODO: this is a horrible side effect in the palette case because the - * png_struct ends up with a pointer to the tRNS buffer owned by the - * png_info. Fix this. - */ - png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans, - &(png_ptr->trans_color)); - - png_ptr->trans_alpha = info_ptr->trans_alpha; + png_ptr->num_trans = png_check_bits(png_ptr, num_trans, 9); } -#endif +#else +# define png_handle_tRNS NULL +#endif /* READ_tRNS */ #ifdef PNG_READ_bKGD_SUPPORTED -void /* PRIVATE */ -png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr) { unsigned int truelen; png_byte buf[6]; @@ -1846,22 +1673,9 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_bKGD"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & PNG_HAVE_IDAT) || - (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && - !(png_ptr->mode & PNG_HAVE_PLTE))) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD)) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD)) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); + png_handle_error(png_ptr, "duplicate"); return; } @@ -1874,10 +1688,9 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) else truelen = 2; - if (length != truelen) + if (png_ptr->chunk_length != truelen) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } @@ -1903,9 +1716,11 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) return; } - background.red = (png_uint_16)png_ptr->palette[buf[0]].red; - background.green = (png_uint_16)png_ptr->palette[buf[0]].green; - background.blue = (png_uint_16)png_ptr->palette[buf[0]].blue; + background.red = png_check_u16(png_ptr, png_ptr->palette[buf[0]].red); + background.green = + png_check_u16(png_ptr, png_ptr->palette[buf[0]].green); + background.blue = + png_check_u16(png_ptr, png_ptr->palette[buf[0]].blue); } else @@ -1934,40 +1749,30 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_set_bKGD(png_ptr, info_ptr, &background); } -#endif +#else +# define png_handle_bKGD NULL +#endif /* READ_bKGD */ #ifdef PNG_READ_hIST_SUPPORTED -void /* PRIVATE */ -png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr) { unsigned int num, i; png_uint_16 readbuf[PNG_MAX_PALETTE_LENGTH]; png_debug(1, "in png_handle_hIST"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & PNG_HAVE_IDAT) || !(png_ptr->mode & PNG_HAVE_PLTE)) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST)) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); + png_handle_error(png_ptr, "duplicate"); return; } - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST)) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } + num = png_ptr->chunk_length / 2; - num = length / 2 ; - - if (num != png_ptr->num_palette || num > PNG_MAX_PALETTE_LENGTH) + if (num != png_ptr->num_palette || 2*num != png_ptr->chunk_length) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } @@ -1984,11 +1789,13 @@ png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_set_hIST(png_ptr, info_ptr, readbuf); } -#endif +#else +# define png_handle_hIST NULL +#endif /* READ_hIST */ #ifdef PNG_READ_pHYs_SUPPORTED -void /* PRIVATE */ -png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr) { png_byte buf[9]; png_uint_32 res_x, res_y; @@ -1996,27 +1803,15 @@ png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_pHYs"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (png_ptr->mode & PNG_HAVE_IDAT) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); + png_handle_error(png_ptr, "duplicate"); return; } - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + if (png_ptr->chunk_length != 9) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - - if (length != 9) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } @@ -2030,11 +1825,13 @@ png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) unit_type = buf[8]; png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type); } -#endif +#else +# define png_handle_pHYs NULL +#endif /* READ_pHYs */ -#ifdef PNG_READ_oFFs_SUPPORTED -void /* PRIVATE */ -png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +#ifdef PNG_READ_oFFs_SUPPORTED /* EXTENSION, before IDAT, no duplicates */ +static void +png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr) { png_byte buf[9]; png_int_32 offset_x, offset_y; @@ -2042,27 +1839,15 @@ png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_oFFs"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (png_ptr->mode & PNG_HAVE_IDAT) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); + png_handle_error(png_ptr, "duplicate"); return; } - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) + if (png_ptr->chunk_length != 9) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - - if (length != 9) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } @@ -2076,12 +1861,13 @@ png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) unit_type = buf[8]; png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type); } -#endif +#else +# define png_handle_oFFs NULL +#endif /* READ_oFFs */ -#ifdef PNG_READ_pCAL_SUPPORTED -/* Read the pCAL chunk (described in the PNG Extensions document) */ -void /* PRIVATE */ -png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +#ifdef PNG_READ_pCAL_SUPPORTED /* EXTENSION: before IDAT, no duplicates */ +static void +png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr) { png_int_32 X0, X1; png_byte type, nparams; @@ -2091,52 +1877,40 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_pCAL"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (png_ptr->mode & PNG_HAVE_IDAT) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL)) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL)) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); + png_handle_error(png_ptr, "duplicate"); return; } png_debug1(2, "Allocating and reading pCAL chunk data (%u bytes)", - length + 1); + png_ptr->chunk_length + 1); - buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); + buffer = png_read_buffer(png_ptr, png_ptr->chunk_length+1, 2/*silent*/); if (buffer == NULL) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of memory"); + png_handle_error(png_ptr, "out of memory"); return; } - png_crc_read(png_ptr, buffer, length); + png_crc_read(png_ptr, buffer, png_ptr->chunk_length); if (png_crc_finish(png_ptr, 0)) return; - buffer[length] = 0; /* Null terminate the last string */ + buffer[png_ptr->chunk_length] = 0; /* Null terminate the last string */ png_debug(3, "Finding end of pCAL purpose string"); for (buf = buffer; *buf; buf++) /* Empty loop */ ; - endptr = buffer + length; + endptr = buffer + png_ptr->chunk_length; /* We need to have at least 12 bytes after the purpose string * in order to get the parameter information. */ - if (endptr <= buf + 12) + if (endptr - buf <= 12) { png_chunk_benign_error(png_ptr, "invalid"); return; @@ -2165,6 +1939,7 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) else if (type >= PNG_EQUATION_LAST) { png_chunk_benign_error(png_ptr, "unrecognized equation type"); + return; } for (buf = units; *buf; buf++) @@ -2172,7 +1947,7 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(3, "Allocating pCAL parameters array"); - params = png_voidcast(png_charpp, png_malloc_warn(png_ptr, + params = png_voidcast(png_charpp, png_malloc_base(png_ptr, nparams * (sizeof (png_charp)))); if (params == NULL) @@ -2201,57 +1976,47 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) } png_set_pCAL(png_ptr, info_ptr, (png_charp)buffer, X0, X1, type, nparams, - (png_charp)units, params); + (png_charp)units, params); png_free(png_ptr, params); } -#endif +#else +# define png_handle_pCAL NULL +#endif /* READ_pCAL */ #ifdef PNG_READ_sCAL_SUPPORTED /* Read the sCAL chunk */ -void /* PRIVATE */ -png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr) { + png_uint_32 length = png_ptr->chunk_length; png_bytep buffer; png_size_t i; int state; png_debug(1, "in png_handle_sCAL"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (png_ptr->mode & PNG_HAVE_IDAT) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL)) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL)) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); + png_handle_error(png_ptr, "duplicate"); return; } /* Need unit type, width, \0, height: minimum 4 bytes */ - else if (length < 4) + if (length < 4) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } png_debug1(2, "Allocating and reading sCAL chunk data (%u bytes)", - length + 1); + length + 1); buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); if (buffer == NULL) { - png_chunk_benign_error(png_ptr, "out of memory"); - png_crc_finish(png_ptr, length); + png_handle_error(png_ptr, "out of memory"); return; } @@ -2296,37 +2061,31 @@ png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) else /* This is the (only) success case. */ png_set_sCAL_s(png_ptr, info_ptr, buffer[0], - (png_charp)buffer+1, (png_charp)buffer+heighti); + (png_charp)buffer+1, (png_charp)buffer+heighti); } } -#endif +#else +# define png_handle_sCAL NULL +#endif /* READ_sCAL */ #ifdef PNG_READ_tIME_SUPPORTED -void /* PRIVATE */ -png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr) { png_byte buf[7]; png_time mod_time; png_debug(1, "in png_handle_tIME"); - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME)) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME)) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); + png_handle_error(png_ptr, "duplicate"); return; } - if (png_ptr->mode & PNG_HAVE_IDAT) - png_ptr->mode |= PNG_AFTER_IDAT; - - if (length != 7) + if (png_ptr->chunk_length != 7) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); + png_handle_bad_length(png_ptr); return; } @@ -2344,13 +2103,15 @@ png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_set_tIME(png_ptr, info_ptr, &mod_time); } -#endif +#else +# define png_handle_tIME NULL +#endif /* READ_tIME */ #ifdef PNG_READ_tEXt_SUPPORTED -/* Note: this does not properly handle chunks that are > 64K under DOS */ -void /* PRIVATE */ -png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr) { + png_uint_32 length = png_ptr->chunk_length; png_text text_info; png_bytep buffer; png_charp key; @@ -2370,33 +2131,17 @@ png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (--png_ptr->user_chunk_cache_max == 1) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "chunk cache full"); + png_handle_error(png_ptr, "no space in chunk cache"); return; } } -#endif - - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - if (png_ptr->mode & PNG_HAVE_IDAT) - png_ptr->mode |= PNG_AFTER_IDAT; - -#ifdef PNG_MAX_MALLOC_64K - if (length > 65535U) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "too large to fit in memory"); - return; - } -#endif +#endif /* USER_LIMITS */ buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/); if (buffer == NULL) { - png_chunk_benign_error(png_ptr, "out of memory"); + png_handle_error(png_ptr, "out of memory"); return; } @@ -2425,13 +2170,15 @@ png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (png_set_text_2(png_ptr, info_ptr, &text_info, 1)) png_warning(png_ptr, "Insufficient memory to process text chunk"); } -#endif +#else +# define png_handle_tEXt NULL +#endif /* READ_tEXt */ #ifdef PNG_READ_zTXt_SUPPORTED -/* Note: this does not correctly handle chunks that are > 64K under DOS */ -void /* PRIVATE */ -png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr) { + png_uint_32 length = png_ptr->chunk_length; png_const_charp errmsg = NULL; png_bytep buffer; png_uint_32 keyword_length; @@ -2449,25 +2196,20 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (--png_ptr->user_chunk_cache_max == 1) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "chunk cache full"); + png_handle_error(png_ptr, "no space in chunk cache"); return; } } -#endif - - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - if (png_ptr->mode & PNG_HAVE_IDAT) - png_ptr->mode |= PNG_AFTER_IDAT; +#endif /* USER_LIMITS */ + /* Note, "length" is sufficient here; we won't be adding + * a null terminator later. + */ buffer = png_read_buffer(png_ptr, length, 2/*silent*/); if (buffer == NULL) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of memory"); + png_handle_error(png_ptr, "out of memory"); return; } @@ -2504,7 +2246,7 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) * and text chunks. */ if (png_decompress_chunk(png_ptr, length, keyword_length+2, - &uncompressed_length, 1/*terminate*/) == Z_STREAM_END) + &uncompressed_length, 1/*terminate*/) == Z_STREAM_END) { png_text text; @@ -2534,13 +2276,15 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (errmsg != NULL) png_chunk_benign_error(png_ptr, errmsg); } -#endif +#else +# define png_handle_zTXt NULL +#endif /* READ_zTXt */ #ifdef PNG_READ_iTXt_SUPPORTED -/* Note: this does not correctly handle chunks that are > 64K under DOS */ -void /* PRIVATE */ -png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +static void +png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr) { + png_uint_32 length = png_ptr->chunk_length; png_const_charp errmsg = NULL; png_bytep buffer; png_uint_32 prefix_length; @@ -2558,25 +2302,17 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (--png_ptr->user_chunk_cache_max == 1) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "chunk cache full"); + png_handle_error(png_ptr, "no space in chunk cache"); return; } } -#endif - - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_chunk_error(png_ptr, "missing IHDR"); - - if (png_ptr->mode & PNG_HAVE_IDAT) - png_ptr->mode |= PNG_AFTER_IDAT; +#endif /* USER_LIMITS */ buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/); if (buffer == NULL) { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of memory"); + png_handle_error(png_ptr, "out of memory"); return; } @@ -2628,7 +2364,7 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) /* prefix_length should now be at the trailing '\0' of the translated * keyword, but it may already be over the end. None of this arithmetic * can overflow because chunks are at most 2^31 bytes long, but on 16-bit - * systems the available allocaton may overflow. + * systems the available allocation may overflow. */ ++prefix_length; @@ -2644,7 +2380,7 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) * iCCP and text chunks. */ if (png_decompress_chunk(png_ptr, length, prefix_length, - &uncompressed_length, 1/*terminate*/) == Z_STREAM_END) + &uncompressed_length, 1/*terminate*/) == Z_STREAM_END) buffer = png_ptr->read_buffer; else @@ -2660,7 +2396,7 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) buffer[uncompressed_length+prefix_length] = 0; - if (compressed != 0) + if (compressed == 0) text.compression = PNG_ITXT_COMPRESSION_NONE; else @@ -2684,78 +2420,97 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (errmsg != NULL) png_chunk_benign_error(png_ptr, errmsg); } -#endif +#else +# define png_handle_iTXt NULL +#endif /* READ_iTXt */ + +/* UNSUPPORTED CHUNKS */ +#define png_handle_sTER NULL +#define png_handle_fRAc NULL +#define png_handle_gIFg NULL +#define png_handle_gIFt NULL +#define png_handle_gIFx NULL +#define png_handle_dSIG NULL + +/* IDAT has special treatment below */ +#define png_handle_IDAT NULL + +/****************************************************************************** + * UNKNOWN HANDLING LOGIC + * + * There are three ways an unknown chunk may arise: + * + * 1) Chunks not in the spec. + * 2) Chunks in the spec where libpng support doesn't exist or has been compiled + * out. These are recognized, for a very small performance benefit at the + * cost of maintaining a png_known_chunks entry for each one. + * 3) Chunks supported by libpng which have been marked as 'unknown' by the + * application. + * + * Prior to 1.7.0 all three cases are handled the same way, in 1.7.0 some + * attempt is made to optimize (2) and (3) by storing flags in + * png_struct::known_unknown for chunks in the spec which have been marked for + * unknown handling. + * + * There are three things libpng can do with an unknown chunk, in order of + * preference: + * + * 1) If PNG_READ_USER_CHUNKS_SUPPORTED call an application supplied callback + * with all the chunk data. If this doesn't handle the chunk in prior + * versions of libpng the chunk would be stored if safe otherwise skipped. + * In 1.7.0 the specified chunk unknown handling is used. + * 2) If PNG_SAVE_UNKNOWN_CHUNKS_SUPPOPRTED the chunk may be saved in the + * info_struct (if there is one.) + * 3) The chunk can be skipped. + * + * In effect libpng tries each option in turn. (2) looks at any per-chunk + * unknown handling then, if one wasn't specified, the overall default. + * + * IHDR and IEND cannot be treated as unknown. PLTE and IDAT can. Prior to + * 1.7.0 they couldn't be skipped without a png_error. 1.7.0 adds an extension + * which allows any critical chunk to be skipped so long as IDAT is skipped; the + * logic for failing on critical chunks only applies if the image data is being + * processed. + * + * The default behavior is (3); unknown chunks are simply skipped. 1.7.0 uses + * this to optimize the read code when possible. + * + * In the read code PNG_READ_UNKNOWN_CHUNKS_SUPPORTED is set only if either (1) + * or (2) or both are supported. + * + *****************************************************************************/ +#ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED +static int +png_chunk_unknown_handling(png_const_structrp png_ptr, png_uint_32 chunk_name) +{ + png_byte chunk_string[5]; + + PNG_CSTRING_FROM_CHUNK(chunk_string, chunk_name); + return png_handle_as_unknown(png_ptr, chunk_string); +} +#endif /* SAVE_UNKNOWN_CHUNKS */ #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED /* Utility function for png_handle_unknown; set up png_ptr::unknown_chunk */ -static int -png_cache_unknown_chunk(png_structrp png_ptr, png_uint_32 length) +static void +png_make_unknown_chunk(png_structrp png_ptr, png_unknown_chunkp chunk, + png_bytep data) { - png_alloc_size_t limit = PNG_SIZE_MAX; - - if (png_ptr->unknown_chunk.data != NULL) - { - png_free(png_ptr, png_ptr->unknown_chunk.data); - png_ptr->unknown_chunk.data = NULL; - } - -# ifdef PNG_SET_USER_LIMITS_SUPPORTED - if (png_ptr->user_chunk_malloc_max > 0 && - png_ptr->user_chunk_malloc_max < limit) - limit = png_ptr->user_chunk_malloc_max; - -# elif PNG_USER_CHUNK_MALLOC_MAX > 0 - if (PNG_USER_CHUNK_MALLOC_MAX < limit) - limit = PNG_USER_CHUNK_MALLOC_MAX; -# endif - - if (length <= limit) - { - PNG_CSTRING_FROM_CHUNK(png_ptr->unknown_chunk.name, png_ptr->chunk_name); - /* The following is safe because of the PNG_SIZE_MAX init above */ - png_ptr->unknown_chunk.size = (png_size_t)length/*SAFE*/; - /* 'mode' is a flag array, only the bottom four bits matter here */ - png_ptr->unknown_chunk.location = (png_byte)png_ptr->mode/*SAFE*/; - - if (length == 0) - png_ptr->unknown_chunk.data = NULL; - - else - { - /* Do a 'warn' here - it is handled below. */ - png_ptr->unknown_chunk.data = png_voidcast(png_bytep, - png_malloc_warn(png_ptr, length)); - } - } - - if (png_ptr->unknown_chunk.data == NULL && length > 0) - { - /* This is benign because we clean up correctly */ - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "unknown chunk exceeds memory limits"); - return 0; - } - - else - { - if (length > 0) - png_crc_read(png_ptr, png_ptr->unknown_chunk.data, length); - png_crc_finish(png_ptr, 0); - return 1; - } + chunk->data = data; + chunk->size = png_ptr->chunk_length; + PNG_CSTRING_FROM_CHUNK(chunk->name, png_ptr->chunk_name); + /* 'mode' is a flag array, only three of the bottom four bits are public: */ + chunk->location = + png_ptr->mode & (PNG_HAVE_IHDR+PNG_HAVE_PLTE+PNG_AFTER_IDAT); } -#endif /* PNG_READ_UNKNOWN_CHUNKS_SUPPORTED */ /* Handle an unknown, or known but disabled, chunk */ void /* PRIVATE */ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, - png_uint_32 length, int keep) + png_bytep chunk_data) { - int handled = 0; /* the chunk was handled */ - png_debug(1, "in png_handle_unknown"); -#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED /* NOTE: this code is based on the code in libpng-1.4.12 except for fixing * the bug which meant that setting a non-default behavior for a specific * chunk would be ignored (the default was always used unless a user @@ -2766,75 +2521,47 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, * will always be PNG_HANDLE_CHUNK_AS_DEFAULT and it needs to be set here. * This is just an optimization to avoid multiple calls to the lookup * function. - */ -# ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED -# ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED - keep = png_chunk_unknown_handling(png_ptr, png_ptr->chunk_name); -# endif -# endif - - /* One of the following methods will read the chunk or skip it (at least one + * + * One of the following methods will read the chunk or skip it (at least one * of these is always defined because this is the only way to switch on * PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) */ # ifdef PNG_READ_USER_CHUNKS_SUPPORTED - /* The user callback takes precedence over the chunk keep value, but the - * keep value is still required to validate a save of a critical chunk. - */ + /* The user callback takes precedence over the chunk handling option: */ if (png_ptr->read_user_chunk_fn != NULL) { - if (png_cache_unknown_chunk(png_ptr, length)) - { - /* Callback to user unknown chunk handler */ - int ret = (*(png_ptr->read_user_chunk_fn))(png_ptr, - &png_ptr->unknown_chunk); + png_unknown_chunk unknown_chunk; + int ret; - /* ret is: - * negative: An error occured, png_chunk_error will be called. - * zero: The chunk was not handled, the chunk will be discarded - * unless png_set_keep_unknown_chunks has been used to set - * a 'keep' behavior for this particular chunk, in which - * case that will be used. A critical chunk will cause an - * error at this point unless it is to be saved. - * positive: The chunk was handled, libpng will ignore/discard it. - */ - if (ret < 0) - png_chunk_error(png_ptr, "error in user chunk"); + /* Callback to user unknown chunk handler */ + png_make_unknown_chunk(png_ptr, &unknown_chunk, chunk_data); + ret = png_ptr->read_user_chunk_fn(png_ptr, &unknown_chunk); - else if (ret == 0) - { - /* Use the default handling, note that if there is per-chunk - * handling specified it has already been set into 'keep'. - * - * NOTE: this is an API change in 1.7.0. Prior to 1.7.0 libpng - * would force keep to PNG_HANDLE_CHUNK_IF_SAFE at this point, - * and 1.6.0 would issue a warning if this caused a default of - * discarding the chunk to be changed. - */ - if (keep == PNG_HANDLE_CHUNK_AS_DEFAULT) - keep = png_ptr->unknown_default; - } + /* ret is: + * negative: An error occurred; png_chunk_error will be called. + * zero: The chunk was not handled, the chunk will be discarded + * unless png_set_keep_unknown_chunks has been used to set + * a 'keep' behavior for this particular chunk, in which + * case that will be used. A critical chunk will cause an + * error at this point unless it is to be saved. + * positive: The chunk was handled, libpng will ignore/discard it. + */ + if (ret > 0) + return; - else /* chunk was handled */ - { - handled = 1; - /* Critical chunks can be safely discarded at this point. */ - keep = PNG_HANDLE_CHUNK_NEVER; - } - } + else if (ret < 0) + png_chunk_error(png_ptr, "application error"); - else - keep = PNG_HANDLE_CHUNK_NEVER; /* insufficient memory */ + /* Else: use the default handling. */ } - - else - /* Use the SAVE_UNKNOWN_CHUNKS code or skip the chunk */ -# endif /* PNG_READ_USER_CHUNKS_SUPPORTED */ +# endif /* READ_USER_CHUNKS */ # ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED { + int keep = png_chunk_unknown_handling(png_ptr, png_ptr->chunk_name); + /* keep is currently just the per-chunk setting, if there was no - * setting change it to the global default now (not that this may + * setting change it to the global default now (note that this may * still be AS_DEFAULT) then obtain the cache of the chunk if required, * if not simply skip the chunk. */ @@ -2842,95 +2569,54 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, keep = png_ptr->unknown_default; if (keep == PNG_HANDLE_CHUNK_ALWAYS || - (keep == PNG_HANDLE_CHUNK_IF_SAFE && + (keep == PNG_HANDLE_CHUNK_IF_SAFE && PNG_CHUNK_ANCILLARY(png_ptr->chunk_name))) - { - if (!png_cache_unknown_chunk(png_ptr, length)) - keep = PNG_HANDLE_CHUNK_NEVER; - } +# ifdef PNG_USER_LIMITS_SUPPORTED + switch (png_ptr->user_chunk_cache_max) + { + case 2: + png_ptr->user_chunk_cache_max = 1; + png_chunk_benign_error(png_ptr, "no space in chunk cache"); + /* FALL THROUGH */ + case 1: + /* NOTE: prior to 1.6.0 this case resulted in an unknown + * critical chunk being skipped, now there will be a hard + * error below. + */ + break; - else - png_crc_finish(png_ptr, length); + default: /* not at limit */ + --(png_ptr->user_chunk_cache_max); + /* FALL THROUGH */ + case 0: /* no limit */ +# endif /* USER_LIMITS */ + /* Here when the limit isn't reached or when limits are + * compiled out; store the chunk. + */ + { + png_unknown_chunk unknown_chunk; + + png_make_unknown_chunk(png_ptr, &unknown_chunk, + chunk_data); + png_set_unknown_chunks(png_ptr, info_ptr, &unknown_chunk, + 1); + return; + } +# ifdef PNG_USER_LIMITS_SUPPORTED + } +# endif /* USER_LIMITS */ } -# else -# ifndef PNG_READ_USER_CHUNKS_SUPPORTED -# error no method to support READ_UNKNOWN_CHUNKS -# endif - - { - /* If here there is no read callback pointer set and no support is - * compiled in to just save the unknown chunks, so simply skip this - * chunk. If 'keep' is something other than AS_DEFAULT or NEVER then - * the app has erroneously asked for unknown chunk saving when there - * is no support. - */ - if (keep > PNG_HANDLE_CHUNK_NEVER) - png_app_error(png_ptr, "no unknown chunk support available"); - - png_crc_finish(png_ptr, length); - } -# endif - -# ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED - /* Now store the chunk in the chunk list if appropriate, and if the limits - * permit it. - */ - if (keep == PNG_HANDLE_CHUNK_ALWAYS || - (keep == PNG_HANDLE_CHUNK_IF_SAFE && - PNG_CHUNK_ANCILLARY(png_ptr->chunk_name))) - { -# ifdef PNG_USER_LIMITS_SUPPORTED - switch (png_ptr->user_chunk_cache_max) - { - case 2: - png_ptr->user_chunk_cache_max = 1; - png_chunk_benign_error(png_ptr, "chunk cache full"); - /* FALL THROUGH */ - case 1: - /* NOTE: prior to 1.6.0 this case resulted in an unknown critical - * chunk being skipped, now there will be a hard error below. - */ - break; - - default: /* not at limit */ - --(png_ptr->user_chunk_cache_max); - /* FALL THROUGH */ - case 0: /* no limit */ -# endif /* PNG_USER_LIMITS_SUPPORTED */ - /* Here when the limit isn't reached or when limits are compiled - * out; store the chunk. - */ - png_set_unknown_chunks(png_ptr, info_ptr, - &png_ptr->unknown_chunk, 1); - handled = 1; -# ifdef PNG_USER_LIMITS_SUPPORTED - break; - } -# endif - } -# else /* no store support: the chunk must be handled by the user callback */ +# else /* !SAVE_UNKNOWN_CHUNKS */ PNG_UNUSED(info_ptr) -# endif +# endif /* !SAVE_UNKNOWN_CHUNKS */ - /* Regardless of the error handling below the cached data (if any) can be - * freed now. Notice that the data is not freed if there is a png_error, but - * it will be freed by destroy_read_struct. + /* This is the 'skip' case, where the read callback (if any) returned 0 and + * the save code did not save the chunk. */ - if (png_ptr->unknown_chunk.data != NULL) - png_free(png_ptr, png_ptr->unknown_chunk.data); - png_ptr->unknown_chunk.data = NULL; - -#else /* !PNG_READ_UNKNOWN_CHUNKS_SUPPORTED */ - /* There is no support to read an unknown chunk, so just skip it. */ - png_crc_finish(png_ptr, length); - PNG_UNUSED(info_ptr) - PNG_UNUSED(keep) -#endif /* !PNG_READ_UNKNOWN_CHUNKS_SUPPORTED */ - - /* Check for unhandled critical chunks */ - if (!handled && PNG_CHUNK_CRITICAL(png_ptr->chunk_name)) + if (PNG_CHUNK_CRITICAL(png_ptr->chunk_name)) png_chunk_error(png_ptr, "unhandled critical chunk"); } +#endif /* READ_UNKNOWN_CHUNKS */ /* This function is called to verify that a chunk name is valid. * This function can't have the "critical chunk check" incorporated @@ -2945,835 +2631,950 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, */ void /* PRIVATE */ -png_check_chunk_name(png_structrp png_ptr, png_uint_32 chunk_name) +png_check_chunk_name(png_const_structrp png_ptr, const png_uint_32 chunk_name) { int i; + png_uint_32 cn=chunk_name; png_debug(1, "in png_check_chunk_name"); for (i=1; i<=4; ++i) { - int c = chunk_name & 0xff; + int c = cn & 0xff; if (c < 65 || c > 122 || (c > 90 && c < 97)) png_chunk_error(png_ptr, "invalid chunk type"); - chunk_name >>= 8; + cn >>= 8; + } +} +void /* PRIVATE */ +png_check_chunk_length(png_const_structrp png_ptr, const png_uint_32 length) +{ + png_alloc_size_t limit = PNG_UINT_31_MAX; + +# ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_malloc_max > 0 && + png_ptr->user_chunk_malloc_max < limit) + limit = png_ptr->user_chunk_malloc_max; +# elif PNG_USER_CHUNK_MALLOC_MAX > 0 + if (PNG_USER_CHUNK_MALLOC_MAX < limit) + limit = PNG_USER_CHUNK_MALLOC_MAX; +# endif + if (png_ptr->chunk_name == png_IDAT) + { + /* color_type 0 x 2 3 4 x 6 */ + int channels[]={1,0,3,1,2,0,4}; + png_alloc_size_t idat_limit = PNG_UINT_31_MAX; + size_t row_factor = + (png_ptr->width * channels[png_ptr->color_type] * + (png_ptr->bit_depth > 8? 2: 1) + + 1 + (png_ptr->interlaced? 6: 0)); + if (png_ptr->height > PNG_UINT_32_MAX/row_factor) + idat_limit=PNG_UINT_31_MAX; + else + idat_limit = png_ptr->height * row_factor; + row_factor = row_factor > 32566? 32566 : row_factor; + idat_limit += 6 + 5*(idat_limit/row_factor+1); /* zlib+deflate overhead */ + idat_limit=idat_limit < PNG_UINT_31_MAX? idat_limit : PNG_UINT_31_MAX; + limit = limit < idat_limit? idat_limit : limit; + } + + if (length > limit) + { + png_debug2(0," length = %lu, limit = %lu", + (unsigned long)length,(unsigned long)limit); + png_chunk_error(png_ptr, "chunk data is too large"); } } -/* Combines the row recently read in with the existing pixels in the row. This - * routine takes care of alpha and transparency if requested. This routine also - * handles the two methods of progressive display of interlaced images, - * depending on the 'display' value; if 'display' is true then the whole row - * (dp) is filled from the start by replicating the available pixels. If - * 'display' is false only those pixels present in the pass are filled in. +/* This is the known chunk table; it contains an entry for each supported + * chunk. */ -void /* PRIVATE */ -png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) +static const struct { - unsigned int pixel_depth = png_ptr->transformed_pixel_depth; - png_const_bytep sp = png_ptr->row_buf + 1; - png_uint_32 row_width = png_ptr->width; - unsigned int pass = png_ptr->pass; - png_bytep end_ptr = 0; - png_byte end_byte = 0; - unsigned int end_mask; + void (*handle)(png_structrp png_ptr, png_infop info_ptr); + png_uint_32 name; + unsigned int before :5; + unsigned int after :5; +} +png_known_chunks[] = +/* To make the code easier to write the following defines are used, note that + * before_end should never trip - it would indicate that libpng attempted to + * read beyond the IEND chunk. + * + * 'within_IDAT' is used for IDAT chunks; PNG_AFTER_IDAT must not be set, but + * PNG_HAVE_IDAT may be set. + */ +#define before_end PNG_HAVE_IEND /* Should be impossible */ +#define within_IDAT (before_end+PNG_AFTER_IDAT) +#define before_IDAT (within_IDAT+PNG_HAVE_IDAT) +#define before_PLTE (before_IDAT+PNG_HAVE_PLTE) +#define before_start (before_PLTE+PNG_HAVE_IHDR) +#define at_start 0 +#define after_start PNG_HAVE_IHDR +#define after_PLTE (after_start+PNG_HAVE_PLTE) /* NOTE: PLTE optional */ +#define after_IDAT (after_PLTE+PNG_AFTER_IDAT) /* NOTE: PLTE optional */ + +/* See pngchunk.h for how this works: */ +#define PNG_CHUNK_END(n, c1, c2, c3, c4, before, after)\ + { png_handle_ ## n, png_ ##n, before, after } +#define PNG_CHUNK(n, c1, c2, c3, c4, before, after)\ + PNG_CHUNK_END(n, c1, c2, c3, c4, before, after), +#define PNG_CHUNK_BEGIN(n, c1, c2, c3, c4, before, after)\ + PNG_CHUNK_END(n, c1, c2, c3, c4, before, after), +{ +# include "pngchunk.h" +}; +#undef PNG_CHUNK_START +#undef PNG_CHUNK +#undef PNG_CHUNK_END + +#define C_KNOWN ((sizeof png_known_chunks)/(sizeof png_known_chunks[0])) + +/* See: scripts/chunkhash.c for code to generate this. This reads the same + * description file (pngchunk.h) as is included above. Whenever + * that file is changed chunkhash needs to be re-run to generate the lines + * following this comment. + * + * PNG_CHUNK_HASH modifes its argument and returns an index. png_chunk_index is + * a function which does the same thing without modifying the value of the + * argument. Both macro and function always return a valid index; to detect + * known chunks it is necessary to check png_known_chunks[index].name against + * the hashed name. + */ +static const png_byte png_chunk_lut[64] = +{ + 10, 20, 7, 3, 0, 23, 8, 0, 0, 11, 24, 0, 0, 0, 0, 4, + 12, 0, 0, 0, 13, 0, 0, 0, 25, 0, 0, 0, 2, 0, 0, 0, + 0, 6, 17, 0, 15, 0, 5, 19, 26, 0, 0, 0, 18, 0, 0, 9, + 1, 0, 21, 0, 22, 14, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0 +}; + +#define PNG_CHUNK_HASH(n)\ + png_chunk_lut[0x3f & (((n += n >> 2),n += n >> 8),n += n >> 16)] + +static png_byte +png_chunk_index(png_uint_32 name) +{ + name += name >> 2; + name += name >> 8; + name += name >> 16; + return png_chunk_lut[name & 0x3f]; +} + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +/* Mark a known chunk to be handled as unknown. */ +void /*PRIVATE*/ +png_cache_known_unknown(png_structrp png_ptr, png_const_bytep add, int keep) + /* Update the png_struct::known_unknown bit cache which stores whether each + * known chunk should be treated as unknown. + * + * This cache exists to avoid doing the search loop on every chunk while + * handling chunks. This code is only ever used if unknown handling is + * invoked, and the loop is isolated code; the function is called from + * add_one_chunk in pngset.c once for each unknown and while this is + * happening no other code is being run in this thread. + */ +{ + /* The cache only stores whether or not to handle the chunk; specifically + * whether or not keep is 0. + */ + png_uint_32 name = PNG_CHUNK_FROM_STRING(add); + + debug(PNG_HANDLE_CHUNK_AS_DEFAULT == 0 && C_KNOWN <= 32); + + /* But do not treat IHDR or IEND as unknown. This is historical; it + * always was this way, it's not clear if PLTE can always safely be + * treated as unknown, but it is allowed. + */ + if (name != png_IHDR && name != png_IEND) + { + png_byte i = png_chunk_index(name); + + if (png_known_chunks[i].name == name) + { + { + if (keep != PNG_HANDLE_CHUNK_AS_DEFAULT) + { + png_ptr->known_unknown |= 1U << i; + +# ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED + if (keep == PNG_HANDLE_CHUNK_ALWAYS || + (keep == PNG_HANDLE_CHUNK_IF_SAFE && + PNG_CHUNK_ANCILLARY(name))) + png_ptr->save_unknown |= 1U << i; + + else /* PNG_HANDLE_CHUNK_NEVER || !SAFE */ + png_ptr->save_unknown &= ~(1U << i); +# endif /* SAVE_UNKNOWN_CHUNKS */ + } + + else + png_ptr->known_unknown &= ~(1U << i); + } + } + + /* else this is not a known chunk */ + } + + else /* 1.7.0: inform the app writer; */ + png_app_warning(png_ptr, "IHDR, IEND cannot be treated as unknown"); + +} +#endif /* HANDLE_AS_UNKNOWN */ + +/* Handle chunk position requirements in a consistent way. The chunk must + * come after 'after' and before 'before', either of which may be 0. If it + * does the function returns true, if it does not an appropriate chunk error + * is issued; benign for non-critical chunks, fatal for critical ones. + */ +static int +png_handle_position(png_const_structrp png_ptr, unsigned int chunk) +{ + unsigned int before = png_known_chunks[chunk].before; + unsigned int after = png_known_chunks[chunk].after; + +# ifdef PNG_ERROR_TEXT_SUPPORTED + png_const_charp error = NULL; +# endif /* ERROR_TEXT */ + + /* PLTE is optional with all color types except PALETTE, so for the other + * color types clear it from the 'after' bits. + * + * TODO: find some better way of recognizing the case where there is a PLTE + * and it follows after_PLTE chunks (see the complex stuff in handle_PLTE.) + */ + if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + after &= PNG_BIC_MASK(PNG_HAVE_PLTE); + + if ((png_ptr->mode & before) == 0 && + (png_ptr->mode & after) == after) + return 1; + + /* The error case; do before first (it is normally more important) */ +# ifdef PNG_ERROR_TEXT_SUPPORTED + switch (before & -before) /* Lowest set bit */ + { + case 0: + /* Check 'after'; only one bit set. */ + switch (after) + { + case PNG_HAVE_IHDR: + error = "missing IHDR"; + break; + + case PNG_HAVE_PLTE: + error = "must occur after PLTE"; + break; + + case PNG_AFTER_IDAT: + error = "must come after IDAT"; + break; + + default: + impossible("invalid 'after' position"); + } + break; + + case PNG_HAVE_IHDR: + error = "must occur first"; + break; + + case PNG_HAVE_PLTE: + error = "must come before PLTE"; + break; + + case PNG_HAVE_IDAT: + error = "must come before IDAT"; + break; + + default: + impossible("invalid 'before' position"); + } +# endif /* ERROR_TEXT */ + + png_chunk_report(png_ptr, error, PNG_CHUNK_CRITICAL(png_ptr->chunk_name) ? + PNG_CHUNK_FATAL : PNG_CHUNK_ERROR); + return 0; +} + +/* This is the shared chunk handling function, used for both the sequential and + * progressive reader. + */ +png_chunk_op /* PRIVATE */ +png_find_chunk_op(png_structrp png_ptr) +{ + /* Given a chunk in png_struct::{chunk_name,chunk_length} validate the name + * and work out how it should be handled. This function checks the chunk + * location using png_struct::mode and will set the PNG_AFTER_IDAT bit if + * appropriate but otherwise makes no changes to the stream read state. + * + * png_chunk_skip Skip this chunk + * png_chunk_unknown This is an unknown chunk which can't be skipped; + * the unknown handler must be called with all the + * chunk data. + * png_chunk_process_all The caller must call png_chunk_handle to handle + * the chunk, when this call is made all the chunk + * data must be available to the handler. + * png_chunk_process_part The handler expects data in png_struct::zstream. + * {next,avail}_in and does not require all of the + * data at once (as png_read_process_IDAT). + */ + png_uint_32 chunk_name = png_ptr->chunk_name; + unsigned int mode = png_ptr->mode; + unsigned int index; + + /* This function should never be called if IEND has been set: + */ + debug((mode & PNG_HAVE_IEND) == 0); + + /* IDAT logic: we are only *after* IDAT when we start reading the first + * following (non-IDAT) chunk, this may already have been set in the IDAT + * handling code, but if IDAT is handled as unknown this doesn't happen. + */ + if (chunk_name != png_IDAT && (mode & PNG_HAVE_IDAT) != 0) + mode = png_ptr->mode |= PNG_AFTER_IDAT; + + index = png_chunk_index(chunk_name); + + if (png_known_chunks[index].name == chunk_name) + { + /* Known chunks have a position requirement; check it, badly positioned + * chunks that do not error out in png_handle_position are simply skipped. + * + * API CHANGE: libpng 1.7.0: prior versions of libpng did not check + * ordering requirements for known chunks where the support for reading + * them had been configured out of libpng. This seems dangerous; the + * user chunk callback could still see them and crash as a result. + */ + if (!png_handle_position(png_ptr, index)) + return png_chunk_skip; + + /* Do the mode update. + * + * API CHANGE 1.7.0: the 'HAVE' flags are now consistently set *before* + * the chunk is handled. Previously only IDAT was handled this way. This + * can only affect an app that was previously handling PLTE itself in a + * callback, however this seems to be impossible. + */ + switch (chunk_name) + { + case png_IHDR: png_ptr->mode |= PNG_HAVE_IHDR; break; + case png_PLTE: png_ptr->mode |= PNG_HAVE_PLTE; break; + case png_IDAT: png_ptr->mode |= PNG_HAVE_IDAT; break; + case png_IEND: png_ptr->mode |= PNG_HAVE_IEND; break; + default: break; + } + +# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + /* A known chunk may still be treated as unknown. Check for that. */ + if (!((png_ptr->known_unknown >> index) & 1U)) +# endif /* HANDLE_AS_UNKNOWN */ + { + /* This is a known chunk that is not being treated as unknown. If + * it is IDAT then partial processing is done, otherwise (at present) + * the whole thing is processed in one shot + * + * TODO: this is a feature of the legacy use of the sequential read + * code in the handlers, fix this. + */ + if (chunk_name == png_IDAT) + return png_chunk_process_part; + + /* Check for a known chunk where support has been compiled out of + * libpng. We know it cannot be a critical chunk; support for those + * cannot be removed. + */ + if (png_known_chunks[index].handle != NULL) + return png_chunk_process_all; + +# ifdef PNG_READ_USER_CHUNKS_SUPPORTED + if (png_ptr->read_user_chunk_fn != NULL) + return png_chunk_unknown; +# endif /* READ_USER_CHUNKS */ + +# ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED + /* There is no per-chunk special handling set for this chunk + * (because of the test on known_unknown above) so only the + * default unknown handling behavior matters. We skip the chunk + * if the behavior is 'NEVER' or 'DEFAULT'. This is irrelevant + * if SAVE_UNKNOWN_CHUNKS is not supported. + */ + if (png_ptr->unknown_default > PNG_HANDLE_CHUNK_NEVER) + return png_chunk_unknown; +# endif /* SAVE_UNKNOWN_CHUNKS */ + + return png_chunk_skip; + } + +# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else + { + /* Else this is a known chunk that is being treated as unknown. If + * there is a user callback the whole shebang is required: + */ +# ifdef PNG_READ_USER_CHUNKS_SUPPORTED + if (png_ptr->read_user_chunk_fn != NULL) + return png_chunk_unknown; +# endif /* READ_USER_CHUNKS */ + + /* No user callback, there is a possibility that we can skip this + * chunk: + */ +# ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED + if ((png_ptr->save_unknown >> index) & 1U) + return png_chunk_unknown; +# endif /* SAVE_UNKNOWN_CHUNKS */ + + /* If this is a critical chunk and IDAT is not being skipped then + * this is an error. The only possibility here is PLTE on an + * image which is palette mapped. If the app ignores this error + * then there will be a more definate one in png_handle_unknown. + */ + if (chunk_name == png_PLTE && + png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_app_error(png_ptr, "skipping PLTE on palette image"); + + return png_chunk_skip; + } +# endif /* HANDLE_AS_UNKNOWN */ + } + + else /* unknown chunk */ + { + /* The code above implicitly validates the chunk name, however if a chunk + * name/type is not recognized it is necessary to validate it to ensure + * that the PNG stream isn't hopelessly damaged: + */ + png_check_chunk_name(png_ptr, chunk_name); + +# ifdef PNG_READ_USER_CHUNKS_SUPPORTED + if (png_ptr->read_user_chunk_fn != NULL) + return png_chunk_unknown; +# endif /* READ_USER_CHUNKS */ + +# ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED + /* There may be per-chunk handling, otherwise the default is used, this + * is the one place where the list needs to be searched: + */ + { + int keep = png_chunk_unknown_handling(png_ptr, chunk_name); + + if (keep == PNG_HANDLE_CHUNK_AS_DEFAULT) + keep = png_ptr->unknown_default; + + if (keep == PNG_HANDLE_CHUNK_ALWAYS || + (keep == PNG_HANDLE_CHUNK_IF_SAFE && + PNG_CHUNK_ANCILLARY(chunk_name))) + return png_chunk_unknown; + } +# endif /* SAVE_UNKNOWN_CHUNKS */ + + /* The chunk will be skipped so it must not be a critical chunk, unless + * IDATs are being skipped too. + */ + if (PNG_CHUNK_CRITICAL(chunk_name) +# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + && !png_IDATs_skipped(png_ptr) +# endif /* HANDLE_AS_UNKNOWN */ + ) + png_chunk_error(png_ptr, "unhandled critical chunk"); + + return png_chunk_skip; + } +} + +void /* PRIVATE */ +png_handle_chunk(png_structrp png_ptr, png_inforp info_ptr) + /* The chunk to handle is in png_struct::chunk_name,chunk_length. + * + * NOTE: at present it is only valid to call this after png_find_chunk_op + * has returned png_chunk_process_all and all the data is available for + * png_handle_chunk (via the libpng read callback.) + */ +{ + png_uint_32 chunk_name = png_ptr->chunk_name; + unsigned int index = png_chunk_index(chunk_name); + + /* So this must be true: */ + affirm(png_known_chunks[index].name == chunk_name && + png_known_chunks[index].handle != NULL); + + png_known_chunks[index].handle(png_ptr, info_ptr); +} + +static void +copy_row(png_const_structrp png_ptr, png_bytep dp, png_const_bytep sp, + png_uint_32 x/*in INPUT*/, png_uint_32 width/*of INPUT*/, int clear) + /* Copy the row in row_buffer; this is the 'simple' case of combine_row + * where no adjustment to the pixel spacing is required. + */ +{ + png_copy_row(png_ptr, dp, sp, x, width, +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + png_ptr->row_bit_depth * PNG_FORMAT_CHANNELS(png_ptr->row_format), +# else + PNG_PIXEL_DEPTH(*png_ptr), +# endif + clear/*clear partial byte at end of row*/, 1/*sp -> dp[x]*/); +} + +#ifdef PNG_READ_INTERLACING_SUPPORTED +static void +combine_row(png_const_structrp png_ptr, png_bytep dp, png_const_bytep sp, + png_uint_32 x/*in INPUT*/, png_uint_32 width/*of INPUT*/, int display) + /* 1.7.0: API CHANGE: prior to 1.7.0 read de-interlace was done in two steps, + * the first would expand a narrow pass by replicating pixels according to + * the inter-pixel spacing of the pixels from the pass in the image. It did + * not take account of any offset from the start of the image row of the + * first pixel. The second step happened in png_combine_row where the result + * was merged into the output rows. + * + * In 1.7.0 this is no longer done. Instead all the work happens here. This + * is only an API change for the progressive reader if the app didn't call + * png_combine_row, but rather expected an expanded row. It's not obvious + * why any user of the progressive reader would want this, particularly given + * the weird non-offseting of the start in the original + * 'png_do_read_interlace'; the behavior was completely undocumented. + * + * In 1.7.0 combine_row does all the work. It expects a raw uncompressed, + * de-filtered, transformed row and it either copies it if: + * + * 1) It is not interlaced. + * 2) libpng isn't handling the de-interlace. + * 3) This is pass 7 (i.e. '6' using the libpng 0-based numbering). + * + * The input data comes from png_struct and sp: + * + * sp[width(pixels)]; the row data from input[x(pixels)...] + * png_struct::pass; the pass + * png_struct::row_number; the row number in the *image* + * png_struct::row_bit_depth, + * png_struct::row_format; the pixel format, if TRANSFORM_MECH, else: + * png_struct::bit_depth, + * png_struct::color_type; the pixel format otherwise + * + * The destination pointer (but not size) and how to handle intermediate + * passes are arguments to the API. The destination is the pointer to the + * entire row buffer, not just the part from output[x] on. 'display' is + * interpreted as: + * + * 0: only overwrite destination pixels that will correspond to the source + * pixel in the final image. 'sparkle' mode. + * 1: overwrite the corresponding destination pixel and all following + * pixels (horizontally and, eventually, vertically) that will come + * from *later* passes. 'block' mode. + */ +{ + const unsigned int pass = png_ptr->pass; png_debug(1, "in png_combine_row"); - /* Added in 1.5.6: it should not be possible to enter this routine until at - * least one row has been read from the PNG data and transformed. + /* Factor out the copy case first, the 'display' argument is irrelevant in + * these cases: */ - if (pixel_depth == 0) - png_error(png_ptr, "internal row logic error"); - - /* Added in 1.5.4: the pixel depth should match the information returned by - * any call to png_read_update_info at this point. Do not continue if we got - * this wrong. - */ - if (png_ptr->info_rowbytes != 0 && png_ptr->info_rowbytes != - PNG_ROWBYTES(pixel_depth, row_width)) - png_error(png_ptr, "internal row size calculation error"); - - /* Don't expect this to ever happen: */ - if (row_width == 0) - png_error(png_ptr, "internal row width error"); - - /* Preserve the last byte in cases where only part of it will be overwritten, - * the multiply below may overflow, we don't care because ANSI-C guarantees - * we get the low bits. - */ - end_mask = (pixel_depth * row_width) & 7; - if (end_mask != 0) + if (!png_ptr->do_interlace || png_ptr->pass == 6) { - /* end_ptr == NULL is a flag to say do nothing */ - end_ptr = dp + PNG_ROWBYTES(pixel_depth, row_width) - 1; - end_byte = *end_ptr; -# ifdef PNG_READ_PACKSWAP_SUPPORTED - if (png_ptr->transformations & PNG_PACKSWAP) /* little-endian byte */ - end_mask = 0xff << end_mask; - - else /* big-endian byte */ -# endif - end_mask = 0xff >> end_mask; - /* end_mask is now the bits to *keep* from the destination row */ + copy_row(png_ptr, dp, sp, x, width, 0/*do not clear*/); + return; } - /* For non-interlaced images this reduces to a memcpy(). A memcpy() - * will also happen if interlacing isn't supported or if the application - * does not call png_set_interlace_handling(). In the latter cases the - * caller just gets a sequence of the unexpanded rows from each interlace - * pass. - */ -#ifdef PNG_READ_INTERLACING_SUPPORTED - if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE) && - pass < 6 && (display == 0 || - /* The following copies everything for 'display' on passes 0, 2 and 4. */ - (display == 1 && (pass & 1) != 0))) + else /* not a simple copy */ { - /* Narrow images may have no bits in a pass; the caller should handle - * this, but this test is cheap: + const unsigned int pixel_depth = +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + png_ptr->row_bit_depth * PNG_IMAGE_PIXEL_CHANNELS(png_ptr->row_format); +# else + PNG_PIXEL_DEPTH(*png_ptr); +# endif + png_uint_32 row_width = png_ptr->width; /* output width */ + /* The first source pixel is written to PNG_COL_FROM_PASS of the + * destination: */ - if (row_width <= PNG_PASS_START_COL(pass)) - return; + png_uint_32 dx = PNG_COL_FROM_PASS_COL(x, pass); + /* The corresponding offset within the 8x8 block: */ + const unsigned int dstart = dx & 0x7U; + /* Find the first pixel written in any 8x8 block IN THIS PASS: */ + const unsigned int pass_start = PNG_PASS_START_COL(pass); + /* Subsequent pixels are written PNG_PASS_COL_OFFSET further on: */ + const unsigned int doffset = PNG_PASS_COL_OFFSET(pass); + /* In 'block' mode when PNG_PASS_START_COL(pass) is 0 (PNG passes 1,3,5,7) + * the same pixel is replicated doffset times, when PNG_PASS_START_COL is + * non-zero (PNG passes 2,4,6) it is replicated PNG_PASS_START_COL times. + * For 'sparkle' mode only one copy of the pixel is written: + */ + unsigned int drep = display ? (pass_start ? pass_start : doffset) : 1; - if (pixel_depth < 8) + /* Standard check for byte alignment */ + debug(((x * pixel_depth/*OVERFLOW OK*/) & 0x7U) == 0U); + + /* The caller should have excluded the narrow cases: */ + affirm(row_width > dx); + row_width -= dx; + /* Advance dp to the start of the 8x8 block containing the first pixel to + * write, adjust dx to be an offset within the block: + */ + dp += png_calc_rowbytes(png_ptr, pixel_depth, dx & ~0x7U); + dx &= 0x7U; + + /* So each source pixel sp[i] is written to: + * + * dp[dstart + i*doffset]..dp[dstart + i*doffset + (drep-1)] + * + * Until we get to row_width. This is easy for pixels that are 8 or more + * bits deep; whole bytes are read and written, slightly more difficult + * when pixel_depth * drep is at least 8 bits, because then dstart * + * pixel_depth will always be a whole byte and most complex when source + * and destination require sub-byte addressing. + * + * Cherry pick the easy cases: + */ + if (pixel_depth > 8U) { - /* For pixel depths up to 4 bpp the 8-pixel mask can be expanded to fit - * into 32 bits, then a single loop over the bytes using the four byte - * values in the 32-bit mask can be used. For the 'display' option the - * expanded mask may also not require any masking within a byte. To - * make this work the PACKSWAP option must be taken into account - it - * simply requires the pixels to be reversed in each byte. - * - * The 'regular' case requires a mask for each of the first 6 passes, - * the 'display' case does a copy for the even passes in the range - * 0..6. This has already been handled in the test above. - * - * The masks are arranged as four bytes with the first byte to use in - * the lowest bits (little-endian) regardless of the order (PACKSWAP or - * not) of the pixels in each byte. - * - * NOTE: the whole of this logic depends on the caller of this function - * only calling it on rows appropriate to the pass. This function only - * understands the 'x' logic; the 'y' logic is handled by the caller. - * - * The following defines allow generation of compile time constant bit - * masks for each pixel depth and each possibility of swapped or not - * swapped bytes. Pass 'p' is in the range 0..6; 'x', a pixel index, - * is in the range 0..7; and the result is 1 if the pixel is to be - * copied in the pass, 0 if not. 'S' is for the sparkle method, 'B' - * for the block method. - * - * With some compilers a compile time expression of the general form: - * - * (shift >= 32) ? (a >> (shift-32)) : (b >> shift) - * - * Produces warnings with values of 'shift' in the range 33 to 63 - * because the right hand side of the ?: expression is evaluated by - * the compiler even though it isn't used. Microsoft Visual C (various - * versions) and the Intel C compiler are known to do this. To avoid - * this the following macros are used in 1.5.6. This is a temporary - * solution to avoid destabilizing the code during the release process. - */ -# if PNG_USE_COMPILE_TIME_MASKS -# define PNG_LSR(x,s) ((x)>>((s) & 0x1f)) -# define PNG_LSL(x,s) ((x)<<((s) & 0x1f)) -# else -# define PNG_LSR(x,s) ((x)>>(s)) -# define PNG_LSL(x,s) ((x)<<(s)) -# endif -# define S_COPY(p,x) (((p)<4 ? PNG_LSR(0x80088822,(3-(p))*8+(7-(x))) :\ - PNG_LSR(0xaa55ff00,(7-(p))*8+(7-(x)))) & 1) -# define B_COPY(p,x) (((p)<4 ? PNG_LSR(0xff0fff33,(3-(p))*8+(7-(x))) :\ - PNG_LSR(0xff55ff00,(7-(p))*8+(7-(x)))) & 1) + /* Convert to bytes: */ + const unsigned int pixel_bytes = pixel_depth >> 3; - /* Return a mask for pass 'p' pixel 'x' at depth 'd'. The mask is - * little endian - the first pixel is at bit 0 - however the extra - * parameter 's' can be set to cause the mask position to be swapped - * within each byte, to match the PNG format. This is done by XOR of - * the shift with 7, 6 or 4 for bit depths 1, 2 and 4. - */ -# define PIXEL_MASK(p,x,d,s) \ - (PNG_LSL(((PNG_LSL(1U,(d)))-1),(((x)*(d))^((s)?8-(d):0)))) - - /* Hence generate the appropriate 'block' or 'sparkle' pixel copy mask. - */ -# define S_MASKx(p,x,d,s) (S_COPY(p,x)?PIXEL_MASK(p,x,d,s):0) -# define B_MASKx(p,x,d,s) (B_COPY(p,x)?PIXEL_MASK(p,x,d,s):0) - - /* Combine 8 of these to get the full mask. For the 1-bpp and 2-bpp - * cases the result needs replicating, for the 4-bpp case the above - * generates a full 32 bits. - */ -# define MASK_EXPAND(m,d) ((m)*((d)==1?0x01010101:((d)==2?0x00010001:1))) - -# define S_MASK(p,d,s) MASK_EXPAND(S_MASKx(p,0,d,s) + S_MASKx(p,1,d,s) +\ - S_MASKx(p,2,d,s) + S_MASKx(p,3,d,s) + S_MASKx(p,4,d,s) +\ - S_MASKx(p,5,d,s) + S_MASKx(p,6,d,s) + S_MASKx(p,7,d,s), d) - -# define B_MASK(p,d,s) MASK_EXPAND(B_MASKx(p,0,d,s) + B_MASKx(p,1,d,s) +\ - B_MASKx(p,2,d,s) + B_MASKx(p,3,d,s) + B_MASKx(p,4,d,s) +\ - B_MASKx(p,5,d,s) + B_MASKx(p,6,d,s) + B_MASKx(p,7,d,s), d) - -#if PNG_USE_COMPILE_TIME_MASKS - /* Utility macros to construct all the masks for a depth/swap - * combination. The 's' parameter says whether the format is PNG - * (big endian bytes) or not. Only the three odd-numbered passes are - * required for the display/block algorithm. - */ -# define S_MASKS(d,s) { S_MASK(0,d,s), S_MASK(1,d,s), S_MASK(2,d,s),\ - S_MASK(3,d,s), S_MASK(4,d,s), S_MASK(5,d,s) } - -# define B_MASKS(d,s) { B_MASK(1,d,s), B_MASK(3,d,s), B_MASK(5,d,s) } - -# define DEPTH_INDEX(d) ((d)==1?0:((d)==2?1:2)) - - /* Hence the pre-compiled masks indexed by PACKSWAP (or not), depth and - * then pass: - */ - static PNG_CONST png_uint_32 row_mask[2/*PACKSWAP*/][3/*depth*/][6] = - { - /* Little-endian byte masks for PACKSWAP */ - { S_MASKS(1,0), S_MASKS(2,0), S_MASKS(4,0) }, - /* Normal (big-endian byte) masks - PNG format */ - { S_MASKS(1,1), S_MASKS(2,1), S_MASKS(4,1) } - }; - - /* display_mask has only three entries for the odd passes, so index by - * pass>>1. - */ - static PNG_CONST png_uint_32 display_mask[2][3][3] = - { - /* Little-endian byte masks for PACKSWAP */ - { B_MASKS(1,0), B_MASKS(2,0), B_MASKS(4,0) }, - /* Normal (big-endian byte) masks - PNG format */ - { B_MASKS(1,1), B_MASKS(2,1), B_MASKS(4,1) } - }; - -# define MASK(pass,depth,display,png)\ - ((display)?display_mask[png][DEPTH_INDEX(depth)][pass>>1]:\ - row_mask[png][DEPTH_INDEX(depth)][pass]) - -#else /* !PNG_USE_COMPILE_TIME_MASKS */ - /* This is the runtime alternative: it seems unlikely that this will - * ever be either smaller or faster than the compile time approach. - */ -# define MASK(pass,depth,display,png)\ - ((display)?B_MASK(pass,depth,png):S_MASK(pass,depth,png)) -#endif /* !PNG_USE_COMPILE_TIME_MASKS */ - - /* Use the appropriate mask to copy the required bits. In some cases - * the byte mask will be 0 or 0xff, optimize these cases. row_width is - * the number of pixels, but the code copies bytes, so it is necessary - * to special case the end. - */ - png_uint_32 pixels_per_byte = 8 / pixel_depth; - png_uint_32 mask; - -# ifdef PNG_READ_PACKSWAP_SUPPORTED - if (png_ptr->transformations & PNG_PACKSWAP) - mask = MASK(pass, pixel_depth, display, 0); - - else -# endif - mask = MASK(pass, pixel_depth, display, 1); + dp += dstart * pixel_bytes; for (;;) { - png_uint_32 m; + unsigned int c; - /* It doesn't matter in the following if png_uint_32 has more than - * 32 bits because the high bits always match those in m<<24; it is, - * however, essential to use OR here, not +, because of this. - */ - m = mask; - mask = (m >> 8) | (m << 24); /* rotate right to good compilers */ - m &= 0xff; + if (drep > row_width) + drep = row_width; - if (m != 0) /* something to copy */ - { - if (m != 0xff) - *dp = (png_byte)((*dp & ~m) | (*sp & m)); - else - *dp = *sp; - } + for (c=0U; c= row_width) + break; - row_width -= pixels_per_byte; - ++dp; - ++sp; + row_width -= doffset; + dp += (doffset-drep) * pixel_bytes; + sp += pixel_bytes; } } - else /* pixel_depth >= 8 */ + else if (pixel_depth == 8U) { - unsigned int bytes_to_copy, bytes_to_jump; - - /* Validate the depth - it must be a multiple of 8 */ - if (pixel_depth & 7) - png_error(png_ptr, "invalid user transform pixel depth"); - - pixel_depth >>= 3; /* now in bytes */ - row_width *= pixel_depth; - - /* Regardless of pass number the Adam 7 interlace always results in a - * fixed number of pixels to copy then to skip. There may be a - * different number of pixels to skip at the start though. + /* Optimize the common 1-byte per pixel case (typical case for palette + * mapped images): */ - { - unsigned int offset = PNG_PASS_START_COL(pass) * pixel_depth; + dp += dstart; - row_width -= offset; - dp += offset; - sp += offset; + for (;;) + { + if (drep > row_width) + drep = row_width; + + memset(dp, *sp++, drep); + + if (doffset >= row_width) + break; + + row_width -= doffset; + dp += doffset; } + } - /* Work out the bytes to copy. */ - if (display != 0) + else /* pixel_depth < 8 */ + { + /* Pixels are 1, 2 or 4 bits in size. */ + unsigned int spixel = *sp++; + unsigned int dbrep = pixel_depth * drep; + unsigned int spos = 0U; +# ifdef PNG_READ_PACKSWAP_SUPPORTED + const int lsb = + (png_ptr->row_format & PNG_FORMAT_FLAG_SWAPPED) != 0; +# endif /* READ_PACKSWAP */ + + if (dbrep >= 8U) { - /* When doing the 'block' algorithm the pixel in the pass gets - * replicated to adjacent pixels. This is why the even (0,2,4,6) - * passes are skipped above - the entire expanded row is copied. + /* brep must be greater than 1, the destination does not require + * sub-byte addressing except, maybe, at the end. + * + * db is the count of bytes required to replicate the source pixel + * drep times. */ - bytes_to_copy = (1<<((6-pass)>>1)) * pixel_depth; + debug((dbrep & 7U) == 0U); + dbrep >>= 3; + debug((dstart * pixel_depth & 7U) == 0U); + dp += (dstart * pixel_depth) >> 3; - /* But don't allow this number to exceed the actual row width. */ - if (bytes_to_copy > row_width) - bytes_to_copy = row_width; - } + for (;;) + { + /* Fill a byte with copies of the next pixel: */ + unsigned int spixel_rep = spixel; - else /* normal row; Adam7 only ever gives us one pixel to copy. */ - bytes_to_copy = pixel_depth; +# ifdef PNG_READ_PACKSWAP_SUPPORTED + if (lsb) + spixel_rep >>= spos; + else +# endif /* READ_PACKSWAP */ + spixel_rep >>= (8U-pixel_depth)-spos; - /* In Adam7 there is a constant offset between where the pixels go. */ - bytes_to_jump = PNG_PASS_COL_OFFSET(pass) * pixel_depth; - - /* And simply copy these bytes. Some optimization is possible here, - * depending on the value of 'bytes_to_copy'. Special case the low - * byte counts, which we know to be frequent. - * - * Notice that these cases all 'return' rather than 'break' - this - * avoids an unnecessary test on whether to restore the last byte - * below. - */ - switch (bytes_to_copy) - { - case 1: - for (;;) + switch (pixel_depth) { - *dp = *sp; - - if (row_width <= bytes_to_jump) - return; - - dp += bytes_to_jump; - sp += bytes_to_jump; - row_width -= bytes_to_jump; + case 1U: spixel_rep &= 1U; spixel_rep |= spixel_rep << 1; + /*FALL THROUGH*/ + case 2U: spixel_rep &= 3U; spixel_rep |= spixel_rep << 2; + /*FALL THROUGH*/ + case 4U: spixel_rep &= 15U; spixel_rep |= spixel_rep << 4; + /*FALL THROUGH*/ + default: break; } - case 2: - /* There is a possibility of a partial copy at the end here; this - * slows the code down somewhat. + /* This may leave some pixels unwritten when there is a partial + * byte write required at the end: */ - do + if (drep > row_width) + drep = row_width, dbrep = (pixel_depth * drep) >> 3; + + memset(dp, spixel_rep, dbrep); + + if (doffset >= row_width) { - dp[0] = sp[0], dp[1] = sp[1]; - - if (row_width <= bytes_to_jump) - return; - - sp += bytes_to_jump; - dp += bytes_to_jump; - row_width -= bytes_to_jump; - } - while (row_width > 1); - - /* And there can only be one byte left at this point: */ - *dp = *sp; - return; - - case 3: - /* This can only be the RGB case, so each copy is exactly one - * pixel and it is not necessary to check for a partial copy. - */ - for(;;) - { - dp[0] = sp[0], dp[1] = sp[1], dp[2] = sp[2]; - - if (row_width <= bytes_to_jump) - return; - - sp += bytes_to_jump; - dp += bytes_to_jump; - row_width -= bytes_to_jump; - } - - default: -#if PNG_ALIGN_TYPE != PNG_ALIGN_NONE - /* Check for double byte alignment and, if possible, use a - * 16-bit copy. Don't attempt this for narrow images - ones that - * are less than an interlace panel wide. Don't attempt it for - * wide bytes_to_copy either - use the memcpy there. - */ - if (bytes_to_copy < 16 /*else use memcpy*/ && - png_isaligned(dp, png_uint_16) && - png_isaligned(sp, png_uint_16) && - bytes_to_copy % (sizeof (png_uint_16)) == 0 && - bytes_to_jump % (sizeof (png_uint_16)) == 0) - { - /* Everything is aligned for png_uint_16 copies, but try for - * png_uint_32 first. + /* End condition; were all 'drep' pixels written at the end? */ - if (png_isaligned(dp, png_uint_32) && - png_isaligned(sp, png_uint_32) && - bytes_to_copy % (sizeof (png_uint_32)) == 0 && - bytes_to_jump % (sizeof (png_uint_32)) == 0) + drep = (pixel_depth * drep - (dbrep << 3)); + + if (drep) { - png_uint_32p dp32 = png_aligncast(png_uint_32p,dp); - png_const_uint_32p sp32 = png_aligncastconst( - png_const_uint_32p, sp); - size_t skip = (bytes_to_jump-bytes_to_copy) / - (sizeof (png_uint_32)); + unsigned int mask; - do - { - size_t c = bytes_to_copy; - do - { - *dp32++ = *sp32++; - c -= (sizeof (png_uint_32)); - } - while (c > 0); + debug(drep < 8U); + dp += dbrep; - if (row_width <= bytes_to_jump) - return; - - dp32 += skip; - sp32 += skip; - row_width -= bytes_to_jump; - } - while (bytes_to_copy <= row_width); - - /* Get to here when the row_width truncates the final copy. - * There will be 1-3 bytes left to copy, so don't try the - * 16-bit loop below. + /* Set 'mask' to have 0's where *dp must be overwritten + * with spixel_rep: */ - dp = (png_bytep)dp32; - sp = (png_const_bytep)sp32; - do - *dp++ = *sp++; - while (--row_width > 0); - return; +# ifdef PNG_READ_PACKSWAP_SUPPORTED + if (lsb) + mask = 0xff << drep; + else +# endif /* READ_PACKSWAP */ + mask = 0xff >> drep; + + *dp = PNG_BYTE((*dp & mask) | (spixel_rep & ~mask)); } - /* Else do it in 16-bit quantities, but only if the size is - * not too large. - */ + break; + } + + row_width -= doffset; + dp += (doffset * pixel_depth) >> 3; + spos += pixel_depth; + if (spos == 8U) + spixel = *sp++, spos = 0U; + } /* for (;;) */ + } /* pixel_depth * drep >= 8 */ + + else /* pixel_depth * drep < 8 */ + { + /* brep may be 1, pixel_depth may be 1, 2 or 4, dbrep is the number + * of bits to set. + */ + unsigned int bstart = dstart * pixel_depth; /* in bits */ + unsigned int dpixel; + + dp += bstart >> 3; + bstart &= 7U; + dpixel = *dp; + + /* dpixel: current *dp, being modified + * bstart: bit offset within dpixel + * drep: pixel size to write (used as a check against row_width) + * doffset: pixel step to next written destination + * + * spixel: current *sp, being read, and: + * spixel_rep: current pixel, replicated to fill a byte + * spos: bit offset within spixel + * + * Set dbrep to a mask for the bits to set: + */ + dbrep = (1U<>= spos; else - { - png_uint_16p dp16 = png_aligncast(png_uint_16p, dp); - png_const_uint_16p sp16 = png_aligncastconst( - png_const_uint_16p, sp); - size_t skip = (bytes_to_jump-bytes_to_copy) / - (sizeof (png_uint_16)); +# endif /* READ_PACKSWAP */ + spixel_rep >>= (8U-pixel_depth)-spos; - do - { - size_t c = bytes_to_copy; - do - { - *dp16++ = *sp16++; - c -= (sizeof (png_uint_16)); - } - while (c > 0); - - if (row_width <= bytes_to_jump) - return; - - dp16 += skip; - sp16 += skip; - row_width -= bytes_to_jump; - } - while (bytes_to_copy <= row_width); - - /* End of row - 1 byte left, bytes_to_copy > row_width: */ - dp = (png_bytep)dp16; - sp = (png_const_bytep)sp16; - do - *dp++ = *sp++; - while (--row_width > 0); - return; - } - } -#endif /* PNG_ALIGN_ code */ - - /* The true default - use a memcpy: */ - for (;;) + switch (pixel_depth) { - memcpy(dp, sp, bytes_to_copy); - - if (row_width <= bytes_to_jump) - return; - - sp += bytes_to_jump; - dp += bytes_to_jump; - row_width -= bytes_to_jump; - if (bytes_to_copy > row_width) - bytes_to_copy = row_width; + case 1U: spixel_rep &= 1U; spixel_rep |= spixel_rep << 1; + /*FALL THROUGH*/ + case 2U: spixel_rep &= 3U; spixel_rep |= spixel_rep << 2; + /*FALL THROUGH*/ + case 4U: spixel_rep &= 15U; spixel_rep |= spixel_rep << 4; + /*FALL THROUGH*/ + default: break; } - } - /* NOT REACHED*/ - } /* pixel_depth >= 8 */ + /* This may leave some pixels unwritten when there is a partial + * byte write required at the end: + */ + if (drep > row_width) + drep = row_width, dbrep = (1U<<(pixel_depth*drep))-1U; - /* Here if pixel_depth < 8 to check 'end_ptr' below. */ - } - else -#endif + { + unsigned int mask; - /* If here then the switch above wasn't used so just memcpy the whole row - * from the temporary row buffer (notice that this overwrites the end of the - * destination row if it is a partial byte.) - */ - memcpy(dp, sp, PNG_ROWBYTES(pixel_depth, row_width)); + /* Mask dbrep bits at bstart: */ +# ifdef PNG_READ_PACKSWAP_SUPPORTED + if (lsb) + mask = bstart; + else +# endif /* READ_PACKSWAP */ + mask = (8U-pixel_depth)-bstart; + mask = dbrep << mask; - /* Restore the overwritten bits from the last byte if necessary. */ - if (end_ptr != NULL) - *end_ptr = (png_byte)((end_byte & end_mask) | (*end_ptr & ~end_mask)); + dpixel &= ~mask; + dpixel |= spixel_rep & mask; + } + + if (doffset >= row_width) + { + *dp = PNG_BYTE(dpixel); + break; + } + + row_width -= doffset; + bstart += doffset * pixel_depth; + + if (bstart >= 8U) + { + *dp = PNG_BYTE(dpixel); + dp += bstart >> 3; + bstart &= 7U; + dpixel = *dp; + } + + spos += pixel_depth; + if (spos == 8U) + spixel = *sp++, spos = 0U; + } /* for (;;) */ + } /* pixel_depth * drep < 8 */ + } /* pixel_depth < 8 */ + } /* not a simple copy */ } -#ifdef PNG_READ_INTERLACING_SUPPORTED -void /* PRIVATE */ -png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass, - png_uint_32 transformations /* Because these may affect the byte layout */) +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +void PNGAPI +png_progressive_combine_row(png_const_structrp png_ptr, png_bytep old_row, + png_const_bytep new_row) { - /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ - /* Offset to next interlace block */ - static PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; - - png_debug(1, "in png_do_read_interlace"); - if (row != NULL && row_info != NULL) + /* new_row is a flag here - if it is NULL then the app callback was called + * from an empty row (see the calls to png_struct::row_fn above), otherwise + * it must be png_struct::transformed_row + */ + if (png_ptr != NULL && new_row != NULL) { - png_uint_32 final_width; - - final_width = row_info->width * png_pass_inc[pass]; - - switch (row_info->pixel_depth) + if (new_row != png_ptr->row_buffer +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + && new_row != png_ptr->transformed_row +# endif /* TRANSFORM_MECH */ + ) + png_app_error(png_ptr, "invalid call to png_progressive_combine_row"); + else { - case 1: + png_uint_32 width = png_ptr->width; + + if (png_ptr->interlaced == PNG_INTERLACE_ADAM7) { - png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 3); - png_bytep dp = row + (png_size_t)((final_width - 1) >> 3); - int sshift, dshift; - int s_start, s_end, s_inc; - int jstop = png_pass_inc[pass]; - png_byte v; - png_uint_32 i; - int j; - -#ifdef PNG_READ_PACKSWAP_SUPPORTED - if (transformations & PNG_PACKSWAP) - { - sshift = (int)((row_info->width + 7) & 0x07); - dshift = (int)((final_width + 7) & 0x07); - s_start = 7; - s_end = 0; - s_inc = -1; - } - - else -#endif - { - sshift = 7 - (int)((row_info->width + 7) & 0x07); - dshift = 7 - (int)((final_width + 7) & 0x07); - s_start = 0; - s_end = 7; - s_inc = 1; - } - - for (i = 0; i < row_info->width; i++) - { - v = (png_byte)((*sp >> sshift) & 0x01); - for (j = 0; j < jstop; j++) - { - unsigned int tmp = *dp & (0x7f7f >> (7 - dshift)); - tmp |= v << dshift; - *dp = (png_byte)(tmp & 0xff); - - if (dshift == s_end) - { - dshift = s_start; - dp--; - } - - else - dshift += s_inc; - } - - if (sshift == s_end) - { - sshift = s_start; - sp--; - } - - else - sshift += s_inc; - } - break; + const unsigned int pass = png_ptr->pass; + width = PNG_PASS_COLS(width, pass); } - case 2: - { - png_bytep sp = row + (png_uint_32)((row_info->width - 1) >> 2); - png_bytep dp = row + (png_uint_32)((final_width - 1) >> 2); - int sshift, dshift; - int s_start, s_end, s_inc; - int jstop = png_pass_inc[pass]; - png_uint_32 i; - -#ifdef PNG_READ_PACKSWAP_SUPPORTED - if (transformations & PNG_PACKSWAP) - { - sshift = (int)(((row_info->width + 3) & 0x03) << 1); - dshift = (int)(((final_width + 3) & 0x03) << 1); - s_start = 6; - s_end = 0; - s_inc = -2; - } - - else -#endif - { - sshift = (int)((3 - ((row_info->width + 3) & 0x03)) << 1); - dshift = (int)((3 - ((final_width + 3) & 0x03)) << 1); - s_start = 0; - s_end = 6; - s_inc = 2; - } - - for (i = 0; i < row_info->width; i++) - { - png_byte v; - int j; - - v = (png_byte)((*sp >> sshift) & 0x03); - for (j = 0; j < jstop; j++) - { - unsigned int tmp = *dp & (0x3f3f >> (6 - dshift)); - tmp |= v << dshift; - *dp = (png_byte)(tmp & 0xff); - - if (dshift == s_end) - { - dshift = s_start; - dp--; - } - - else - dshift += s_inc; - } - - if (sshift == s_end) - { - sshift = s_start; - sp--; - } - - else - sshift += s_inc; - } - break; - } - - case 4: - { - png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 1); - png_bytep dp = row + (png_size_t)((final_width - 1) >> 1); - int sshift, dshift; - int s_start, s_end, s_inc; - png_uint_32 i; - int jstop = png_pass_inc[pass]; - -#ifdef PNG_READ_PACKSWAP_SUPPORTED - if (transformations & PNG_PACKSWAP) - { - sshift = (int)(((row_info->width + 1) & 0x01) << 2); - dshift = (int)(((final_width + 1) & 0x01) << 2); - s_start = 4; - s_end = 0; - s_inc = -4; - } - - else -#endif - { - sshift = (int)((1 - ((row_info->width + 1) & 0x01)) << 2); - dshift = (int)((1 - ((final_width + 1) & 0x01)) << 2); - s_start = 0; - s_end = 4; - s_inc = 4; - } - - for (i = 0; i < row_info->width; i++) - { - png_byte v = (png_byte)((*sp >> sshift) & 0x0f); - int j; - - for (j = 0; j < jstop; j++) - { - unsigned int tmp = *dp & (0xf0f >> (4 - dshift)); - tmp |= v << dshift; - *dp = (png_byte)(tmp & 0xff); - - if (dshift == s_end) - { - dshift = s_start; - dp--; - } - - else - dshift += s_inc; - } - - if (sshift == s_end) - { - sshift = s_start; - sp--; - } - - else - sshift += s_inc; - } - break; - } - - default: - { - png_size_t pixel_bytes = (row_info->pixel_depth >> 3); - - png_bytep sp = row + (png_size_t)(row_info->width - 1) - * pixel_bytes; - - png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes; - - int jstop = png_pass_inc[pass]; - png_uint_32 i; - - for (i = 0; i < row_info->width; i++) - { - png_byte v[8]; /* SAFE; pixel_depth does not exceed 64 */ - int j; - - memcpy(v, sp, pixel_bytes); - - for (j = 0; j < jstop; j++) - { - memcpy(dp, v, pixel_bytes); - dp -= pixel_bytes; - } - - sp -= pixel_bytes; - } - break; - } + combine_row(png_ptr, old_row, new_row, 0U, width, 1/*blocky display*/); } - - row_info->width = final_width; - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, final_width); } -#ifndef PNG_READ_PACKSWAP_SUPPORTED - PNG_UNUSED(transformations) /* Silence compiler warning */ -#endif } -#endif /* PNG_READ_INTERLACING_SUPPORTED */ +#endif /* PROGRESSIVE_READ */ +#else /* !READ_INTERLACING */ + /* No read deinterlace support, so 'combine' always reduces to 'copy', there + * is no 'display' argument: + */ +# define combine_row(pp, dp, sp, x, w, display)\ + copy_row(pp, dp, sp, x, w, 0/*!clear*/) +#endif /* !READ_INTERLACING */ static void -png_read_filter_row_sub(png_row_infop row_info, png_bytep row, - png_const_bytep prev_row) +png_read_filter_row_sub(png_alloc_size_t row_bytes, unsigned int bpp, + png_bytep row, png_const_bytep prev_row, png_const_bytep prev_pixels) { - png_size_t i; - png_size_t istop = row_info->rowbytes; - unsigned int bpp = (row_info->pixel_depth + 7) >> 3; - png_bytep rp = row + bpp; + while (row_bytes >= bpp) + { + unsigned int i; + + for (i=0; i 0) { - *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff); - rp++; + *row = PNG_BYTE(*row + *prev_row); + ++row; + ++prev_row; + --row_bytes; + } + + PNG_UNUSED(bpp) + PNG_UNUSED(prev_pixels) +} + +static void +png_read_filter_row_avg(png_alloc_size_t row_bytes, unsigned int bpp, + png_bytep row, png_const_bytep prev_row, png_const_bytep prev_pixels) +{ + while (row_bytes >= bpp) + { + unsigned int i; + + for (i=0; irowbytes; - png_bytep rp = row; - png_const_bytep pp = prev_row; + png_const_bytep rp_end = row + row_bytes; + png_byte a, c; - for (i = 0; i < istop; i++) - { - *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); - rp++; - } -} + /* prev_pixels stores pixel a then c */ + a = prev_pixels[0]; + c = prev_pixels[1]; -static void -png_read_filter_row_avg(png_row_infop row_info, png_bytep row, - png_const_bytep prev_row) -{ - png_size_t i; - png_bytep rp = row; - png_const_bytep pp = prev_row; - unsigned int bpp = (row_info->pixel_depth + 7) >> 3; - png_size_t istop = row_info->rowbytes - bpp; - - for (i = 0; i < bpp; i++) - { - *rp = (png_byte)(((int)(*rp) + - ((int)(*pp++) / 2 )) & 0xff); - - rp++; - } - - for (i = 0; i < istop; i++) - { - *rp = (png_byte)(((int)(*rp) + - (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff); - - rp++; - } -} - -static void -png_read_filter_row_paeth_1byte_pixel(png_row_infop row_info, png_bytep row, - png_const_bytep prev_row) -{ - png_bytep rp_end = row + row_info->rowbytes; - int a, c; - - /* First pixel/byte */ - c = *prev_row++; - a = *row + c; - *row++ = (png_byte)a; - - /* Remainder */ while (row < rp_end) { - int b, pa, pb, pc, p; + png_byte b; + int pa, pb, pc, p; - a &= 0xff; /* From previous iteration or start */ b = *prev_row++; p = b - c; @@ -3792,43 +3593,39 @@ png_read_filter_row_paeth_1byte_pixel(png_row_infop row_info, png_bytep row, /* Find the best predictor, the least of pa, pb, pc favoring the earlier * ones in the case of a tie. */ - if (pb < pa) pa = pb, a = b; + if (pb < pa) + { + pa = pb; a = b; + } if (pc < pa) a = c; /* Calculate the current pixel in a, and move the previous row pixel to c * for the next time round the loop */ c = b; - a += *row; - *row++ = (png_byte)a; + a = 0xFFU & (a + *row); + *row++ = a; } + + PNG_UNUSED(bpp) } static void -png_read_filter_row_paeth_multibyte_pixel(png_row_infop row_info, png_bytep row, - png_const_bytep prev_row) +png_read_filter_row_paeth_multibyte_pixel(png_alloc_size_t row_bytes, + unsigned int bpp, png_bytep row, png_const_bytep prev_row, + png_const_bytep prev_pixels) { - int bpp = (row_info->pixel_depth + 7) >> 3; png_bytep rp_end = row + bpp; - /* Process the first pixel in the row completely (this is the same as 'up' - * because there is only one candidate predictor for the first row). - */ + /* 'a' and 'c' for the first pixel come from prev_pixels: */ while (row < rp_end) { - int a = *row + *prev_row++; - *row++ = (png_byte)a; - } + png_byte a, b, c; + int pa, pb, pc, p; - /* Remainder */ - rp_end += row_info->rowbytes - bpp; - - while (row < rp_end) - { - int a, b, c, pa, pb, pc, p; - - c = *(prev_row - bpp); - a = *(row - bpp); + /* prev_pixels stores bpp bytes for 'a', the bpp for 'c': */ + c = *(prev_pixels+bpp); + a = *prev_pixels++; b = *prev_row++; p = b - c; @@ -3844,16 +3641,54 @@ png_read_filter_row_paeth_multibyte_pixel(png_row_infop row_info, png_bytep row, pc = (p + pc) < 0 ? -(p + pc) : p + pc; # endif - if (pb < pa) pa = pb, a = b; + if (pb < pa) + { + pa = pb; a = b; + } if (pc < pa) a = c; - a += *row; - *row++ = (png_byte)a; + a = 0xFFU & (a + *row); + *row++ = a; + } + + /* Remainder */ + rp_end += row_bytes - bpp; + + while (row < rp_end) + { + png_byte a, b, c; + int pa, pb, pc, p; + + c = *(prev_row-bpp); + a = *(row-bpp); + b = *prev_row++; + + p = b - c; + pc = a - c; + +# ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +# else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +# endif + + if (pb < pa) + { + pa = pb; a = b; + } + if (pc < pa) a = c; + + a = 0xFFU & (a + *row); + *row++ = a; } } static void -png_init_filter_functions(png_structrp pp) +png_init_filter_functions(png_structrp pp, unsigned int bpp) /* This function is called once for every PNG image (except for PNG images * that only use PNG_FILTER_VALUE_NONE for all rows) to set the * implementations required to reverse the filtering of PNG rows. Reversing @@ -3864,8 +3699,6 @@ png_init_filter_functions(png_structrp pp) * interlacing causes the actual row width to vary. */ { - unsigned int bpp = (pp->pixel_depth + 7) >> 3; - pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub; pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up; pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg; @@ -3889,562 +3722,1071 @@ png_init_filter_functions(png_structrp pp) #endif } -void /* PRIVATE */ -png_read_filter_row(png_structrp pp, png_row_infop row_info, png_bytep row, - png_const_bytep prev_row, int filter) +/* This is an IDAT specific wrapper for png_zlib_inflate; the input is already + * in png_ptr->zstream.{next,avail}_in however the output uses the full + * capabilities of png_zlib_inflate, returning a byte count of bytes read. + * This is just a convenience for IDAT processing. + * + * NOTE: this function works just fine after the zstream has ended, it just + * fills the buffer with zeros (outputing an error message once.) + */ +static png_alloc_size_t +png_inflate_IDAT(png_structrp png_ptr, int finish, + /* OUTPUT: */ png_bytep output, png_alloc_size_t output_size) { - /* OPTIMIZATION: DO NOT MODIFY THIS FUNCTION, instead #define - * PNG_FILTER_OPTIMIZATIONS to a function that overrides the generic - * implementations. See png_init_filter_functions above. + /* Expect Z_OK if !finsh and Z_STREAM_END if finish; if Z_STREAM_END is + * delivered when finish is not set the IDAT stream is truncated, if Z_OK is + * delivered when finish is set this is harmless and indicates that the + * stream end code has not been read. + * + * finish should be set as follows: + * + * 0: not reading the last row, stream not expected to end + * 1: reading the last row, stream expected to end + * 2: looking for stream end after the last row has been read, expect no + * more output and stream end. */ - if (filter > PNG_FILTER_VALUE_NONE && filter < PNG_FILTER_VALUE_LAST) + png_alloc_size_t original_size = output_size; + int ret = Z_STREAM_END; /* In case it ended ok before. */ + + if (!png_ptr->zstream_ended) { - if (pp->read_filter[0] == NULL) - png_init_filter_functions(pp); + png_const_bytep next_in = png_ptr->zstream.next_in; + png_uint_32 avail_in = png_ptr->zstream.avail_in; - pp->read_filter[filter-1](row_info, row, prev_row); - } -} + ret = png_zlib_inflate(png_ptr, png_IDAT, finish, + &next_in, &avail_in, &output, &output_size/*remaining*/); -#ifdef PNG_SEQUENTIAL_READ_SUPPORTED -void /* PRIVATE */ -png_read_IDAT_data(png_structrp png_ptr, png_bytep output, - png_alloc_size_t avail_out) -{ - /* Loop reading IDATs and decompressing the result into output[avail_out] */ - png_ptr->zstream.next_out = output; - png_ptr->zstream.avail_out = 0; /* safety: set below */ + debug(next_in == png_ptr->zstream.next_in); + debug(avail_in == png_ptr->zstream.avail_in); + debug(output == png_ptr->zstream.next_out); + /* But zstream.avail_out may be truncated to uInt */ - if (output == NULL) - avail_out = 0; - - do - { - int ret; - png_byte tmpbuf[PNG_INFLATE_BUF_SIZE]; - - if (png_ptr->zstream.avail_in == 0) + switch (ret) { - uInt avail_in; - png_bytep buffer; - - while (png_ptr->idat_size == 0) - { - png_crc_finish(png_ptr, 0); - - png_ptr->idat_size = png_read_chunk_header(png_ptr); - /* This is an error even in the 'check' case because the code just - * consumed a non-IDAT header. + case Z_STREAM_END: + /* The caller must set finish on the last row of the image (not + * the last row of the pass!) */ - if (png_ptr->chunk_name != png_IDAT) - png_error(png_ptr, "Not enough image data"); - } + debug(png_ptr->zstream_ended); - avail_in = png_ptr->IDAT_read_size; + if (!finish) /* early end */ + break; - if (avail_in > png_ptr->idat_size) - avail_in = (uInt)png_ptr->idat_size; + if (output_size > 0) /* incomplete read */ + { + if (finish == 2) /* looking for end; it has been found */ + return original_size - output_size; - /* A PNG with a gradually increasing IDAT size will defeat this attempt - * to minimize memory usage by causing lots of re-allocs, but - * realistically doing IDAT_read_size re-allocs is not likely to be a - * big problem. - */ - buffer = png_read_buffer(png_ptr, avail_in, 0/*error*/); + /* else those bytes are really needed: */ + break; + } - png_crc_read(png_ptr, buffer, avail_in); - png_ptr->idat_size -= avail_in; + /* else: FALL THROUGH: success */ - png_ptr->zstream.next_in = buffer; - png_ptr->zstream.avail_in = avail_in; + case Z_BUF_ERROR: + /* this is the success case: output or input is empty: */ + original_size -= output_size; /* bytes written */ + + if (output_size > 0) + { + /* Some output still needed; if the next chunk is known + * to not be an IDAT then this is the truncation case. + */ + affirm(avail_in == 0); + + if ((png_ptr->mode & PNG_AFTER_IDAT) != 0) + { + /* Zlib doesn't know we are out of data, so this must be + * done here: + */ + png_ptr->zstream_ended = 1; + break; + } + } + + return original_size; /* bytes written */ + + default: + /* error */ + break; } - /* And set up the output side. */ - if (output != NULL) /* standard read */ - { - uInt out = ZLIB_IO_MAX; - - if (out > avail_out) - out = (uInt)avail_out; - - avail_out -= out; - png_ptr->zstream.avail_out = out; - } - - else /* after last row, checking for end */ - { - png_ptr->zstream.next_out = tmpbuf; - png_ptr->zstream.avail_out = (sizeof tmpbuf); - } - - /* Use NO_FLUSH; this gives zlib the maximum opportunity to optimize the - * process. If the LZ stream is truncated the sequential reader will - * terminally damage the stream, above, by reading the chunk header of the - * following chunk (it then exits with png_error). - * - * TODO: deal more elegantly with truncated IDAT lists. + /* The 'ended' flag should always be set if we get here, the success + * cases where the LZ stream hasn't reached an end or an error leave + * the function at the return above. */ - ret = inflate(&png_ptr->zstream, Z_NO_FLUSH); - - /* Take the unconsumed output back. */ - if (output != NULL) - avail_out += png_ptr->zstream.avail_out; - - else /* avail_out counts the extra bytes */ - avail_out += (sizeof tmpbuf) - png_ptr->zstream.avail_out; - - png_ptr->zstream.avail_out = 0; - - if (ret == Z_STREAM_END) - { - /* Do this for safety; we won't read any more into this row. */ - png_ptr->zstream.next_out = NULL; - - png_ptr->mode |= PNG_AFTER_IDAT; - png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; - - if (png_ptr->zstream.avail_in > 0 || png_ptr->idat_size > 0) - png_chunk_benign_error(png_ptr, "Extra compressed data"); - break; - } - - if (ret != Z_OK) - { - png_zstream_error(png_ptr, ret); - - if (output != NULL) - png_chunk_error(png_ptr, png_ptr->zstream.msg); - - else /* checking */ - { - png_chunk_benign_error(png_ptr, png_ptr->zstream.msg); - return; - } - } - } while (avail_out > 0); - - if (avail_out > 0) - { - /* The stream ended before the image; this is the same as too few IDATs so - * should be handled the same way. - */ - if (output != NULL) - png_error(png_ptr, "Not enough image data"); - - else /* the deflate stream contained extra data */ - png_chunk_benign_error(png_ptr, "Too much image data"); + debug(png_ptr->zstream_ended); } + + /* This is the error return case; there was missing data, or an error. + * Either continue with a warning (once; hence the zstream_error flag) + * or png_error. + */ + if (!png_ptr->zstream_error) /* first time */ + { +#ifdef PNG_BENIGN_READ_ERRORS_SUPPORTED + switch (png_ptr->IDAT_error_action) + { + case PNG_ERROR: + if(!strncmp(png_ptr->zstream.msg,"incorrect data check",20)) + { + if (png_ptr->current_crc != crc_quiet_use) + png_chunk_warning(png_ptr, "ADLER32 checksum mismatch"); + } + + else + { + png_chunk_error(png_ptr, png_ptr->zstream.msg); + } + break; + + case PNG_WARN: + png_chunk_warning(png_ptr, png_ptr->zstream.msg); + break; + + default: /* ignore */ + /* Keep going */ + break; + } +#else + { + if(!strncmp(png_ptr->zstream.msg,"incorrect data check",20)) + png_chunk_warning(png_ptr, "ADLER32 checksum mismatch"); + else + png_chunk_error(png_ptr, png_ptr->zstream.msg); + } +#endif /* !BENIGN_ERRORS */ + + /* And prevent the report about too many IDATs on streams with internal + * LZ errors: + */ + png_ptr->zstream_error = 1; + } + + /* This is the error recovery case; fill the buffer with zeros. This is + * safe because it makes the filter byte 'NONE' and the row fairly innocent. + */ + memset(output, 0, output_size); + return original_size; +} + +/* SHARED IDAT HANDLING. + * + * This is the 1.7+ common read code; shared by both the progressive and + * sequential readers. + */ +/* Initialize the row buffers, etc. */ +void /* PRIVATE */ +png_read_start_IDAT(png_structrp png_ptr) +{ +# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + /* This won't work at all if the app turned on unknown handling for IDAT + * chunks; the first IDAT has already been consumed! + */ + if (png_ptr->known_unknown & 1U) + png_error(png_ptr, "Attempt to read image with unknown IDAT"); +# endif /* HANDLE_AS_UNKNOWN */ + + /* This is a missing read of the header information; we still haven't + * countered the first IDAT chunk. This can only happen in the sequential + * reader if the app didn't call png_read_info. + */ + if (png_ptr->chunk_name != png_IDAT) + png_error(png_ptr, "Missing call to png_read_info"); + + /* Two things need to happen: first work out the effect of any + * transformations (if supported) on the row size, second, allocate + * row_buffer and claim the zstream. + */ + png_init_row_info(png_ptr); + + /* Now allocate the row buffer and, if that succeeds, claim the zstream. + */ + png_ptr->row_buffer = png_voidcast(png_bytep, png_malloc(png_ptr, + png_calc_rowbytes(png_ptr, PNG_PIXEL_DEPTH(*png_ptr), png_ptr->width))); + + if (png_inflate_claim(png_ptr, png_IDAT) != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg); +} + +/* The process function gets called when there is some IDAT data to process + * and it just does the right thing with it. The zstream must have been claimed + * (owner png_IDAT) and the input data is in zstream.{next,avail}_in. The + * output next_{in,out} must not be changed by the caller; it is used + * internally. + * + * Result codes are as follows: + * + * png_row_incomplete: Insufficient IDAT data (from zstream) was present to + * process the next row. zstream.avail_in will be 0. + * png_row_process: A new row is available in the input buffer, it should be + * handled before the next call (if any) to this function. + * png_row_repeat: For interlaced images (only) this row is not in the pass, + * however the existing buffer may be displayed in lieu; if doing the + * 'blocky' (not 'sparkle') display the row should be displayed, + * otherwise treat as: + * png_row_skip: For interlaced images (only) the interlace pass has no data + * appropriate to this row, it should be skipped. + * + * In both of the two cases zstream.avail_in may be non-0, indicating that some + * IDAT data at zstream.next_in remains to be consumed. This data must be + * preserved and preset at the next call to the function. + * + * The function may also call png_error if an unrecoverable error occurs. + * + * The caller passes in a callback function and parameter to be called when row + * data is available. The callback is called repeatedly for each row to handle + * all the transformed row data. + */ +png_row_op /*PRIVATE*/ +png_read_process_IDAT(png_structrp png_ptr, png_bytep transformed_row, + png_bytep display_row, int save_row) +{ + /* Common sub-expressions. These are all constant across the whole PNG, but + * are recalculated here each time because this is fast and it only happens + * once per row + once per block of input data. + */ + const unsigned int max_pixels = png_max_pixel_block(png_ptr); + const unsigned int pixel_depth = png_ptr->row_input_pixel_depth; + /* The number of input bytes read each time (cannot overflow because it is + * limited by PNG_ROW_BUFFER_SIZE): + */ + const unsigned int input_byte_count = (max_pixels * pixel_depth) / 8U; + const unsigned int bpp = (pixel_depth+0x7U)>>3; + const png_uint_32 width = png_ptr->width; + const unsigned int interlaced = png_ptr->interlaced != PNG_INTERLACE_NONE; + + png_uint_32 row_number = png_ptr->row_number; + unsigned int pass = png_ptr->pass; + enum anonymous { + start_of_row = 0U, /* at the start of the row; read a filter byte */ + need_row_bytes = 2U, /* reading the row */ + processing_row = 3U /* control returned to caller to process the row */ + } state = png_upcast(enum anonymous, png_ptr->row_state); + + /* The caller is responsible for calling png_read_start_IDAT: */ + affirm(png_ptr->zowner == png_IDAT); + + /* Basic sanity checks: */ + affirm(pixel_depth > 0U && pixel_depth <= 64U && + input_byte_count <= PNG_ROW_BUFFER_SIZE && + pixel_depth <= 8U*PNG_MAX_PIXEL_BYTES); + + for (;;) switch (state) + { + png_alloc_size_t row_bytes_processed; + png_alloc_size_t bytes_read; /* bytes in pixel_buffer */ + png_uint_32 pass_width; + png_byte row_filter; + union + { + PNG_ROW_BUFFER_ALIGN_TYPE force_buffer_alignment; + png_byte buffer[16U]; + } previous_pixels; + union + { + PNG_ROW_BUFFER_ALIGN_TYPE force_buffer_alignment; + png_byte buffer[PNG_ROW_BUFFER_SIZE]; + } pixel_buffer; + + case need_row_bytes: + /* The above variables need to be restored: */ + row_bytes_processed = png_ptr->row_bytes_read; + bytes_read = row_bytes_processed % input_byte_count; + row_bytes_processed -= bytes_read; + + pass_width = width; + if (interlaced) + pass_width = PNG_PASS_COLS(pass_width, pass); + + memcpy(pixel_buffer.buffer, png_ptr->scratch, bytes_read); + memcpy(previous_pixels.buffer, png_ptr->scratch+bytes_read, 2*bpp); + row_filter = png_ptr->scratch[bytes_read+2*bpp]; + + goto pixel_loop; + + case processing_row: + /* When there was a previous row (not at the start of the image) the + * row number needs to be updated and, possibly, the pass number. + */ + if (++row_number == png_ptr->height) + { + affirm(interlaced && pass < 6); /* else too many calls */ + + /* Start a new pass: there never is a pending filter byte so it + * is always necessary to read the filter byte of the next row. + */ + png_ptr->pass = ++pass & 0x7; + row_number = 0U; + } /* end of pass */ + + png_ptr->row_number = row_number; + + /* This is a new row, but it may not be in the pass data so it + * may be possible to simply return control to the caller to + * skip it or use the previous row as appropriate. + */ + if (interlaced) + { + debug(pass <= 6); + + /* This macro cannot overflow because the PNG width (and height) + * have already been checked to ensure that they are less than + * 2^31 (i.e. they are 31-bit values, not 32-bit values.) + */ + pass_width = PNG_PASS_COLS(width, pass); + + /* On average most rows are skipped, so do this first: */ + if (pass_width == 0 || + !PNG_ROW_IN_INTERLACE_PASS(row_number, pass)) + { + /* Using the PNG specification numbering (pass+1), passes 1, + * 2, 4, 6 contribute to all the rows in 'block' interlaced + * filling mode. Pass 3 contributes to four rows (5,6,7,8), + * pass 5 to two rows (3,4 then 7,8) and pass 7 only to one + * (the one on which it is processed). have_row must be set + * appropriately; it is set when a row is processed (end of + * this function) and remains set while the 'block' mode of + * interlace handling should reuse the previous row for this + * row. + * + * Each pass row can be used in a fixed number of rows, shown + * in 'rows' below, the '*' indicates that the row is actually + * in the pass, the '^' that the previous '*' row is used in + * block display update and the '@' that the pass doesn't + * contribte at all to that row in block display mode: + * + * PASS: 0 1 2 3 4 5 6 + * rows: 8 8 4 4 2 2 1 + * 0: * * @ * @ * @ + * 1: ^ ^ @ ^ @ ^ * + * 2: ^ ^ @ ^ * * @ + * 3: ^ ^ @ ^ ^ ^ * + * 4: ^ ^ * * @ * @ + * 5: ^ ^ ^ ^ @ ^ * + * 6: ^ ^ ^ ^ * * @ + * 7: ^ ^ ^ ^ ^ ^ * + * + * The '@' signs are the interesting thing, since we know that + * this row isn't present in the pass data. Rewriting the + * above table with '1' for '@', little endian (i.e. row 0 at + * the LSB end): + * + * row: 76543210 + * Pass 0: 00000000 0x00 [bit 3, 0x8 of row unset (always)] + * Pass 1: 00000000 0x00 + * Pass 2: 00001111 0x0F [bit 2, 0x4 of row unset] + * Pass 3: 00000000 0x00 + * Pass 4: 00110011 0x33 [bit 1, 0x2 of row unset] + * Pass 5: 00000000 0x00 + * Pass 6: 01010101 0x55 [bit 0, 0x1 of row unset] + * + * PNG_PASS_BLOCK_SKIP(pass, row) can be written two ways; + * + * As a shift and a mask: + * (0x55330F00 >> ((pass >> 1) + (row & 7))) & ~pass & 1 + * + * And, somewhat simpler, as a bit check on the low bits of + * row: + * + * ~((row) >> (3-(pass >> 1))) & ~pass & 1 + */ +# define PNG_PASS_BLOCK_SKIP(pass, row)\ + (~((row) >> (3U-((pass) >> 1))) & ~(pass) & 0x1U) + + /* Hence: */ + debug(png_ptr->row_state == processing_row); + return pass_width == 0 || PNG_PASS_BLOCK_SKIP(pass, + row_number) ? png_row_skip : png_row_repeat; + } /* skipped row */ + + /* processed; fall through to start_of_row */ + } /* interlaced */ + + /* FALL THROUGH */ + case start_of_row: + { + /* Read the filter byte for the next row, previous_pixels is just + * used as a temporary buffer; it is reset below. + */ + png_alloc_size_t cb = png_inflate_IDAT(png_ptr, 0/*finish*/, + previous_pixels.buffer, 1U); + + /* This can be temporary; it verifies the invariants on how + * png_inflate_IDAT updates the {next,avail}_out fields: + */ +#ifndef __COVERITY__ /* Suppress bogus Coverity complaint */ + debug(png_ptr->zstream.avail_out == 1-cb && + png_ptr->zstream.next_out == cb + previous_pixels.buffer); +#endif + + /* next_out points to previous_pixels, for security do this: */ + png_ptr->zstream.next_out = NULL; + png_ptr->zstream.avail_out = 0U; + + /* One byte, so we either got it or have to get more input data: */ + if (cb != 1U) + { + affirm(cb == 0U && png_ptr->zstream.avail_in == 0U); + png_ptr->row_state = start_of_row; + return png_row_incomplete; + } + } + + /* Check the filter byte. */ + row_filter = previous_pixels.buffer[0]; + if (row_filter >= PNG_FILTER_VALUE_LAST) + png_chunk_error(png_ptr, "invalid PNG filter"); + + /* These are needed for the filter check below: */ + pass_width = width; + if (interlaced) + pass_width = PNG_PASS_COLS(pass_width, pass); + + /* The filter is followed by the row data, but first check the + * filter byte; the spec requires that we invent an empty row + * if the first row of a pass requires it. + * + * Note that row_number is the image row. + */ + if (row_number == PNG_PASS_START_ROW(pass)) switch (row_filter) + { + case PNG_FILTER_VALUE_UP: + /* x-0 == x, so do this optimization: */ + row_filter = PNG_FILTER_VALUE_NONE; + break; + + case PNG_FILTER_VALUE_PAETH: + /* The Paeth predictor is always the preceding (leftwards) + * value, so this is the same as sub: + */ + row_filter = PNG_FILTER_VALUE_SUB; + break; + + case PNG_FILTER_VALUE_AVG: + /* It would be possible to 'invent' a new filter that did + * AVG using only the previous byte; it's 'SUB' of half the + * preceding value, but this seems pointless. Zero out the + * row buffer to make AVG work. + */ + memset(png_ptr->row_buffer, 0U, + PNG_ROWBYTES(pixel_depth, pass_width)); + break; + + default: + break; + } /* switch row_filter */ + + /* Always zero the 'previous pixel' out at the start of a row; this + * allows the filter code to ignore the row start. + */ + memset(previous_pixels.buffer, 0U, sizeof previous_pixels.buffer); + + row_bytes_processed = 0U; + bytes_read = 0U; + + pixel_loop: + /* At this point the following must be set correctly: + * + * row_bytes_processed: bytes processed so far + * pass_width: width of a row in this pass in pixels + * pixel_depth: depth in bits of a pixel + * bytes_read: count of filtered bytes in pixel_buffer + * row_filter: filter byte for this row + * previous_pixels[0]: pixel 'a' for the filter + * previous_pixels[1]: pixel 'c' for the filter + * pixel_buffer[]: bytes_read filtered bytes + * + * The code in the loop decompresses PNG_ROW_BUFFER_SIZE filterd pixels + * from the input, unfilters and transforms them, then saves them to + * png_struct::row_buffer[row_bytes_processed...]. + */ + { /* pixel loop */ + const png_alloc_size_t row_bytes = + PNG_ROWBYTES(pixel_depth, pass_width); + png_bytep row_buffer = png_ptr->row_buffer + row_bytes_processed; + unsigned int pixels; + png_uint_32 x; + + /* Sanity check for potential buffer overwrite: */ + affirm(row_bytes > row_bytes_processed); + + /* Work out the current pixel index of the pixel at the start of the + * row buffer: + */ + switch (pixel_depth) + { + case 1U: x = (png_uint_32)/*SAFE*/(row_bytes_processed << 3); + break; + case 2U: x = (png_uint_32)/*SAFE*/(row_bytes_processed << 2); + break; + case 4U: x = (png_uint_32)/*SAFE*/(row_bytes_processed << 1); + break; + case 8U: x = (png_uint_32)/*SAFE*/row_bytes_processed; + break; + default: x = (png_uint_32)/*SAFE*/(row_bytes_processed / bpp); + debug(row_bytes_processed % bpp == 0U); + break; + } + + for (pixels = max_pixels; x < pass_width; x += pixels) + { + if (pixels > pass_width - x) + pixels = (unsigned int)/*SAFE*/(pass_width - x); + + /* At the end of the image pass Z_FINISH to zlib to optimize the + * final read (very slightly, is this worth doing?) To do this + * work out if we are at the end. + */ + { + const png_uint_32 height = png_ptr->height; + + /* last_pass_row indicates that this is the last row in this + * pass (the test is optimized for the non-interlaced case): + */ + const int last_pass_row = row_number+1 >= height || + (interlaced && PNG_LAST_PASS_ROW(row_number,pass,height)); + + /* Set 'finish' if this is the last row in the last pass of + * the image: + */ + const int finish = last_pass_row && (!interlaced || + pass >= PNG_LAST_PASS(width, height)); + + const png_alloc_size_t bytes_to_read = + PNG_ROWBYTES(pixel_depth, pixels); + + png_alloc_size_t cb; + + affirm(bytes_to_read > bytes_read); + cb = png_inflate_IDAT(png_ptr, finish, + pixel_buffer.buffer + bytes_read, + bytes_to_read - bytes_read); + bytes_read += cb; + + if (bytes_read < bytes_to_read) + { + /* Fewer bytes were read than needed: we need to stash all + * the information required at pixel_loop in png_struct so + * that the need_row_bytes case can restore it when more + * input is available. + */ + debug(png_ptr->zstream.avail_in == 0U); + png_ptr->zstream.next_out = NULL; + png_ptr->zstream.avail_out = 0U; + + png_ptr->row_bytes_read = row_bytes_processed + bytes_read; + memcpy(png_ptr->scratch, pixel_buffer.buffer, bytes_read); + memcpy(png_ptr->scratch+bytes_read, previous_pixels.buffer, + 2*bpp); + png_ptr->scratch[bytes_read+2*bpp] = row_filter; + png_ptr->row_state = need_row_bytes; + return png_row_incomplete; + } + + debug(bytes_read == bytes_to_read); + } /* fill pixel_buffer */ + + /* The buffer is full or the row is complete but the calculation + * was done using the pixel count, so double check against the + * byte count here: + */ + implies(bytes_read != input_byte_count, + bytes_read == row_bytes - row_bytes_processed); + + /* At this point all the required information to process the next + * block of pixels in the row has been read from the input stream + * and the original, filtered, row data is held in pixel_buffer. + * + * Because the buffer will be transformed after the unfilter + * operation we require whole pixels: + */ + debug(bytes_read >= bpp && bytes_read % bpp == 0); + + if (row_filter > PNG_FILTER_VALUE_NONE) + { + /* This is checked in the read code above: */ + debug(row_filter < PNG_FILTER_VALUE_LAST); + + /* Lazy init of the read functions, which allows hand crafted + * optimizations for 'bpp' (which does not change.) + */ + if (png_ptr->read_filter[0] == NULL) + png_init_filter_functions(png_ptr, bpp); + + /* Pixels 'a' then 'c' are in previous_pixels, pixel 'b' is in + * row_buffer and pixel 'x' (filtered) is in pixel_buffer. + */ + png_ptr->read_filter[row_filter-1](bytes_read, bpp, + pixel_buffer.buffer, row_buffer, previous_pixels.buffer); + } /* do the filter */ + + /* Now pixel_buffer.buffer contains the *un*filtered bytes of the + * current row and row_buffer needs updating with these. First + * preserve pixels 'a' and 'c' for the next time round the loop + * (if necessary). + */ + if (bytes_read < row_bytes - row_bytes_processed) + { + debug(bytes_read == input_byte_count); + memcpy(previous_pixels.buffer/* pixel 'a' */, + pixel_buffer.buffer + bytes_read - bpp, bpp); + memcpy(previous_pixels.buffer+bpp/* pixel 'c' */, + row_buffer + bytes_read - bpp, bpp); + } + + /* Now overwrite the previous row pixels in row_buffer with the + * current row pixels: + */ + memcpy(row_buffer, pixel_buffer.buffer, bytes_read); + row_buffer += bytes_read; + row_bytes_processed += bytes_read; + bytes_read = 0U; /* for next buffer */ + + /* Any transforms can now be performed along with any output + * handling (copy or interlace handling). + */ +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + if (png_ptr->transform_list != NULL) + { + unsigned int max_depth; + png_transform_control tc; + + png_init_transform_control(&tc, png_ptr); + + tc.width = pixels; + tc.sp = tc.dp = pixel_buffer.buffer; + + /* Run the list. It is ok if it doesn't end up doing + * anything; this can happen with a lazy init. + * + * NOTE: if the only thing in the list is a palette check + * function it can remove itself at this point. + */ + max_depth = png_run_transform_list_forwards(png_ptr, &tc); + + /* This is too late, a stack overwrite has already + * happened, but it may still prevent exploits: + */ + affirm(max_depth <= png_ptr->row_max_pixel_depth); + + /* It is very important that the transform produces the + * same pixel format as the TC_INIT steps: + */ + affirm(png_ptr->row_format == tc.format && + png_ptr->row_range == tc.range && + png_ptr->row_bit_depth == tc.bit_depth); +# ifdef PNG_READ_GAMMA_SUPPORTED + /* This checks the output gamma taking into account the + * fact that small gamma changes are eliminated. + */ + debug(png_ptr->row_gamma == tc.gamma || + png_gamma_check(png_ptr, &tc)); +# endif /* READ_GAMMA */ + + /* If the caller needs the row saved (for the progressive + * read API) or if this PNG is interlaced and this row may + * be required in a subsequent pass (any pass before the + * last one) then it is stored in + * png_struct::transformed_row, and that may need to be + * allocated here. + */ +# if defined(PNG_PROGRESSIVE_READ_SUPPORTED) ||\ + defined(PNG_READ_INTERLACING_SUPPORTED) + if (png_ptr->transform_list != NULL && + (save_row +# ifdef PNG_READ_INTERLACING_SUPPORTED + || (png_ptr->do_interlace && pass < 6U) +# endif /* READ_INTERLACING */ + )) + { + if (png_ptr->transformed_row == NULL) + png_ptr->transformed_row = png_voidcast(png_bytep, + png_malloc(png_ptr, png_calc_rowbytes(png_ptr, + png_ptr->row_bit_depth * + PNG_FORMAT_CHANNELS(png_ptr->row_format), + save_row ? width : (width+1U)>>1))); + + copy_row(png_ptr, png_ptr->transformed_row, + pixel_buffer.buffer, x, pixels, 1/*clear*/); + } +# endif /* PROGRESSIVE_READ || READ_INTERLACING */ + } /* transform_list != NULL */ +# endif /* TRANSFORM_MECH */ + + /* There are now 'pixels' possibly transformed pixels starting at + * row pixel x, where 'x' is an index in the interlaced row if + * interlacing is happening. Handle this row. + */ + if (transformed_row != NULL) + combine_row(png_ptr, transformed_row, pixel_buffer.buffer, + x, pixels, 0/*!display*/); + + if (display_row != NULL) + combine_row(png_ptr, display_row, pixel_buffer.buffer, x, + pixels, 1/*display*/); + } /* for x < pass_width */ + } /* pixel loop */ + + png_ptr->row_state = processing_row; + return png_row_process; + + default: + impossible("bad row state"); + } /* forever switch */ + + PNG_UNUSED(save_row) /* May not be used above */ } void /* PRIVATE */ +png_read_free_row_buffers(png_structrp png_ptr) +{ + /* The transformed row only gets saved if needed: */ +# if (defined(PNG_PROGRESSIVE_READ_SUPPORTED) ||\ + defined(PNG_READ_INTERLACING_SUPPORTED)) &&\ + defined(PNG_TRANSFORM_MECH_SUPPORTED) + if (png_ptr->transformed_row != NULL) + { + png_free(png_ptr, png_ptr->transformed_row); + png_ptr->transformed_row = NULL; + } +# endif /* PROGRESSIVE_READ || READ_INTERLACING */ + + if (png_ptr->row_buffer != NULL) + { + png_free(png_ptr, png_ptr->row_buffer); + png_ptr->row_buffer = NULL; + } +} + +/* Complete reading of the IDAT chunks. This returns 0 if more data is to + * be read, 1 if the zlib stream has terminated. Call this routine with + * zstream.avail_in greater than zero unless there is no more input data. + * When zstream_avail_in is 0 on entry and the stream does not terminate + * an "IDAT truncated" error will be output. + */ +int /* PRIVATE */ png_read_finish_IDAT(png_structrp png_ptr) { + enum + { + no_error = 0, + LZ_too_long, + IDAT_too_long, + IDAT_truncated + } error = no_error; + + /* Release the rowd buffers first; they can use considerable amounts of + * memory. + */ + png_read_free_row_buffers(png_ptr); + + affirm(png_ptr->zowner == png_IDAT); /* else this should not be called */ + /* We don't need any more data and the stream should have ended, however the * LZ end code may actually not have been processed. In this case we must * read it otherwise stray unread IDAT data or, more likely, an IDAT chunk * may still remain to be consumed. */ - if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED)) + if (!png_ptr->zstream_ended) { - /* The NULL causes png_read_IDAT_data to swallow any remaining bytes in - * the compressed stream, but the stream may be damaged too, so even after - * this call we may need to terminate the zstream ownership. - */ - png_read_IDAT_data(png_ptr, NULL, 0); - png_ptr->zstream.next_out = NULL; /* safety */ + int end_of_IDAT = png_ptr->zstream.avail_in == 0; + png_byte b[1]; + png_alloc_size_t cb = png_inflate_IDAT(png_ptr, 2/*finish*/, b, 1); - /* Now clear everything out for safety; the following may not have been - * done. - */ - if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED)) + debug(png_ptr->zstream.avail_out == 1-cb && + png_ptr->zstream.next_out == cb + b); + + /* As above, for safety do this: */ + png_ptr->zstream.next_out = NULL; + png_ptr->zstream.avail_out = 0; + + /* No data is expected, either compressed or in the IDAT: */ + if (cb != 0) + error = LZ_too_long; + + else if (png_ptr->zstream.avail_in == 0 /* && cb == 0 */) { - png_ptr->mode |= PNG_AFTER_IDAT; - png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; + /* This is the normal case but there may still be some waiting codes + * (including the adler32 that follow the LZ77 end code; so we can + * have at least 5 bytes after the end of the row data before the + * end of the stream. + */ + if (!png_ptr->zstream_ended) + { + if (!end_of_IDAT) + return 0; /* keep reading, no detectable error yet */ + + error = IDAT_truncated; + } + + /* Else there may still be an error; too much IDAT, but we can't + * tell. + */ } } - /* If the zstream has not been released do it now *and* terminate the reading - * of the final IDAT chunk. + /* If there is still pending zstream input then there was too much IDAT + * data: + */ + if (!error && png_ptr->zstream.avail_in > 0) + error = IDAT_too_long; + + /* Either this is the success case or an error has been detected and + * warned about. */ - if (png_ptr->zowner == png_IDAT) { - /* Always do this; the pointers otherwise point into the read buffer. */ - png_ptr->zstream.next_in = NULL; - png_ptr->zstream.avail_in = 0; + int ret = inflateEnd(&png_ptr->zstream); - /* Now we no longer own the zstream. */ - png_ptr->zowner = 0; - - /* The slightly weird semantics of the sequential IDAT reading is that we - * are always in or at the end of an IDAT chunk, so we always need to do a - * crc_finish here. If idat_size is non-zero we also need to read the - * spurious bytes at the end of the chunk now. + /* In fact we expect this to always succeed, so it is a good idea to + * catch it in pre-release builds: */ - (void)png_crc_finish(png_ptr, png_ptr->idat_size); - } -} + debug_handled(ret == Z_OK); -void /* PRIVATE */ -png_read_finish_row(png_structrp png_ptr) -{ -#ifdef PNG_READ_INTERLACING_SUPPORTED - /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ - - /* Start of interlace block */ - static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; - - /* Offset to next interlace block */ - static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; - - /* Start of interlace block in the y direction */ - static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; - - /* Offset to next interlace block in the y direction */ - static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; -#endif /* PNG_READ_INTERLACING_SUPPORTED */ - - png_debug(1, "in png_read_finish_row"); - png_ptr->row_number++; - if (png_ptr->row_number < png_ptr->num_rows) - return; - -#ifdef PNG_READ_INTERLACING_SUPPORTED - if (png_ptr->interlaced) - { - png_ptr->row_number = 0; - - /* TO DO: don't do this if prev_row isn't needed (requires - * read-ahead of the next row's filter byte. - */ - memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1); - - do + if (ret != Z_OK) { - png_ptr->pass++; + /* This is just a warning; it's safe, and the zstream_error flag is + * not set. + */ + png_zstream_error(&png_ptr->zstream, ret); + png_chunk_warning(png_ptr, png_ptr->zstream.msg); + } + } - if (png_ptr->pass >= 7) + /* Output an error message if required: */ + if (error && !png_ptr->zstream_error) + { + switch (error) + { + case LZ_too_long: + png_benign_error(png_ptr, "compressed data too long"); break; - png_ptr->iwidth = (png_ptr->width + - png_pass_inc[png_ptr->pass] - 1 - - png_pass_start[png_ptr->pass]) / - png_pass_inc[png_ptr->pass]; + case IDAT_too_long: + png_benign_error(png_ptr, "uncompressed data too long"); + break; - if (!(png_ptr->transformations & PNG_INTERLACE)) - { - png_ptr->num_rows = (png_ptr->height + - png_pass_yinc[png_ptr->pass] - 1 - - png_pass_ystart[png_ptr->pass]) / - png_pass_yinc[png_ptr->pass]; - } + case IDAT_truncated: + png_benign_error(png_ptr, "data truncated"); + break; - else /* if (png_ptr->transformations & PNG_INTERLACE) */ - break; /* libpng deinterlacing sees every row */ + default: + case no_error: /* Satisfy the compiler */ + break; + } - } while (png_ptr->num_rows == 0 || png_ptr->iwidth == 0); - - if (png_ptr->pass < 7) - return; - } -#endif /* PNG_READ_INTERLACING_SUPPORTED */ - - /* Here after at the end of the last row of the last pass. */ - png_read_finish_IDAT(png_ptr); -} -#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ - -void /* PRIVATE */ -png_read_start_row(png_structrp png_ptr) -{ -#ifdef PNG_READ_INTERLACING_SUPPORTED - /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ - - /* Start of interlace block */ - static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; - - /* Offset to next interlace block */ - static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; - - /* Start of interlace block in the y direction */ - static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; - - /* Offset to next interlace block in the y direction */ - static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; -#endif - - int max_pixel_depth; - png_size_t row_bytes; - - png_debug(1, "in png_read_start_row"); - -#ifdef PNG_READ_TRANSFORMS_SUPPORTED - png_init_read_transformations(png_ptr); -#endif -#ifdef PNG_READ_INTERLACING_SUPPORTED - if (png_ptr->interlaced) - { - if (!(png_ptr->transformations & PNG_INTERLACE)) - png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - - png_pass_ystart[0]) / png_pass_yinc[0]; - - else - png_ptr->num_rows = png_ptr->height; - - png_ptr->iwidth = (png_ptr->width + - png_pass_inc[png_ptr->pass] - 1 - - png_pass_start[png_ptr->pass]) / - png_pass_inc[png_ptr->pass]; + png_ptr->zstream_error = 1; } - else -#endif /* PNG_READ_INTERLACING_SUPPORTED */ - { - png_ptr->num_rows = png_ptr->height; - png_ptr->iwidth = png_ptr->width; - } - - max_pixel_depth = png_ptr->pixel_depth; - - /* WARNING: * png_read_transform_info (pngrtran.c) performs a simpliar set of - * calculations to calculate the final pixel depth, then - * png_do_read_transforms actually does the transforms. This means that the - * code which effectively calculates this value is actually repeated in three - * separate places. They must all match. Innocent changes to the order of - * transformations can and will break libpng in a way that causes memory - * overwrites. - * - * TODO: fix this. + /* WARNING: leave {next,avail}_in set here, the progressive reader uses these + * to complete the PNG chunk CRC calculation. */ -#ifdef PNG_READ_PACK_SUPPORTED - if ((png_ptr->transformations & PNG_PACK) && png_ptr->bit_depth < 8) - max_pixel_depth = 8; -#endif + png_ptr->zstream_ended = 1; + png_ptr->zowner = 0; -#ifdef PNG_READ_EXPAND_SUPPORTED - if (png_ptr->transformations & PNG_EXPAND) + return 1; /* end of stream */ +} + +/* Optional call to update the users info_ptr structure, can be used from both + * the progressive and sequential reader, but the app must call it. + */ +void PNGAPI +png_read_update_info(png_structrp png_ptr, png_inforp info_ptr) +{ + png_debug(1, "in png_read_update_info"); + + if (png_ptr != NULL) { - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + if (png_ptr->zowner != png_IDAT) { - if (png_ptr->num_trans) - max_pixel_depth = 32; + png_read_start_IDAT(png_ptr); - else - max_pixel_depth = 24; - } - - else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) - { - if (max_pixel_depth < 8) - max_pixel_depth = 8; - - if (png_ptr->num_trans) - max_pixel_depth *= 2; - } - - else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) - { - if (png_ptr->num_trans) - { - max_pixel_depth *= 4; - max_pixel_depth /= 3; - } - } - } -#endif - -#ifdef PNG_READ_EXPAND_16_SUPPORTED - if (png_ptr->transformations & PNG_EXPAND_16) - { -# ifdef PNG_READ_EXPAND_SUPPORTED - /* In fact it is an error if it isn't supported, but checking is - * the safe way. - */ - if (png_ptr->transformations & PNG_EXPAND) - { - if (png_ptr->bit_depth < 16) - max_pixel_depth *= 2; - } - else -# endif - png_ptr->transformations &= ~PNG_EXPAND_16; - } -#endif - -#ifdef PNG_READ_FILLER_SUPPORTED - if (png_ptr->transformations & (PNG_FILLER)) - { - if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) - { - if (max_pixel_depth <= 8) - max_pixel_depth = 16; - - else - max_pixel_depth = 32; - } - - else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB || - png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) - { - if (max_pixel_depth <= 32) - max_pixel_depth = 32; - - else - max_pixel_depth = 64; - } - } -#endif - -#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED - if (png_ptr->transformations & PNG_GRAY_TO_RGB) - { - if ( -#ifdef PNG_READ_EXPAND_SUPPORTED - (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND)) || -#endif -#ifdef PNG_READ_FILLER_SUPPORTED - (png_ptr->transformations & (PNG_FILLER)) || -#endif - png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - { - if (max_pixel_depth <= 16) - max_pixel_depth = 32; - - else - max_pixel_depth = 64; +# ifdef PNG_READ_TRANSFORMS_SUPPORTED + png_read_transform_info(png_ptr, info_ptr); +# else + PNG_UNUSED(info_ptr) +# endif } + /* New in 1.6.0 this avoids the bug of doing the initializations twice */ else - { - if (max_pixel_depth <= 8) - { - if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - max_pixel_depth = 32; + png_app_error(png_ptr, + "png_read_update_info/png_start_read_image: duplicate call"); + } +} + +png_int_32 /* PRIVATE */ +png_read_setting(png_structrp png_ptr, png_uint_32 setting, + png_uint_32 parameter, png_int_32 value) +{ + /* Caller checks the arguments for basic validity */ + int only_get = (setting & PNG_SF_GET) != 0U; + + if (only_get) /* spurious: in case it isn't used */ + setting &= ~PNG_SF_GET; + + switch (setting) + { +# ifdef PNG_SEQUENTIAL_READ_SUPPORTED + case PNG_SR_COMPRESS_buffer_size: + if (parameter > 0 && parameter <= ZLIB_IO_MAX) + { + png_ptr->IDAT_size = parameter; + return 0; /* Cannot return a 32-bit value */ + } else - max_pixel_depth = 24; + return PNG_EINVAL; +# endif /* SEQUENTIAL_READ */ + +# ifdef PNG_READ_GAMMA_SUPPORTED + case PNG_SR_GAMMA_threshold: + if (parameter <= 0xFFFF) + { + if (!only_get) + png_ptr->gamma_threshold = PNG_UINT_16(parameter); + + return (png_int_32)/*SAFE*/parameter; + } + + return PNG_EDOM; + +#if 0 /*NYI*/ + case PNG_SR_GAMMA_accuracy: + if (parameter <= 1600) + { + if (!only_get) + png_ptr->gamma_accuracy = parameter; + + return (png_int_32)/*SAFE*/parameter; + } + + return PNG_EDOM; +#endif /*NYI*/ +# endif /* READ_GAMMA */ + + case PNG_SR_CRC_ACTION: + /* Tell libpng how we react to CRC errors in critical chunks */ + switch (parameter) + { + case PNG_CRC_NO_CHANGE: /* Leave setting as is */ + break; + + case PNG_CRC_WARN_USE: /* Warn/use data */ + png_ptr->critical_crc = crc_warn_use; + break; + + case PNG_CRC_QUIET_USE: /* Quiet/use data */ + png_ptr->critical_crc = crc_quiet_use; + break; + + default: + case PNG_CRC_WARN_DISCARD: /* Not valid for critical data */ + return PNG_EINVAL; + + case PNG_CRC_ERROR_QUIT: /* Error/quit */ + case PNG_CRC_DEFAULT: + png_ptr->critical_crc = crc_error_quit; + break; } - else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - max_pixel_depth = 64; + /* Tell libpng how we react to CRC errors in ancillary chunks */ + switch (value) + { + case PNG_CRC_NO_CHANGE: /* Leave setting as is */ + break; - else - max_pixel_depth = 48; - } + case PNG_CRC_WARN_USE: /* Warn/use data */ + png_ptr->ancillary_crc = crc_warn_use; + break; + + case PNG_CRC_QUIET_USE: /* Quiet/use data */ + png_ptr->ancillary_crc = crc_quiet_use; + break; + + case PNG_CRC_ERROR_QUIT: /* Error/quit */ + png_ptr->ancillary_crc = crc_error_quit; + break; + + case PNG_CRC_WARN_DISCARD: /* Warn/discard data */ + case PNG_CRC_DEFAULT: + png_ptr->ancillary_crc = crc_warn_discard; + break; + + default: + return PNG_EINVAL; + } + + return 0; /* success */ + +# ifdef PNG_SET_OPTION_SUPPORTED + case PNG_SRW_OPTION: + switch (parameter) + { + case PNG_MAXIMUM_INFLATE_WINDOW: + if (png_ptr->maximum_inflate_window) + { + if (!value && !only_get) + png_ptr->maximum_inflate_window = 0U; + return PNG_OPTION_ON; + } + + else + { + if (value && !only_get) + png_ptr->maximum_inflate_window = 1U; + return PNG_OPTION_OFF; + } + + default: + return PNG_OPTION_UNSET; + } +# endif /* SET_OPTION */ + +# ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED + case PNG_SRW_CHECK_FOR_INVALID_INDEX: + /* The 'enabled' value is a FORTRAN style three-state: */ + if (value > 0) + png_ptr->palette_index_check = PNG_PALETTE_CHECK_ON; + + else if (value < 0) + png_ptr->palette_index_check = PNG_PALETTE_CHECK_OFF; + + else + png_ptr->palette_index_check = PNG_PALETTE_CHECK_DEFAULT; + + return 0; +# endif /* READ_CHECK_FOR_INVALID_INDEX */ + +# ifdef PNG_BENIGN_READ_ERRORS_SUPPORTED + case PNG_SRW_ERROR_HANDLING: + /* The parameter is a bit mask of what to set, the value is what to + * set it to. + */ + if (value >= PNG_IGNORE && value <= PNG_ERROR && + parameter <= PNG_ALL_ERRORS) + { + if ((parameter & PNG_BENIGN_ERRORS) != 0U) + png_ptr->benign_error_action = value & 0x3U; + + if ((parameter & PNG_APP_WARNINGS) != 0U) + png_ptr->app_warning_action = value & 0x3U; + + if ((parameter & PNG_APP_ERRORS) != 0U) + png_ptr->app_error_action = value & 0x3U; + + if ((parameter & PNG_IDAT_ERRORS) != 0U) + png_ptr->IDAT_error_action = value & 0x3U; + + return 0; + } + + return PNG_EINVAL; +# endif /* BENIGN_READ_ERRORS */ + + default: + return PNG_ENOSYS; /* not supported (whatever it is) */ } -#endif - -#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) && \ -defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) - if (png_ptr->transformations & PNG_USER_TRANSFORM) - { - int user_pixel_depth = png_ptr->user_transform_depth * - png_ptr->user_transform_channels; - - if (user_pixel_depth > max_pixel_depth) - max_pixel_depth = user_pixel_depth; - } -#endif - - /* This value is stored in png_struct and double checked in the row read - * code. - */ - png_ptr->maximum_pixel_depth = (png_byte)max_pixel_depth; - png_ptr->transformed_pixel_depth = 0; /* calculated on demand */ - - /* Align the width on the next larger 8 pixels. Mainly used - * for interlacing - */ - row_bytes = ((png_ptr->width + 7) & ~((png_uint_32)7)); - /* Calculate the maximum bytes needed, adding a byte and a pixel - * for safety's sake - */ - row_bytes = PNG_ROWBYTES(max_pixel_depth, row_bytes) + - 1 + ((max_pixel_depth + 7) >> 3); - -#ifdef PNG_MAX_MALLOC_64K - if (row_bytes > (png_uint_32)65536L) - png_error(png_ptr, "This image requires a row greater than 64KB"); -#endif - - if (row_bytes + 48 > png_ptr->big_row_buf_size) - { - png_free(png_ptr, png_ptr->big_row_buf); - png_free(png_ptr, png_ptr->big_prev_row); - - if (png_ptr->interlaced) - png_ptr->big_row_buf = (png_bytep)png_calloc(png_ptr, - row_bytes + 48); - - else - png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, row_bytes + 48); - - png_ptr->big_prev_row = (png_bytep)png_malloc(png_ptr, row_bytes + 48); - -#ifdef PNG_ALIGNED_MEMORY_SUPPORTED - /* Use 16-byte aligned memory for row_buf with at least 16 bytes - * of padding before and after row_buf; treat prev_row similarly. - * NOTE: the alignment is to the start of the pixels, one beyond the start - * of the buffer, because of the filter byte. Prior to libpng 1.5.6 this - * was incorrect; the filter byte was aligned, which had the exact - * opposite effect of that intended. - */ - { - png_bytep temp = png_ptr->big_row_buf + 32; - int extra = (int)((temp - (png_bytep)0) & 0x0f); - png_ptr->row_buf = temp - extra - 1/*filter byte*/; - - temp = png_ptr->big_prev_row + 32; - extra = (int)((temp - (png_bytep)0) & 0x0f); - png_ptr->prev_row = temp - extra - 1/*filter byte*/; - } - -#else - /* Use 31 bytes of padding before and 17 bytes after row_buf. */ - png_ptr->row_buf = png_ptr->big_row_buf + 31; - png_ptr->prev_row = png_ptr->big_prev_row + 31; -#endif - png_ptr->big_row_buf_size = row_bytes + 48; - } - -#ifdef PNG_MAX_MALLOC_64K - if (png_ptr->rowbytes > 65535) - png_error(png_ptr, "This image requires a row greater than 64KB"); - -#endif - if (png_ptr->rowbytes > (PNG_SIZE_MAX - 1)) - png_error(png_ptr, "Row has too many bytes to allocate in memory"); - - memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1); - - png_debug1(3, "width = %u,", png_ptr->width); - png_debug1(3, "height = %u,", png_ptr->height); - png_debug1(3, "iwidth = %u,", png_ptr->iwidth); - png_debug1(3, "num_rows = %u,", png_ptr->num_rows); - png_debug1(3, "rowbytes = %lu,", (unsigned long)png_ptr->rowbytes); - png_debug1(3, "irowbytes = %lu", - (unsigned long)PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1); - - /* The sequential reader needs a buffer for IDAT, but the progressive reader - * does not, so free the read buffer now regardless; the sequential reader - * reallocates it on demand. - */ - if (png_ptr->read_buffer) - { - png_bytep buffer = png_ptr->read_buffer; - - png_ptr->read_buffer_size = 0; - png_ptr->read_buffer = NULL; - png_free(png_ptr, buffer); - } - - /* Finally claim the zstream for the inflate of the IDAT data, use the bits - * value from the stream (note that this will result in a fatal error if the - * IDAT stream has a bogus deflate header window_bits value, but this should - * not be happening any longer!) - */ - if (png_inflate_claim(png_ptr, png_IDAT) != Z_OK) - png_error(png_ptr, png_ptr->zstream.msg); - - png_ptr->flags |= PNG_FLAG_ROW_INIT; } -#endif /* PNG_READ_SUPPORTED */ +#endif /* READ */ diff --git a/ext/libpng17/pngset.c b/ext/libpng17/pngset.c index f784fbf08b..abd13c377c 100644 --- a/ext/libpng17/pngset.c +++ b/ext/libpng17/pngset.c @@ -2,7 +2,7 @@ /* pngset.c - storage of image information into info struct * * Last changed in libpng 1.7.0 [(PENDING RELEASE)] - * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -17,6 +17,7 @@ */ #include "pngpriv.h" +#define PNG_SRC_FILE PNG_SRC_FILE_pngset #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) @@ -59,7 +60,7 @@ png_set_cHRM_fixed(png_const_structrp png_ptr, png_inforp info_ptr, xy.whitey = white_y; if (png_colorspace_set_chromaticities(png_ptr, &info_ptr->colorspace, &xy, - 2/* override with app values*/)) + 2/* override with app values*/) != 0) info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; png_colorspace_sync_info(png_ptr, info_ptr); @@ -90,7 +91,8 @@ png_set_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_inforp info_ptr, XYZ.blue_Y = int_blue_Y; XYZ.blue_Z = int_blue_Z; - if (png_colorspace_set_endpoints(png_ptr, &info_ptr->colorspace, &XYZ, 2)) + if (png_colorspace_set_endpoints(png_ptr, &info_ptr->colorspace, + &XYZ, 2) != 0) info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; png_colorspace_sync_info(png_ptr, info_ptr); @@ -103,14 +105,14 @@ png_set_cHRM(png_const_structrp png_ptr, png_inforp info_ptr, double green_x, double green_y, double blue_x, double blue_y) { png_set_cHRM_fixed(png_ptr, info_ptr, - png_fixed(png_ptr, white_x, "cHRM White X"), - png_fixed(png_ptr, white_y, "cHRM White Y"), - png_fixed(png_ptr, red_x, "cHRM Red X"), - png_fixed(png_ptr, red_y, "cHRM Red Y"), - png_fixed(png_ptr, green_x, "cHRM Green X"), - png_fixed(png_ptr, green_y, "cHRM Green Y"), - png_fixed(png_ptr, blue_x, "cHRM Blue X"), - png_fixed(png_ptr, blue_y, "cHRM Blue Y")); + png_fixed(png_ptr, white_x, "cHRM White X"), + png_fixed(png_ptr, white_y, "cHRM White Y"), + png_fixed(png_ptr, red_x, "cHRM Red X"), + png_fixed(png_ptr, red_y, "cHRM Red Y"), + png_fixed(png_ptr, green_x, "cHRM Green X"), + png_fixed(png_ptr, green_y, "cHRM Green Y"), + png_fixed(png_ptr, blue_x, "cHRM Blue X"), + png_fixed(png_ptr, blue_y, "cHRM Blue Y")); } void PNGAPI @@ -119,19 +121,19 @@ png_set_cHRM_XYZ(png_const_structrp png_ptr, png_inforp info_ptr, double red_X, double blue_X, double blue_Y, double blue_Z) { png_set_cHRM_XYZ_fixed(png_ptr, info_ptr, - png_fixed(png_ptr, red_X, "cHRM Red X"), - png_fixed(png_ptr, red_Y, "cHRM Red Y"), - png_fixed(png_ptr, red_Z, "cHRM Red Z"), - png_fixed(png_ptr, green_X, "cHRM Red X"), - png_fixed(png_ptr, green_Y, "cHRM Red Y"), - png_fixed(png_ptr, green_Z, "cHRM Red Z"), - png_fixed(png_ptr, blue_X, "cHRM Red X"), - png_fixed(png_ptr, blue_Y, "cHRM Red Y"), - png_fixed(png_ptr, blue_Z, "cHRM Red Z")); + png_fixed(png_ptr, red_X, "cHRM Red X"), + png_fixed(png_ptr, red_Y, "cHRM Red Y"), + png_fixed(png_ptr, red_Z, "cHRM Red Z"), + png_fixed(png_ptr, green_X, "cHRM Green X"), + png_fixed(png_ptr, green_Y, "cHRM Green Y"), + png_fixed(png_ptr, green_Z, "cHRM Green Z"), + png_fixed(png_ptr, blue_X, "cHRM Blue X"), + png_fixed(png_ptr, blue_Y, "cHRM Blue Y"), + png_fixed(png_ptr, blue_Z, "cHRM Blue Z")); } -# endif /* PNG_FLOATING_POINT_SUPPORTED */ +# endif /* FLOATING_POINT */ -#endif /* PNG_cHRM_SUPPORTED */ +#endif /* cHRM */ #ifdef PNG_gAMA_SUPPORTED void PNGFAPI @@ -162,8 +164,6 @@ void PNGAPI png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr, png_const_uint_16p hist) { - int i; - png_debug1(1, "in %s storage function", "hIST"); if (png_ptr == NULL || info_ptr == NULL) @@ -189,13 +189,18 @@ png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr, if (info_ptr->hist == NULL) { png_warning(png_ptr, "Insufficient memory for hIST chunk data"); + return; } info_ptr->free_me |= PNG_FREE_HIST; - for (i = 0; i < info_ptr->num_palette; i++) - info_ptr->hist[i] = hist[i]; + { + unsigned int i; + + for (i = 0; i < info_ptr->num_palette; i++) + info_ptr->hist[i] = hist[i]; + } info_ptr->valid |= PNG_INFO_hIST; } @@ -214,31 +219,16 @@ png_set_IHDR(png_const_structrp png_ptr, png_inforp info_ptr, info_ptr->width = width; info_ptr->height = height; - info_ptr->bit_depth = (png_byte)bit_depth; - info_ptr->color_type = (png_byte)color_type; - info_ptr->compression_type = (png_byte)compression_type; - info_ptr->filter_type = (png_byte)filter_type; - info_ptr->interlace_type = (png_byte)interlace_type; + info_ptr->bit_depth = png_check_bits(png_ptr, bit_depth, 6); + info_ptr->format = png_check_bits(png_ptr, + PNG_FORMAT_FROM_COLOR_TYPE(color_type), PNG_RF_BITS); + info_ptr->compression_type = png_check_byte(png_ptr, compression_type); + info_ptr->filter_type = png_check_byte(png_ptr, filter_type); + info_ptr->interlace_type = png_check_byte(png_ptr, interlace_type); png_check_IHDR (png_ptr, info_ptr->width, info_ptr->height, - info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type, + info_ptr->bit_depth, color_type, info_ptr->interlace_type, info_ptr->compression_type, info_ptr->filter_type); - - if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) - info_ptr->channels = 1; - - else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) - info_ptr->channels = 3; - - else - info_ptr->channels = 1; - - if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) - info_ptr->channels++; - - info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth); - - info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, width); } #ifdef PNG_oFFs_SUPPORTED @@ -253,7 +243,7 @@ png_set_oFFs(png_const_structrp png_ptr, png_inforp info_ptr, info_ptr->x_offset = offset_x; info_ptr->y_offset = offset_y; - info_ptr->offset_unit_type = (png_byte)unit_type; + info_ptr->offset_unit_type = png_check_byte(png_ptr, unit_type); info_ptr->valid |= PNG_INFO_oFFs; } #endif @@ -270,7 +260,7 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr, png_debug1(1, "in %s storage function", "pCAL"); if (png_ptr == NULL || info_ptr == NULL || purpose == NULL || units == NULL - || (nparams > 0 && params == NULL)) + || (nparams > 0 && params == NULL)) return; length = strlen(purpose) + 1; @@ -288,16 +278,19 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr, /* Validate params[nparams] */ for (i=0; ipcal_purpose = png_voidcast(png_charp, - png_malloc_warn(png_ptr, length)); + png_malloc_warn(png_ptr, length)); if (info_ptr->pcal_purpose == NULL) { png_warning(png_ptr, "Insufficient memory for pCAL purpose"); + return; } @@ -306,19 +299,20 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr, png_debug(3, "storing X0, X1, type, and nparams in info"); info_ptr->pcal_X0 = X0; info_ptr->pcal_X1 = X1; - info_ptr->pcal_type = (png_byte)type; - info_ptr->pcal_nparams = (png_byte)nparams; + info_ptr->pcal_type = png_check_byte(png_ptr, type); + info_ptr->pcal_nparams = png_check_byte(png_ptr, nparams); length = strlen(units) + 1; png_debug1(3, "allocating units for info (%lu bytes)", - (unsigned long)length); + (unsigned long)length); info_ptr->pcal_units = png_voidcast(png_charp, - png_malloc_warn(png_ptr, length)); + png_malloc_warn(png_ptr, length)); if (info_ptr->pcal_units == NULL) { png_warning(png_ptr, "Insufficient memory for pCAL units"); + return; } @@ -330,6 +324,7 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr, if (info_ptr->pcal_params == NULL) { png_warning(png_ptr, "Insufficient memory for pCAL params"); + return; } @@ -346,6 +341,7 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr, if (info_ptr->pcal_params[i] == NULL) { png_warning(png_ptr, "Insufficient memory for pCAL parameter"); + return; } @@ -383,18 +379,19 @@ png_set_sCAL_s(png_const_structrp png_ptr, png_inforp info_ptr, sheight[0] == 45 /* '-' */ || !png_check_fp_string(sheight, lengthh)) png_error(png_ptr, "Invalid sCAL height"); - info_ptr->scal_unit = (png_byte)unit; + info_ptr->scal_unit = png_check_byte(png_ptr, unit); ++lengthw; png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)lengthw); info_ptr->scal_s_width = png_voidcast(png_charp, - png_malloc_warn(png_ptr, lengthw)); + png_malloc_warn(png_ptr, lengthw)); if (info_ptr->scal_s_width == NULL) { png_warning(png_ptr, "Memory allocation failed while processing sCAL"); + return; } @@ -405,7 +402,7 @@ png_set_sCAL_s(png_const_structrp png_ptr, png_inforp info_ptr, png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)lengthh); info_ptr->scal_s_height = png_voidcast(png_charp, - png_malloc_warn(png_ptr, lengthh)); + png_malloc_warn(png_ptr, lengthh)); if (info_ptr->scal_s_height == NULL) { @@ -413,6 +410,7 @@ png_set_sCAL_s(png_const_structrp png_ptr, png_inforp info_ptr, info_ptr->scal_s_width = NULL; png_warning(png_ptr, "Memory allocation failed while processing sCAL"); + return; } @@ -444,9 +442,9 @@ png_set_sCAL(png_const_structrp png_ptr, png_inforp info_ptr, int unit, char sheight[PNG_sCAL_MAX_DIGITS+1]; png_ascii_from_fp(png_ptr, swidth, (sizeof swidth), width, - PNG_sCAL_PRECISION); + PNG_sCAL_PRECISION); png_ascii_from_fp(png_ptr, sheight, (sizeof sheight), height, - PNG_sCAL_PRECISION); + PNG_sCAL_PRECISION); png_set_sCAL_s(png_ptr, info_ptr, unit, swidth, sheight); } @@ -494,7 +492,7 @@ png_set_pHYs(png_const_structrp png_ptr, png_inforp info_ptr, info_ptr->x_pixels_per_unit = res_x; info_ptr->y_pixels_per_unit = res_y; - info_ptr->phys_unit_type = (png_byte)unit_type; + info_ptr->phys_unit_type = png_check_byte(png_ptr, unit_type); info_ptr->valid |= PNG_INFO_pHYs; } #endif @@ -504,57 +502,69 @@ png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_const_colorp palette, int num_palette) { + png_uint_32 max_palette_length; + png_debug1(1, "in %s storage function", "PLTE"); if (png_ptr == NULL || info_ptr == NULL) return; - if (num_palette < 0 || num_palette > PNG_MAX_PALETTE_LENGTH) + max_palette_length = (info_ptr->format & PNG_FORMAT_FLAG_COLORMAP) == 0 ? + (1 << info_ptr->bit_depth) : PNG_MAX_PALETTE_LENGTH; + + if (num_palette < 0 || num_palette > (int) max_palette_length) { - if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) - png_error(png_ptr, "Invalid palette length"); + if ((info_ptr->format & PNG_FORMAT_FLAG_COLORMAP) != 0) + png_chunk_error(png_ptr, "Invalid palette length"); else { - png_warning(png_ptr, "Invalid palette length"); + png_chunk_report(png_ptr, "Invalid palette length", PNG_CHUNK_ERROR); return; } } - if ((num_palette > 0 && palette == NULL) || - (num_palette == 0 -# ifdef PNG_MNG_FEATURES_SUPPORTED - && (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0 -# endif - )) - { +#ifdef PNG_MNG_FEATURES_SUPPORTED + if ((num_palette > 0 && palette == NULL) || (num_palette == 0 && + (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0)) png_error(png_ptr, "Invalid palette"); - return; - } +#else + if ((num_palette > 0 && palette == NULL) || num_palette == 0) + png_error(png_ptr, "Invalid palette"); +#endif /* MNG_FEATURES */ - /* It may not actually be necessary to set png_ptr->palette here; - * we do it for backward compatibility with the way the png_handle_tRNS - * function used to do the allocation. - * - * 1.6.0: the above statement appears to be incorrect; something has to set - * the palette inside png_struct on read. - */ png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0); /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead - * of num_palette entries, in case of an invalid PNG file that has - * too-large sample values. + * of num_palette entries, in case of an invalid PNG file or incorrect + * call to png_set_PLTE() with too-large sample values. */ - png_ptr->palette = png_voidcast(png_colorp, png_calloc(png_ptr, + info_ptr->palette = png_voidcast(png_colorp, png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * (sizeof (png_color)))); if (num_palette > 0) - memcpy(png_ptr->palette, palette, num_palette * (sizeof (png_color))); - info_ptr->palette = png_ptr->palette; - info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette; + memcpy(info_ptr->palette, palette, num_palette * (sizeof (png_color))); + /* Set the remainder of the palette entries to something recognizable; the + * code used to leave them set to 0, which made seeing palette index errors + * difficult. + */ + if (num_palette < PNG_MAX_PALETTE_LENGTH) + { + int i; + png_color c; + + memset(&c, 0x42, sizeof c); /* fill in any padding */ + c.red = 0xbe; + c.green = 0xad; + c.blue = 0xed; /* Visible in memory as 'beaded' */ + + for (i=num_palette; ipalette[i] = c; + } + + info_ptr->num_palette = png_check_bits(png_ptr, num_palette, 9); info_ptr->free_me |= PNG_FREE_PLTE; - info_ptr->valid |= PNG_INFO_PLTE; } @@ -595,7 +605,8 @@ png_set_sRGB_gAMA_and_cHRM(png_const_structrp png_ptr, png_inforp info_ptr, if (png_ptr == NULL || info_ptr == NULL) return; - if (png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace, srgb_intent)) + if (png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace, + srgb_intent) != 0) { /* This causes the gAMA and cHRM to be written too */ info_ptr->colorspace.flags |= @@ -633,7 +644,7 @@ png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, */ { int result = png_colorspace_set_ICC(png_ptr, &info_ptr->colorspace, name, - proflen, profile, info_ptr->color_type); + proflen, profile, (info_ptr->format & PNG_FORMAT_FLAG_COLOR) != 0); png_colorspace_sync_info(png_ptr, info_ptr); @@ -643,7 +654,7 @@ png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, /* But do write the gAMA and cHRM chunks from the profile. */ info_ptr->colorspace.flags |= - PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM; + PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM; } length = strlen(name)+1; @@ -652,18 +663,20 @@ png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, if (new_iccp_name == NULL) { png_benign_error(png_ptr, "Insufficient memory to process iCCP chunk"); + return; } memcpy(new_iccp_name, name, length); new_iccp_profile = png_voidcast(png_bytep, - png_malloc_warn(png_ptr, proflen)); + png_malloc_warn(png_ptr, proflen)); if (new_iccp_profile == NULL) { png_free(png_ptr, new_iccp_name); png_benign_error(png_ptr, "Insufficient memory to process iCCP profile"); + return; } @@ -671,7 +684,6 @@ png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0); - info_ptr->iccp_proflen = proflen; info_ptr->iccp_name = new_iccp_name; info_ptr->iccp_profile = new_iccp_profile; info_ptr->free_me |= PNG_FREE_ICCP; @@ -679,6 +691,77 @@ png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, } #endif +#if defined(PNG_TEXT_SUPPORTED) || defined(PNG_tIME_SUPPORTED) +static png_byte +get_location(png_const_structrp png_ptr) + /* Return the correct location flag for a chunk. For a png_set_ + * called during read this is the current read location, for a + * png_set_ called during write it is the following write location + * (because the chunks at the current location have already been written.) + * For a png_set_ called before read starts (none of the 'position' + * mode bits are set) the position is set to the start (PNG_HAVE_IHDR). For + * a png_set_chunk> called before write starts PNG_HAVE_PLTE|PNG_AFTER_IDAT + * are set because we don't know whether this is the main png_info or the one + * for use after the IDAT from png_write_end. + * + * The latter behavior gives compatibility with the old behavior of + * png_set_text; it only wrote text chunks after the PLTE or IDAT and it just + * wrote them once. + */ +{ + if ((png_ptr->mode & PNG_AFTER_IDAT) != 0U) + return PNG_AFTER_IDAT; + + else if ((png_ptr->mode & PNG_HAVE_PLTE) != 0U) + { + /* In a write operation PNG_HAVE_PLTE is set when the chunks before the + * IDAT are written (in png_write_info), so the location needs to be after + * IDAT. In a read the chunk was read after the PLTE but before the IDAT. + */ + if (png_ptr->read_struct) + return PNG_HAVE_PLTE; + + else /* write struct */ + return PNG_AFTER_IDAT; + } + + else if ((png_ptr->mode & PNG_HAVE_IHDR) != 0U) + { + /* For read this means the chunk is between the IHDR and any PLTE; there + * may be none but then there is no order to preserve. + * + * For write png_write_IHDR has been called and that means that the info + * before PLTE has been written, so the chunk goes after. + */ + if (png_ptr->read_struct) + return PNG_HAVE_IHDR; + + else /* write struct */ + return PNG_HAVE_PLTE; + } + + else if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) != 0U) + { + /* This should not happen on read, on write it means that the app has + * started writing so assume the text is meant to go before PLTE. + */ + return PNG_HAVE_IHDR; + } + + /* Either a png_set_ from the application during read before reading + * starts (so mode is 0) or the same for write. + */ + else + { + if (png_ptr->read_struct) + return PNG_HAVE_IHDR; + + else + return PNG_HAVE_PLTE|PNG_AFTER_IDAT; + } +} +#endif /* TEXT || tIME */ + #ifdef PNG_TEXT_SUPPORTED void PNGAPI png_set_text(png_structrp png_ptr, png_inforp info_ptr, @@ -697,7 +780,7 @@ png_set_text_2(png_structrp png_ptr, png_inforp info_ptr, { int i; - png_debug1(1, "in %lx storage function", png_ptr == NULL ? "unexpected" : + png_debug1(1, "in %lx storage function", png_ptr == NULL ? 0xabadca11 : (unsigned long)png_ptr->chunk_name); if (png_ptr == NULL || info_ptr == NULL || num_text <= 0 || text_ptr == NULL) @@ -714,6 +797,11 @@ png_set_text_2(png_structrp png_ptr, png_inforp info_ptr, int max_text; png_textp new_text = NULL; + /* The code below goes horribly wrong if old_num_text ever ends up + * negative, so: + */ + affirm(old_num_text >= 0); + /* Calculate an appropriate max_text, checking for overflow. */ max_text = old_num_text; if (num_text <= INT_MAX - max_text) @@ -727,18 +815,18 @@ png_set_text_2(png_structrp png_ptr, png_inforp info_ptr, else max_text = INT_MAX; - /* Now allocate a new array and copy the old members in, this does all + /* Now allocate a new array and copy the old members in; this does all * the overflow checks. */ new_text = png_voidcast(png_textp,png_realloc_array(png_ptr, - info_ptr->text, old_num_text, max_text-old_num_text, - sizeof *new_text)); + info_ptr->text, old_num_text, max_text-old_num_text, + sizeof *new_text)); } if (new_text == NULL) { png_chunk_report(png_ptr, "too many text chunks", - PNG_CHUNK_WRITE_ERROR); + PNG_CHUNK_WRITE_ERROR); return 1; } @@ -766,7 +854,7 @@ png_set_text_2(png_structrp png_ptr, png_inforp info_ptr, text_ptr[i].compression >= PNG_TEXT_COMPRESSION_LAST) { png_chunk_report(png_ptr, "text compression mode is out of range", - PNG_CHUNK_WRITE_ERROR); + PNG_CHUNK_WRITE_ERROR); continue; } @@ -795,14 +883,20 @@ png_set_text_2(png_structrp png_ptr, png_inforp info_ptr, else lang_key_len = 0; } -# else /* PNG_iTXt_SUPPORTED */ +# else /* iTXt */ { png_chunk_report(png_ptr, "iTXt chunk not supported", - PNG_CHUNK_WRITE_ERROR); + PNG_CHUNK_WRITE_ERROR); continue; } # endif + /* Record the location for posterity. On the read side this just says + * where the chunk was (approximately). On the write side it says how far + * through the write process libpng was before this API was called. + */ + textp->location = get_location(png_ptr); + if (text_ptr[i].text == NULL || text_ptr[i].text[0] == '\0') { text_length = 0; @@ -827,7 +921,8 @@ png_set_text_2(png_structrp png_ptr, png_inforp info_ptr, if (textp->key == NULL) { png_chunk_report(png_ptr, "text chunk: out of memory", - PNG_CHUNK_WRITE_ERROR); + PNG_CHUNK_WRITE_ERROR); + return 1; } @@ -891,23 +986,38 @@ png_set_tIME(png_const_structrp png_ptr, png_inforp info_ptr, { png_debug1(1, "in %s storage function", "tIME"); - if (png_ptr == NULL || info_ptr == NULL || mod_time == NULL || - (png_ptr->mode & PNG_WROTE_tIME)) + if (png_ptr == NULL || info_ptr == NULL || mod_time == NULL) return; + /* It is valid to do set the tIME chunk after the IDAT has been written, but + * not if one has already been written. This was ignored before - the + * previous time was used - this is a bad thing. + */ + if ((info_ptr->valid & PNG_INFO_tIME) != 0 /* Changing a time chunk */ && + (png_ptr->mode & PNG_HAVE_IHDR) != 0 /* after writing started */) + { + /* So it can be *set* but it can't be *changed* after the info before PLTE + * has been written. (Note that putting tIME into an unknown chunk + * currently gets round this; to be fixed.) + */ + png_app_error(png_ptr, "cannot change tIME after writing starts"); + return; + } + if (mod_time->month == 0 || mod_time->month > 12 || mod_time->day == 0 || mod_time->day > 31 || mod_time->hour > 23 || mod_time->minute > 59 || mod_time->second > 60) { - png_warning(png_ptr, "Ignoring invalid time value"); + png_app_error(png_ptr, "Ignoring invalid time value"); return; } info_ptr->mod_time = *mod_time; + info_ptr->time_location = get_location(png_ptr); info_ptr->valid |= PNG_INFO_tIME; } -#endif +#endif /* tIME */ #ifdef PNG_tRNS_SUPPORTED void PNGAPI @@ -919,11 +1029,11 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr, if (png_ptr == NULL || info_ptr == NULL) return; - if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + if ((info_ptr->format & PNG_FORMAT_FLAG_ALPHA) != 0) png_chunk_report(png_ptr, - "png_set_tRNS: invalid on PNG with alpha channel", PNG_CHUNK_ERROR); + "png_set_tRNS: invalid on PNG with alpha channel", PNG_CHUNK_ERROR); - else if (info_ptr->color_type & PNG_COLOR_MASK_PALETTE) + else if ((info_ptr->format & PNG_FORMAT_FLAG_COLORMAP) != 0) { int max_num; @@ -931,23 +1041,23 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); /* Do this just in case the old data was not owned by libpng: */ - info_ptr->valid &= ~PNG_INFO_tRNS; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_tRNS); info_ptr->trans_alpha = NULL; info_ptr->num_trans = 0; /* Expect png_set_PLTE to happen before png_set_tRNS, so num_palette will * be set, but this is not a requirement of the API. */ - if (png_ptr->num_palette) - max_num = png_ptr->num_palette; + if (info_ptr->num_palette) + max_num = info_ptr->num_palette; else - max_num = 1 << png_ptr->bit_depth; + max_num = 1 << info_ptr->bit_depth; if (num_trans > max_num) { png_chunk_report(png_ptr, "png_set_tRNS: num_trans too large", - PNG_CHUNK_ERROR); + PNG_CHUNK_ERROR); /* If control returns simply limit it; the behavior prior to 1.7 was to * issue a warning and skip the palette in png_write_tRNS. */ @@ -964,34 +1074,33 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr, * indices. */ info_ptr->trans_alpha = png_voidcast(png_bytep, png_malloc(png_ptr, - PNG_MAX_PALETTE_LENGTH)); + PNG_MAX_PALETTE_LENGTH)); info_ptr->free_me |= PNG_FREE_TRNS; memcpy(info_ptr->trans_alpha, trans_alpha, - (unsigned)/*SAFE*/num_trans); + (unsigned)/*SAFE*/num_trans); info_ptr->valid |= PNG_INFO_tRNS; - info_ptr->num_trans = (png_uint_16)num_trans; /* SAFE */ + info_ptr->num_trans = png_check_bits(png_ptr, num_trans, 9); } } else /* not a PALETTE image */ { - /* Invalidate any prior transparent color, set num_trans too, it is not + /* Invalidate any prior transparent color and set num_trans. It is not * used internally in this case but png_get_tRNS still returns it. */ - info_ptr->valid &= ~PNG_INFO_tRNS; + info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_tRNS); info_ptr->num_trans = 0; /* for png_get_tRNS */ - if (trans_color != NULL) + if (trans_color != NULL && info_ptr->bit_depth < 16) { - int sample_max = (1 << info_ptr->bit_depth); + unsigned int sample_max = (1U << info_ptr->bit_depth) - 1U; - if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY && - trans_color->gray <= sample_max) || - (info_ptr->color_type == PNG_COLOR_TYPE_RGB && + if (!(info_ptr->format & PNG_FORMAT_FLAG_COLOR) ? + trans_color->gray <= sample_max : trans_color->red <= sample_max && trans_color->green <= sample_max && - trans_color->blue <= sample_max)) + trans_color->blue <= sample_max) { info_ptr->trans_color = *trans_color; info_ptr->valid |= PNG_INFO_tRNS; @@ -1000,8 +1109,8 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr, else png_chunk_report(png_ptr, - "tRNS chunk has out-of-range samples for bit_depth", - PNG_CHUNK_ERROR); + "tRNS chunk has out-of-range samples for bit_depth", + PNG_CHUNK_ERROR); } } } @@ -1029,8 +1138,8 @@ png_set_sPLT(png_structrp png_ptr, * overflows. Notice that the parameters are (int) and (size_t) */ np = png_voidcast(png_sPLT_tp,png_realloc_array(png_ptr, - info_ptr->splt_palettes, info_ptr->splt_palettes_num, nentries, - sizeof *np)); + info_ptr->splt_palettes, info_ptr->splt_palettes_num, nentries, + sizeof *np)); if (np == NULL) { @@ -1061,8 +1170,8 @@ png_set_sPLT(png_structrp png_ptr, np->depth = entries->depth; - /* In the even of out-of-memory just return - there's no point keeping on - * trying to add sPLT chunks. + /* In the event of out-of-memory just return - there's no point keeping + * on trying to add sPLT chunks. */ length = strlen(entries->name) + 1; np->name = png_voidcast(png_charp, png_malloc_base(png_ptr, length)); @@ -1073,8 +1182,8 @@ png_set_sPLT(png_structrp png_ptr, memcpy(np->name, entries->name, length); /* IMPORTANT: we have memory now that won't get freed if something else - * goes wrong, this code must free it. png_malloc_array produces no - * warnings, use a png_chunk_report (below) if there is an error. + * goes wrong; this code must free it. png_malloc_array produces no + * warnings; use a png_chunk_report (below) if there is an error. */ np->entries = png_voidcast(png_sPLT_entryp, png_malloc_array(png_ptr, entries->nentries, sizeof (png_sPLT_entry))); @@ -1082,6 +1191,7 @@ png_set_sPLT(png_structrp png_ptr, if (np->entries == NULL) { png_free(png_ptr, np->name); + np->name = NULL; break; } @@ -1090,7 +1200,7 @@ png_set_sPLT(png_structrp png_ptr, * checked it when doing the allocation. */ memcpy(np->entries, entries->entries, - entries->nentries * sizeof (png_sPLT_entry)); + entries->nentries * sizeof (png_sPLT_entry)); /* Note that 'continue' skips the advance of the out pointer and out * count, so an invalid entry is not added. @@ -1098,13 +1208,14 @@ png_set_sPLT(png_structrp png_ptr, info_ptr->valid |= PNG_INFO_sPLT; ++(info_ptr->splt_palettes_num); ++np; + ++entries; } - while (++entries, --nentries); + while (--nentries); if (nentries > 0) png_chunk_report(png_ptr, "sPLT out of memory", PNG_CHUNK_WRITE_ERROR); } -#endif /* PNG_sPLT_SUPPORTED */ +#endif /* sPLT */ #ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED static png_byte @@ -1113,17 +1224,17 @@ check_location(png_const_structrp png_ptr, int location) location &= (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT); /* New in 1.6.0; copy the location and check it. This is an API - * change, previously the app had to use the + * change; previously the app had to use the * png_set_unknown_chunk_location API below for each chunk. */ - if (location == 0 && !(png_ptr->mode & PNG_IS_READ_STRUCT)) + if (location == 0 && !png_ptr->read_struct) { /* Write struct, so unknown chunks come from the app */ png_app_warning(png_ptr, - "png_set_unknown_chunks now expects a valid location"); + "png_set_unknown_chunks now expects a valid location"); /* Use the old behavior */ - location = (png_byte)(png_ptr->mode & - (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT)); + location = png_check_byte(png_ptr, png_ptr->mode & + (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT)); } /* This need not be an internal error - if the app calls @@ -1141,17 +1252,17 @@ check_location(png_const_structrp png_ptr, int location) /* The cast is safe because 'location' is a bit mask and only the low four * bits are significant. */ - return (png_byte)location; + return png_check_byte(png_ptr, location); } void PNGAPI png_set_unknown_chunks(png_structrp png_ptr, - png_inforp info_ptr, png_const_unknown_chunkp unknowns, int num_unknowns) + png_inforp info_ptr, png_const_unknown_chunkp unknowns, int num_unknowns) { png_unknown_chunkp np; if (png_ptr == NULL || info_ptr == NULL || num_unknowns <= 0 || - unknowns == NULL) + unknowns == NULL) return; /* Check for the failure cases where support has been disabled at compile @@ -1160,19 +1271,19 @@ png_set_unknown_chunks(png_structrp png_ptr, * code) but may be meaningless if the read or write handling of unknown * chunks is not compiled in. */ -# if !defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) && \ - defined(PNG_READ_SUPPORTED) - if (png_ptr->mode & PNG_IS_READ_STRUCT) +# ifndef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED + if (png_ptr->read_struct) { png_app_error(png_ptr, "no unknown chunk support on read"); + return; } # endif -# if !defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) && \ - defined(PNG_WRITE_SUPPORTED) - if (!(png_ptr->mode & PNG_IS_READ_STRUCT)) +# ifndef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + if (!png_ptr->read_struct) { png_app_error(png_ptr, "no unknown chunk support on write"); + return; } # endif @@ -1188,9 +1299,7 @@ png_set_unknown_chunks(png_structrp png_ptr, if (np == NULL) { - png_chunk_report(png_ptr, "too many unknown chunks", - PNG_CHUNK_WRITE_ERROR); - + png_chunk_report(png_ptr, "too many unknown chunks", PNG_CHUNK_ERROR); return; } @@ -1218,12 +1327,12 @@ png_set_unknown_chunks(png_structrp png_ptr, else { np->data = png_voidcast(png_bytep, - png_malloc_base(png_ptr, unknowns->size)); + png_malloc_base(png_ptr, unknowns->size)); if (np->data == NULL) { png_chunk_report(png_ptr, "unknown chunk: out of memory", - PNG_CHUNK_WRITE_ERROR); + PNG_CHUNK_ERROR); /* But just skip storing the unknown chunk */ continue; } @@ -1255,7 +1364,7 @@ png_set_unknown_chunk_location(png_const_structrp png_ptr, png_inforp info_ptr, { png_app_error(png_ptr, "invalid unknown chunk location"); /* Fake out the pre 1.6.0 behavior: */ - if ((location & PNG_HAVE_IDAT)) /* undocumented! */ + if ((location & PNG_HAVE_IDAT) != 0) /* undocumented! */ location = PNG_AFTER_IDAT; else @@ -1270,44 +1379,34 @@ png_set_unknown_chunk_location(png_const_structrp png_ptr, png_inforp info_ptr, else if (png_ptr != NULL) png_app_warning(png_ptr, "unknown chunk index out of range"); } -#endif - - -#ifdef PNG_MNG_FEATURES_SUPPORTED -png_uint_32 PNGAPI -png_permit_mng_features (png_structrp png_ptr, png_uint_32 mng_features) -{ - png_debug(1, "in png_permit_mng_features"); - - if (png_ptr == NULL) - return 0; - - png_ptr->mng_features_permitted = mng_features & PNG_ALL_MNG_FEATURES; - - return png_ptr->mng_features_permitted; -} -#endif +#endif /* STORE_UNKNOWN_CHUNKS */ #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED static unsigned int -add_one_chunk(png_bytep list, unsigned int count, png_const_bytep add, int keep) +add_one_chunk(png_structrp png_ptr, png_bytep list, unsigned int count, + png_const_bytep add, int keep) { unsigned int i; /* Utility function: update the 'keep' state of a chunk if it is already in * the list, otherwise add it to the list. */ - for (i=0; i= PNG_HANDLE_CHUNK_LAST) { png_app_error(png_ptr, "png_set_keep_unknown_chunks: invalid keep"); return; } +# ifndef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED + /* This is only a warning; if the application handles the chunk with a + * read callback it may work fine. + */ + if (png_ptr->read_struct && keep >= PNG_HANDLE_CHUNK_IF_SAFE) + png_app_warning(png_ptr, + "png_set_keep_unknown_chunks: unsupported keep"); +# endif + if (num_chunks_in <= 0) { - png_ptr->unknown_default = keep; + png_ptr->unknown_default = (unsigned)keep & 0x3; /* '0' means just set the flags, so stop here */ if (num_chunks_in == 0) @@ -1364,7 +1475,7 @@ png_set_keep_unknown_chunks(png_structrp png_ptr, int keep, }; chunk_list = chunks_to_ignore; - num_chunks = (sizeof chunks_to_ignore)/5; + num_chunks = (unsigned int)/*SAFE*/(sizeof chunks_to_ignore)/5U; } else /* num_chunks_in > 0 */ @@ -1424,12 +1535,15 @@ png_set_keep_unknown_chunks(png_structrp png_ptr, int keep, unsigned int i; for (i=0; irow_pointers && (info_ptr->row_pointers != row_pointers)) + if (info_ptr->row_pointers != NULL && + (info_ptr->row_pointers != row_pointers)) png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); info_ptr->row_pointers = row_pointers; @@ -1499,63 +1615,25 @@ png_set_rows(png_const_structrp png_ptr, png_inforp info_ptr, #endif void PNGAPI -png_set_compression_buffer_size(png_structrp png_ptr, png_size_t size) +png_set_compression_buffer_size(png_structrp png_ptr, png_alloc_size_t size) { - if (png_ptr == NULL) - return; + if (png_ptr == NULL) + return; - if (size == 0 || size > PNG_UINT_31_MAX) - png_error(png_ptr, "invalid compression buffer size"); + if (size == 0 || + size > (png_ptr->read_struct ? ZLIB_IO_MAX : PNG_UINT_31_MAX)) + png_error(png_ptr, "invalid compression buffer size"); -# ifdef PNG_SEQUENTIAL_READ_SUPPORTED - if (png_ptr->mode & PNG_IS_READ_STRUCT) - { - png_ptr->IDAT_read_size = (png_uint_32)size; /* checked above */ - return; - } -# endif - -# ifdef PNG_WRITE_SUPPORTED - if (!(png_ptr->mode & PNG_IS_READ_STRUCT)) - { - if (png_ptr->zowner != 0) - { - png_warning(png_ptr, - "Compression buffer size cannot be changed because it is in use"); - return; - } - - if (size > ZLIB_IO_MAX) - { - png_warning(png_ptr, - "Compression buffer size limited to system maximum"); - size = ZLIB_IO_MAX; /* must fit */ - } - - else if (size < 6) - { - /* Deflate will potentially go into an infinite loop on a SYNC_FLUSH - * if this is permitted. - */ - png_warning(png_ptr, - "Compression buffer size cannot be reduced below 6"); - return; - } - - if (png_ptr->zbuffer_size != size) - { - png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list); - png_ptr->zbuffer_size = (uInt)size; - } - } -# endif +# if (defined PNG_SEQUENTIAL_READ_SUPPORTED) || defined PNG_WRITE_SUPPORTED + png_ptr->IDAT_size = (png_uint_32)/*SAFE*/size; +# endif /* SEQUENTIAL_READ || WRITE */ } void PNGAPI png_set_invalid(png_const_structrp png_ptr, png_inforp info_ptr, int mask) { - if (png_ptr && info_ptr) - info_ptr->valid &= ~mask; + if (png_ptr != NULL && info_ptr != NULL) + info_ptr->valid &= PNG_BIC_MASK(mask); } @@ -1567,13 +1645,13 @@ png_set_user_limits (png_structrp png_ptr, png_uint_32 user_width_max, { /* Images with dimensions larger than these limits will be * rejected by png_set_IHDR(). To accept any PNG datastream - * regardless of dimensions, set both limits to 0x7ffffffL. + * regardless of dimensions, set both limits to 0x7fffffffL. */ - if (png_ptr != NULL) - { - png_ptr->user_width_max = user_width_max; - png_ptr->user_height_max = user_height_max; - } + if (png_ptr == NULL) + return; + + png_ptr->user_width_max = user_width_max; + png_ptr->user_height_max = user_height_max; } /* This function was added to libpng 1.4.0 */ @@ -1592,50 +1670,5 @@ png_set_chunk_malloc_max (png_structrp png_ptr, if (png_ptr != NULL) png_ptr->user_chunk_malloc_max = user_chunk_malloc_max; } -#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ - - -#ifdef PNG_BENIGN_ERRORS_SUPPORTED -void PNGAPI -png_set_benign_errors(png_structrp png_ptr, int allowed) -{ - png_debug(1, "in png_set_benign_errors"); - - /* If allowed is 1, png_benign_error() is treated as a warning. - * - * If allowed is 0, png_benign_error() is treated as an error (which - * is the default behavior if png_set_benign_errors() is not called). - */ - - if (allowed != 0) - png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN | - PNG_FLAG_APP_WARNINGS_WARN | PNG_FLAG_APP_ERRORS_WARN; - - else - png_ptr->flags &= ~(PNG_FLAG_BENIGN_ERRORS_WARN | - PNG_FLAG_APP_WARNINGS_WARN | PNG_FLAG_APP_ERRORS_WARN); -} -#endif /* PNG_BENIGN_ERRORS_SUPPORTED */ - -#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED - /* Whether to report invalid palette index; added at libng-1.5.10. - * It is possible for an indexed (color-type==3) PNG file to contain - * pixels with invalid (out-of-range) indexes if the PLTE chunk has - * fewer entries than the image's bit-depth would allow. We recover - * from this gracefully by filling any incomplete palette with zeroes - * (opaque black). By default, when this occurs libpng will issue - * a benign error. This API can be used to override that behavior. - */ -void PNGAPI -png_set_check_for_invalid_index(png_structrp png_ptr, int allowed) -{ - png_debug(1, "in png_set_check_for_invalid_index"); - - if (allowed > 0) - png_ptr->num_palette_max = 0; - - else - png_ptr->num_palette_max = -1; -} -#endif -#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ +#endif /* ?SET_USER_LIMITS */ +#endif /* READ || WRITE */ diff --git a/ext/libpng17/pngstruct.h b/ext/libpng17/pngstruct.h index e4341eede6..c6ca8475e0 100644 --- a/ext/libpng17/pngstruct.h +++ b/ext/libpng17/pngstruct.h @@ -1,12 +1,11 @@ /* pngstruct.h - header file for PNG reference library * - * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * Last changed in libpng 1.7.0 [(PENDING RELEASE)] + * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * - * Last changed in libpng 1.7.0 [(PENDING RELEASE)] - * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h @@ -27,14 +26,14 @@ #ifndef ZLIB_CONST /* We must ensure that zlib uses 'const' in declarations. */ # define ZLIB_CONST -#endif +#endif /* !ZLIB_CONST */ #include PNG_ZLIB_HEADER #ifdef const /* zlib.h sometimes #defines const to nothing, undo this. */ # undef const -#endif +#endif /* const */ /* zlib.h has mediocre z_const use before 1.2.6, this stuff is for compatibility * with older builds. @@ -42,10 +41,10 @@ #if ZLIB_VERNUM < 0x1260 # define PNGZ_MSG_CAST(s) png_constcast(char*,s) # define PNGZ_INPUT_CAST(b) png_constcast(png_bytep,b) -#else +#else /* ZLIB_VERNUM >= 0x1260 */ # define PNGZ_MSG_CAST(s) (s) # define PNGZ_INPUT_CAST(b) (b) -#endif +#endif /* ZLIB_VERNUM >= 0x1260 */ /* zlib.h declares a magic type 'uInt' that limits the amount of data that zlib * can handle at once. This type need be no larger than 16 bits (so maximum of @@ -56,20 +55,19 @@ * even improve performance on some systems (and degrade it on others.) */ #ifndef ZLIB_IO_MAX -# define ZLIB_IO_MAX ((uInt)-1) -#endif +# ifdef __COVERITY__ +# define ZLIB_IO_MAX ((uInt)255U) /* else COVERITY whines */ +# else /* !COVERITY */ +# define ZLIB_IO_MAX ((uInt)-1) +# endif /* !COVERITY */ +#endif /* !ZLIB_IO_MAX */ #ifdef PNG_WRITE_SUPPORTED -/* The type of a compression buffer list used by the write code. */ -typedef struct png_compression_buffer -{ - struct png_compression_buffer *next; - png_byte output[1]; /* actually zbuf_size */ -} png_compression_buffer, *png_compression_bufferp; - -#define PNG_COMPRESSION_BUFFER_SIZE(pp)\ - (offsetof(png_compression_buffer, output) + (pp)->zbuffer_size) -#endif +/* The write compression control (allocated on demand). + * TODO: use this for the read state too. + */ +typedef struct png_zlib_state *png_zlib_statep; +#endif /* WRITE */ /* Colorspace support; structures used in png_struct, png_info and in internal * functions to hold and communicate information about the color space. @@ -103,7 +101,7 @@ typedef struct png_XYZ #endif /* COLORSPACE */ #if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED) -/* A colorspace is all the above plus, potentially, profile information, +/* A colorspace is all the above plus, potentially, profile information; * however at present libpng does not use the profile internally so it is only * stored in the png_info struct (if iCCP is supported.) The rendering intent * is retained here and is checked. @@ -116,13 +114,13 @@ typedef struct png_colorspace { #ifdef PNG_GAMMA_SUPPORTED png_fixed_point gamma; /* File gamma */ -#endif +#endif /* GAMMA */ #ifdef PNG_COLORSPACE_SUPPORTED png_xy end_points_xy; /* End points as chromaticities */ png_XYZ end_points_XYZ; /* End points as CIE XYZ colorant values */ png_uint_16 rendering_intent; /* Rendering intent of a profile */ -#endif +#endif /* COLORSPACE */ /* Flags are always defined to simplify the code. */ png_uint_16 flags; /* As defined below */ @@ -131,33 +129,251 @@ typedef struct png_colorspace typedef const png_colorspace * PNG_RESTRICT png_const_colorspacerp; /* General flags for the 'flags' field */ -#define PNG_COLORSPACE_HAVE_GAMMA 0x0001 -#define PNG_COLORSPACE_HAVE_ENDPOINTS 0x0002 -#define PNG_COLORSPACE_HAVE_INTENT 0x0004 -#define PNG_COLORSPACE_FROM_gAMA 0x0008 -#define PNG_COLORSPACE_FROM_cHRM 0x0010 -#define PNG_COLORSPACE_FROM_sRGB 0x0020 -#define PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB 0x0040 -#define PNG_COLORSPACE_MATCHES_sRGB 0x0080 /* exact match on profile */ -#define PNG_COLORSPACE_RGB_TO_GRAY_SET 0x0100 /* user specified coeffs */ -#define PNG_COLORSPACE_INVALID 0x8000 -#define PNG_COLORSPACE_CANCEL(flags) (0xffff ^ (flags)) +#define PNG_COLORSPACE_HAVE_GAMMA 0x0001U +#define PNG_COLORSPACE_HAVE_ENDPOINTS 0x0002U +#define PNG_COLORSPACE_HAVE_INTENT 0x0004U +#define PNG_COLORSPACE_FROM_gAMA 0x0008U +#define PNG_COLORSPACE_FROM_cHRM 0x0010U +#define PNG_COLORSPACE_FROM_sRGB 0x0020U +#define PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB 0x0040U +#define PNG_COLORSPACE_MATCHES_sRGB 0x0080U /* exact match on profile */ +#define PNG_COLORSPACE_RGB_TO_GRAY_SET 0x0100U /* user specified coeffs */ +#define PNG_COLORSPACE_INVALID 0x8000U +#define PNG_COLORSPACE_CANCEL(flags) (0xffffU - (flags)) #endif /* COLORSPACE || GAMMA */ +#ifdef PNG_TRANSFORM_MECH_SUPPORTED +/***************************** READ and WRITE TRANSFORMS *********************** + * These structures are used in pngrtran.c, pngwtran.c and pngtrans.c to hold + * information about transforms in progress. This mechanism was introduced in + * libpng 1.7.0 to ensure reliable transform code and to fix multiple bugs in + * the pre-1.7 transform handling. + * + * Prior to 1.7.0 the internal transform routines took a png_row_infop, like the + * user transform function, but without the png_ptr because it was never used. + * In 1.7.0 a separate internal structure is used in place of this to allow both + * future development to change the structure. + * + * The values in this structure will normally be changed by transformation + * implementations. + ***************************** READ and WRITE TRANSFORMS **********************/ +typedef struct +{ + png_structp png_ptr; /* png_struct for error handling and some + * transform parameters. May be aliased. + */ + png_const_voidp sp; /* Source; the input row. */ + png_voidp dp; /* Output buffer for the transformed row, + * this may be the same as sp. + */ + /* If the row is changed the tranform routine must write the result to + * dp[] and set sp to dp, otherwise it must not write to dp and must leave + * sp unchanged. dp[] and sp[] are both 'malloc' aligned; i.e. they have + * the system alignment, so the data can be read as any valid ANSI-C + * type. + */ + png_uint_32 width; /* width of row */ +# ifdef PNG_READ_GAMMA_SUPPORTED + png_fixed_point gamma; /* Current gamma of the row data */ + /* When a row is being transformed this contains the current gamma of the + * data if known. During initialization the value is used to accumulate + * information for png_struct::row_gamma in the first step, + * PNG_TC_INIT_FORMAT, then used to insert the correct gamma transforms + * during PNG_TC_INIT_FINAL. The field is only used on read; write + * transforms do not modify the gamma of the data. + */ +# endif /* READ_GAMMA */ + unsigned int format; /* As pngstruct::row_format below */ + unsigned int range; /* Count of range transforms */ +# define PNG_TC_CHANNELS(tc) PNG_FORMAT_CHANNELS((tc).format) + unsigned int bit_depth; /* bit depth of row */ + png_byte sBIT_R; + png_byte sBIT_G; + png_byte sBIT_B; + png_byte sBIT_A; /* Signnificant bits in the row channels. */ + /* The above four values are initially set to the number of bits significant + * in the input PNG data, R/G/B are set to the same (gray) value for + * grayscale input. All values are set to the bit depth if there is no sBIT + * chunk, if there is no alpha channel sBIT_A is set to the bit depth. + * + * When any potentially spurious bits have been cleared PNG_INFO_sBIT will be + * set in invalid_info. From this point on the above values record the + * approximate number of bits of accuracy in the channels and the lower bits + * should be preserved; they potentially contain useful information. + */ +# define PNG_TC_PIXEL_DEPTH(tc) (PNG_TC_CHANNELS(tc) * (tc).bit_depth) +# define PNG_TC_ROWBYTES(tc) PNG_ROWBYTES(PNG_TC_PIXEL_DEPTH(tc), (tc).width) + unsigned int invalid_info; /* PNG_INFO_* for invalidated chunks */ + unsigned int cost; /* Cache cost */ +# define PNG_CACHE_COST_LIMIT 0x100U + /* This is a runtime structure, so size doesn't matter much, and it helps + * code reliability to use real member names here. Feel free to experiment + * with integer values rather than bitfields. + */ + unsigned int init :2; /* 0 for processing, non zero for init: */ +# define PNG_TC_INIT_FORMAT 0x01U /* Initialization step 1: just set 'format', + * 'bit_depth' and 'gamma' to the output + * values iff the transform corresponds to + * a user requested change to those values. + */ +# define PNG_TC_INIT_FINAL 0x03U /* Initialization step 2; set the 'format' + * 'bit_depth' and 'gamma' to the values the + * transform will actually produce (which + * need not be the same as the above). + */ + /* During initialization 'init' must be set and sp and dp may be NULL. If + * neither flag is set sp and dp must be non-NULL. + * + * When the transform runs it must update 'format', 'bit_depth' and 'gamma' + * to the values previously reported during PNG_TC_INIT_FINAL; not doing so + * may result in an affirm from a later transform. + */ + unsigned int caching :1; /* The color values are being used to + * generate a cache of the transforms. + */ + unsigned int palette :1; /* The values come from a PNG palette and + * the palette will not be expanded. The + * CACHE flag must be set too. A + * transform which causes the palette to + * be expanded must clear this flag. + */ +#if 0 /* NYI */ + unsigned int interchannel:1; /* Set by a transform that combines two or + * more channels together; for example + * alpha composition or RGB to gray. + */ +#endif /* NYI */ + unsigned int channel_add :1; /* A channel (alpha/filler) was added */ + unsigned int strip_alpha :1; /* Set if the alpha channel will be + * stripped on read, this also prevents + * the tRNS chunk being expanded. Only + * some transforms check this, depending + * on the handling order and checks in + * pre-1.7 versions. + */ + unsigned int expand_tRNS :1; /* Set if the tRNS chunk should be + * expanded (ignored if read_strip_alpha + * is set). If this is *not* set + * transforms which do not use alpha/tRNS + * but would invalidate it (such as + * simple gamma correction) will simply + * mark the tRNS info as invalid. + */ + unsigned int transparent_alpha :1; /* Indicates that the alpha channel + * consists entirely of opaque (1.0 alpha) + * or completely transparent (0.0 alpha) + * pixels. Set when tRNS is expanded to + * alpha. + */ + unsigned int optimized_alpha :1; /* Meaningful only when bit_depth is + * 16 and gamma is 1 or unknown (0). + * Indicates that pixels which are opaque + * (alpha 1.0) have not been expanded to + * 16-bit linear; instead these pixels + * are encoded in the final format in + * png_struct::row_bit_depth and + * png_struct::row_gamma. This will + * invariably match the file format. + */ +} png_transform_control, *png_transform_controlp; + +typedef const png_transform_control *png_const_transform_controlp; +typedef const png_row_info *png_const_row_infop; + +typedef struct png_transform *png_transformp; /* Forward declaration */ +typedef void (*png_transform_free_fn)(/* Function to free a transform */ + png_const_structrp png_ptr, + png_transformp transform); /* pointer to this transform */ + /* This function need not exist in a transform, it must free all the data + * allocated within the transform but not the transform itself. It is called + * from png_transform_free. + */ +typedef void (*png_transform_fn)(/* Function to implement a transform */ + png_transformp *transform, /* pointer to this transform */ + png_transform_controlp control); /* row information */ + /* The transform function has two modes of operation: + * + * 1) Initialization. The list of transforms is processed from the start to + * the end and each function is called with one of hte PNG_TC_INIT_ flags + * set in control->flags, control->dp and control->sp may be NULL. + * + * For read the control structure contains the input row format and bit + * depth, the transform function changes this to represent what the + * transform will produce when it runs. + * + * For write the control structure contains the *required* output format + * and bit depth. The transform function changes this to the values that + * it needs to produce the required values. + * + * In both cases the transform function may update the 'fn' function to a + * new function to perform the desired transform; this allows considerable + * optimization on multi-row images. + * + * In both cases the caller considers the pixel bit depth changes and + * records the maximum required so that it can allocate a suitably sized + * buffer. + * + * 2) Execution. + * + * In the read case the transforms are processed in the stored order and + * must transform the row data appropriately *and* update the bit depth + * and format as before. + * + * In the write case the transforms are called in the reverse order and + * the input bit depth and format should match the required values. + * + * It is valid during initialization for the transform function to push + * another transform into the list in either the read or the write case if + * the transform cannot handle (read) or produce (write) the required format. * The transform pushes another transform into the list ahead of itself (at + * *transform) and runs that initialization; when control is returned to the + * caller the caller will re-run the transform initialization. + * + * It is also valid (during initialization) to push new transforms onto the + * list, just so long as the order of the transform is greater than the + * current transform (so that the caller will still call the new transform + * initialization.) + * + * In the write case the user transform callback might still end up producing + * an unexpected format, but at present this is unavoidable; the libpng API + * is extremely inconsistent in how a user transform reports the changes it + * made. + * + * TODO: fix this, probably with an API change in 1.7.0 + */ +typedef struct png_transform /* Linked list of transform functions */ +{ + png_transformp next; /* Next transform in the list */ + png_transform_fn fn; /* Function to implement the transform */ + png_transform_free_fn free; /* Free allocated data, normally NULL */ + unsigned int order; /* Order of the transform in the list. */ + unsigned int size; /* Size of this structure (max 65535) */ + png_uint_32 args; /* Optional transform arguments. */ +} png_transform; +#endif /* TRANSFORM_MECH */ + +/* Action to take on CRC errors (four values) */ +typedef enum +{ + crc_error_quit = PNG_CRC_ERROR_QUIT-1, + crc_warn_discard = PNG_CRC_WARN_DISCARD-1, + crc_warn_use = PNG_CRC_WARN_USE-1, + crc_quiet_use = PNG_CRC_QUIET_USE-1 +} png_crc_action; + struct png_struct_def { /* Rearranged in libpng 1.7 to attempt to lessen padding; in general - * (char), (short), (int) and pointer types are kept separate, however + * (char), (short), (int) and pointer types are kept separate; however * associated members under the control of the same #define are still * together. */ #ifdef PNG_SETJMP_SUPPORTED /* jmp_buf can have very high alignment requirements on some systems, so put * it first (the other setjmp members are later as they are infrequently - * accesed.) + * accessed.) */ jmp_buf jmp_buf_local; -#endif +#endif /* SETJMP */ /* Next the frequently accessed fields. Many processors perform arithmetic * in the address pipeline, but frequently the amount of addition or @@ -165,59 +381,223 @@ struct png_struct_def * the hope is that such processors will generate code that is both smaller * and faster. */ +#ifdef PNG_READ_SUPPORTED png_colorp palette; /* palette from the input file */ - size_t rowbytes; /* size of row in bytes */ - size_t info_rowbytes; /* cache of updated row bytes */ +#endif /* READ */ +#ifdef PNG_READ_tRNS_SUPPORTED + png_bytep trans_alpha; /* alpha values for paletted files */ +#endif /* READ_tRNS */ + png_uint_32 width; /* width of image in pixels */ png_uint_32 height; /* height of image in pixels */ - png_uint_32 num_rows; /* number of rows in current pass */ - png_uint_32 usr_width; /* width of row at start of write */ - png_uint_32 iwidth; /* width of current interlaced row in pixels */ - png_uint_32 row_number; /* current row in interlace pass */ png_uint_32 chunk_name; /* PNG_CHUNK() id of current chunk */ +#ifdef PNG_READ_SUPPORTED + png_uint_32 chunk_length; /* Length (possibly remaining) in said chunk. */ +#endif /* READ */ png_uint_32 crc; /* current chunk CRC value */ - png_uint_32 mode; /* tells us where we are in the PNG file */ - png_uint_32 flags; /* flags indicating various things to libpng */ - png_uint_32 transformations;/* which transformations to perform */ - png_uint_32 zowner; /* ID (chunk type) of zstream owner, 0 if none */ - png_uint_32 free_me; /* items libpng is responsible for freeing */ - int maximum_pixel_depth; /* pixel depth used for the row buffers */ -#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED - int num_palette_max; /* maximum palette index found in IDAT */ -#endif - - png_uint_16 num_palette; /* number of color entries in palette */ - png_uint_16 num_trans; /* number of transparency values */ + unsigned int mode :6; /* where we are in the PNG file */ + unsigned int read_struct :1; /* this is a read (not write) struct */ + unsigned int num_palette :9; /* number of color entries in palette */ +#ifdef PNG_READ_tRNS_SUPPORTED + unsigned int num_trans :9; /* number of transparency values */ + unsigned int transparent_palette :1; /* if they are all 0 or 255 */ +#endif /* READ_tRNS */ +#ifdef PNG_PALETTE_MAX_SUPPORTED + unsigned int palette_index_max :8; /* maximum palette index in IDAT */ + unsigned int palette_index_check :2; /* one of the following: */ +# define PNG_PALETTE_CHECK_DEFAULT 0U +# define PNG_PALETTE_CHECK_OFF 1U +# define PNG_PALETTE_CHECK_ON 2U + unsigned int palette_index_have_max :1; /* max is being set */ + unsigned int palette_index_check_issued :1; /* error message output */ +#endif /* PALETTE_MAX */ +#ifdef PNG_READ_tRNS_SUPPORTED + png_color_16 trans_color; /* transparent color for non-paletted files */ +#endif /* READ_tRNS */ +#ifdef PNG_READ_sBIT_SUPPORTED + png_color_8 sig_bit; /* significant bits in each channel */ +#endif /* READ_sBIT */ /* Single byte values, typically used either to save space or to hold 1-byte * values from the PNG chunk specifications. */ - png_byte compression_type; /* file compression type (always 0) */ - png_byte filter; /* file filter type (always 0) */ + png_byte filter_method; /* file filter type (only non-0 with MNG) */ png_byte interlaced; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ - png_byte pass; /* current interlace pass (0 - 6) */ - png_byte do_filter; /* row filter flags (see PNG_FILTER_ below ) */ png_byte color_type; /* color type of file */ png_byte bit_depth; /* bit depth of file */ - png_byte pixel_depth; /* number of bits per pixel */ - png_byte channels; /* number of channels in file */ - png_byte sig_bytes; /* magic bytes read/written from start of file */ - png_byte transformed_pixel_depth; - /* pixel depth after read/write transforms */ + png_byte sig_bytes; /* magic bytes read/written at start of file */ + +#ifdef PNG_READ_SUPPORTED +#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED) + /* The png_struct colorspace structure is only required on read - on write it + * is in (just) the info_struct. + */ + png_colorspace colorspace; +#endif /* COLORSPACE || GAMMA */ +#endif /* READ */ + + /* Transform handling */ +#ifdef PNG_TRANSFORM_MECH_SUPPORTED + png_transformp transform_list; /* List of transformation to perform. */ +#endif /* TRANSFORM_MECH */ + + /* ROW BUFFERS and CONTROL + * + * Members used for image row compression (write) or decompression (read). + * filter byte (which is in next_filter.) All fields are only used during + * IDAT processing and start of 0. + */ +#ifdef PNG_READ_SUPPORTED + png_bytep row_buffer; /* primary row buffer */ +#if (defined(PNG_PROGRESSIVE_READ_SUPPORTED) ||\ + defined(PNG_READ_INTERLACING_SUPPORTED)) &&\ + defined(PNG_TRANSFORM_MECH_SUPPORTED) + png_bytep transformed_row; /* pointer to the transformed row, if + * required. May point to row_buffer. + */ +#endif /* (PROGRESSIVE_READ || READ_INTERLACING) && TRANSFORM_MECH */ + + png_alloc_size_t row_bytes_read; /* Total read in row */ +#endif /* READ */ + + png_uint_32 row_number; /* current row in pass */ +#ifdef PNG_READ_SUPPORTED +#ifdef PNG_READ_GAMMA_SUPPORTED + png_fixed_point row_gamma; /* Gamma of final output */ +#if 0 /* NYI */ + unsigned int gamma_accuracy; + /* LINEAR gamma cache table size (in bits) times 100; for non-linear + * tables the value used is gamma_accuracy/gamma where 'gamma' is the + * encoding value of the data (typically less than 1). + * + * default: PNG_DEFAULT_GAMMA_ACCURACY (665) + */ +#endif /* NYI */ + png_uint_16 gamma_threshold; + /* Gamma threshold value as a fixed-point value in the range 0..1; the + * threshold at or below which gamma correction is skipped. '0' forces + * gamma correction even when there is none because the input and output + * gammas are equal. + * + * default: PNG_GAMMA_THRESHOLD_FIXED (153) + */ +#endif /* READ_GAMMA */ +#ifdef PNG_READ_TRANSFORMS_SUPPORTED + unsigned int invalid_info; /* PNG_INFO_* for invalidated chunks */ + unsigned int palette_updated:1; /* png_struct::palette changed */ +#endif /* READ_TRANSFORMS */ +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED + unsigned int read_started :1; /* at least one call to png_read_row */ +#endif /* SEQUENTIAL_READ */ + /* The next field is just used by the read IDAT process functions to store + * the state of IDAT processing; they should not be altered or used by other + * functions. + */ + unsigned int row_state :2; /* state of row parsing (internal) */ +#endif /* READ */ + +#if defined (PNG_READ_INTERLACING_SUPPORTED) ||\ + defined (PNG_WRITE_INTERLACING_SUPPORTED) + unsigned int do_interlace :1; /* libpng handles the interlace */ +# endif /* R/W INTERLACING */ + + unsigned int pass :3; /* current (interlace) pass (0 - 6) */ + + /* The following fields are set by png_row_init to the pixel depths of the + * pixels at various states. If transforms are not supported they will + * always be the same value: + * + * READ WRITE + * input: PNG From application + * output: To application PNG + * max: Largest in transform + */ + unsigned int row_input_pixel_depth :8; + unsigned int row_output_pixel_depth :8; + unsigned int row_max_pixel_depth :8; + +# define PNG_RF_BITS 9 /* Number of bits required for the row format (below) */ +#ifdef PNG_TRANSFORM_MECH_SUPPORTED + /* The following fields describe the format of the user row; the output on + * read or the input on write, and give the maximum pixel depth, which + * controls the row buffer allocation size (row_allocated_bytes) above. + */ + unsigned int row_range :3; /* range error count */ + unsigned int row_bit_depth :6; /* bits per channel (up to 32) */ + unsigned int row_format:PNG_RF_BITS;/* format of output(R)/input(W) row: */ + /* PNG_FORMAT_FLAG_ALPHA 0x01U format with an alpha channel + * PNG_FORMAT_FLAG_COLOR 0x02U color format: otherwise grayscale + * PNG_FORMAT_FLAG_LINEAR 0x04U NOT used (informational) + * PNG_FORMAT_FLAG_COLORMAP 0x08U image data is color-mapped + * PNG_FORMAT_FLAG_BGR 0x10U BGR colors, else order is RGB + * PNG_FORMAT_FLAG_AFIRST 0x20U alpha channel comes first * + * PNG_FORAMT_FLAG_AFILLER 0x40U The 'alpha' channel is a filler: + * PNG_FORMAT_FLAG_ALPHA is set however the value in the alpha channel + * is not an alpha value and (therefore) cannot be used for alpha + * computations, it is just a filler value. PNG_COLOR_TYPE_FROM_FORMAT + * will return a color type *without* PNG_COLOR_MASK_ALPHA, however + * PNG_FORMAT_CHANNELS will return the correct number, including the + * filler channel. + * PNG_FORMAT_FLAG_SWAPPED 0x80U bytes or bits swapped: + * When the bit depth is 16 this means that the bytes within the + * components have been swapped, when the bit depth is less than 8 + * it means the pixels within the bytes have been swapped. It should + * not be set for 8-bit compononents (it is meaningless). + * PNG_FORMAT_FLAG_RANGE 0x100U component range not 0..bit-depth: + * Low-bit-depth grayscale components have been unpacked into bytes + * without scaling, or RGB[A] pixels have been shifted back to the + * significant-bit range from the sBIT chunk or channels (currently + * alpha or gray) have been inverted. + * PNG_FORMAT_FLAG_INVALID NOT STORED HERE + */ +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED + unsigned int info_format:PNG_RF_BITS; + /* This field is used to validate the png_info used to write the + * IHDR. This is a new check in 1.7.0; previously it was possible to pass + * a png_info from a png_read with the read tranform information in the + * format having manually removed the required transforms from the rows + * passed to png_write_row. + */ +#endif /* WRITE_TRANSFORMS */ +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + unsigned int write_invert_alpha :1; + /* This indicates the png_set_invert_alpha was called, it is used by the + * write code to implement the transform without needing to run the whole + * transform mechanism on the PNG palette data. + */ +#endif /* WRITE_INVERT_ALPHA */ +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + unsigned int rgb_to_gray_status :1; + /* If set an RGB pixel was encountered by the RGB to gray transform + * wherein !(r==g==b). + */ +#endif /* RGB_TO_GRAY */ +#endif /* TRANSFORM_MECH */ + +#ifdef PNG_READ_SUPPORTED + /* These, and IDAT_size below, control how much input and output (at most) is + * available to zlib during read decompression. + */ + png_alloc_size_t read_buffer_size; /* current size of the buffer */ +#endif /* READ */ + +#if defined(PNG_SEQUENTIAL_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + png_uint_32 IDAT_size; /* limit on IDAT read and write IDAT size */ +#endif /* SEQUENTIAL_READ || WRITE */ /* ERROR HANDLING */ #ifdef PNG_SETJMP_SUPPORTED jmp_buf *jmp_buf_ptr; /* passed to longjmp_fn */ png_longjmp_ptr longjmp_fn; /* setjmp non-local goto function. */ size_t jmp_buf_size; /* size of *jmp_buf_ptr, if allocated */ -#endif +#endif /* SETJMP */ /* Error/warning callbacks */ png_error_ptr error_fn; /* print an error message and abort */ #ifdef PNG_WARNINGS_SUPPORTED png_error_ptr warning_fn; /* print a warning and continue */ -#endif +#endif /* WARNINGS */ png_voidp error_ptr; /* user supplied data for the above */ /* MEMORY ALLOCATION */ @@ -225,29 +605,31 @@ struct png_struct_def png_malloc_ptr malloc_fn; /* allocate memory */ png_free_ptr free_fn; /* free memory */ png_voidp mem_ptr; /* user supplied data for the above */ -#endif +#endif /* USER_MEM */ /* IO and BASIC READ/WRITE SUPPORT */ png_voidp io_ptr; /* user supplied data for IO callbacks */ + png_rw_ptr rw_data_fn; /* read/write some bytes (must succeed) */ #ifdef PNG_READ_SUPPORTED - png_rw_ptr read_data_fn; /* read some bytes (must succeed) */ png_read_status_ptr read_row_fn; /* called after each row is decoded */ png_bytep read_buffer; /* buffer for reading chunk data */ /* During read the following array is set up to point to the appropriate * un-filter function, this allows per-image and per-processor optimization. */ - void (*read_filter[PNG_FILTER_VALUE_LAST-1])(png_row_infop row_info, - png_bytep row, png_const_bytep prev_row); + void (*read_filter[PNG_FILTER_VALUE_LAST-1])(png_alloc_size_t row_bytes, + unsigned int bpp, png_bytep row, png_const_bytep prev_row, + png_const_bytep prev_pixels); +#endif /* READ */ -#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED) - /* The png_struct colorspace structure is only required on read - on write it - * is in (just) the info_struct. - */ - png_colorspace colorspace; -#endif -#endif /* PNG_READ_SUPPORTED */ +#ifdef PNG_WRITE_SUPPORTED + png_write_status_ptr write_row_fn; /* called after each row is encoded */ + +#ifdef PNG_WRITE_FLUSH_SUPPORTED + png_flush_ptr output_flush_fn; /* Function for flushing output */ +#endif /* WRITE_FLUSH */ +#endif /* WRITE */ #ifdef PNG_SET_USER_LIMITS_SUPPORTED png_uint_32 user_width_max; /* Maximum width on read */ @@ -257,14 +639,14 @@ struct png_struct_def * - it is decremented as memory is allocated. */ png_alloc_size_t user_chunk_malloc_max; -#endif +#endif /* SET_USER_LIMITS */ #ifdef PNG_USER_LIMITS_SUPPORTED /* limit on total *number* of sPLT, text and unknown chunks that can be * stored. 0 means unlimited. This field is a counter - it is decremented * as chunks are encountered. */ png_uint_32 user_chunk_cache_max; -#endif +#endif /* USER_LIMITS */ /* The progressive reader gets passed data and calls application handling * functions when appropriate. @@ -278,81 +660,24 @@ struct png_struct_def png_bytep save_buffer_ptr; /* current location in save_buffer */ png_bytep save_buffer; /* buffer for previously read data */ png_bytep current_buffer_ptr; /* current location in current_buffer */ - png_bytep current_buffer; /* buffer for recently used data */ size_t save_buffer_size; /* amount of data now in save_buffer */ size_t save_buffer_max; /* total size of save_buffer */ - size_t buffer_size; /* total amount of available input data */ size_t current_buffer_size; /* amount of data now in current_buffer */ + size_t buffer_size; /* total amount of available input data */ - png_uint_32 push_length; /* size of current input chunk */ - png_uint_32 skip_length; /* bytes to skip in input data */ - - int process_mode; /* what push library is currently doing */ - int cur_palette; /* current push library palette index */ -#endif - -#ifdef PNG_WRITE_SUPPORTED - png_rw_ptr write_data_fn;/* write some bytes (must succeed) */ - png_write_status_ptr write_row_fn; /* called after each row is encoded */ -#endif - -#ifdef PNG_WRITE_FLUSH_SUPPORTED - png_flush_ptr output_flush_fn; /* Function for flushing output */ - png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */ - png_uint_32 flush_rows; /* number of rows written since last flush */ -#endif - -#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED - png_bytep prev_filters; /* filter type(s) of previous row(s) */ - png_uint_16p filter_weights; /* weight(s) for previous line(s) */ - png_uint_16p inv_filter_weights; /* 1/weight(s) for previous line(s) */ - png_uint_16p filter_costs; /* relative filter calculation cost */ - png_uint_16p inv_filter_costs; /* 1/relative filter calculation cost */ - png_byte heuristic_method; /* heuristic for row filter selection */ - png_byte num_prev_filters; /* number of weights for previous rows */ -#endif - -#ifdef PNG_WRITE_SUPPORTED - png_byte usr_bit_depth; /* bit depth of users row */ - png_byte usr_channels; /* channels at start of write */ -#endif + unsigned int process_mode :8; + /* This is one or two four bit codes describing the current state of the + * 'push' reader. Normally the low four bits are a state code, however in + * some cases this may be pushed to the top four bits and replaced by a + * different temporary state code. The value is, in effect, a two entry + * stack. + */ +#endif /* PROGRESSIVE_READ */ #ifdef PNG_IO_STATE_SUPPORTED png_uint_32 io_state; /* tells the app read/write progress */ -#endif - - /* ROW BUFFERS - * - * Members that hold pointers to the decompressed image rows. - */ - png_bytep row_buf; /* buffer for the current (unfiltered) row */ -#if defined(PNG_WRITE_FILTER_SUPPORTED) || defined(PNG_READ_SUPPORTED) - png_bytep prev_row; /* buffer to save the previous (unfiltered) row */ -#endif - -#ifdef PNG_READ_SUPPORTED - /* The row_buf and prev_row pointers are misaligned so that the start of the - * row - after the filter byte - is aligned, the 'big_' pointers record the - * original allocated pointer. - */ - png_bytep big_row_buf; - png_bytep big_prev_row; - size_t big_row_buf_size; /* Actual size of both */ -#endif - -#ifdef PNG_WRITE_SUPPORTED - /* This is somewhat excessive, there is no obvious reason on write to - * allocate a buffer for each possible filtered row, only for the one being - * tested and the current best. - * - * TODO: fix this - */ - png_bytep sub_row; /* buffer to save "sub" row when filtering */ - png_bytep up_row; /* buffer to save "up" row when filtering */ - png_bytep avg_row; /* buffer to save "avg" row when filtering */ - png_bytep paeth_row; /* buffer to save "Paeth" row when filtering */ -#endif +#endif /* IO_STATE */ /* UNKNOWN CHUNK HANDLING */ /* TODO: this is excessively complicated, there are multiple ways of doing @@ -366,195 +691,87 @@ struct png_struct_def * set_unknown_chunks interface.) */ png_voidp user_chunk_ptr; -#endif +#endif /* USER_CHUNKS */ #ifdef PNG_READ_USER_CHUNKS_SUPPORTED /* This is called back from the unknown chunk handling */ png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */ -#endif -#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED - /* Temporary storage for unknown chunk that the library doesn't recognize, - * used while reading the chunk. - */ - png_unknown_chunk unknown_chunk; -#endif +#endif /* READ_USER_CHUNKS */ + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + png_uint_32 known_unknown; /* Bit mask of known chunks to be treated as + * unknown in the read code. + */ +#ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED + png_uint_32 save_unknown; /* Whether to save or skip these chunks: + * 'save' is 'known & save', 'skip' is + * 'known & ~save'. + */ +# define png_IDATs_skipped(pp) (((pp)->known_unknown & ~(pp)->save_unknown)&1U) +#else /* !SAVE_UNKNOWN_CHUNKS */ +# define png_IDATs_skipped(pp) ((pp)->known_unknown & 1U) +#endif /* !SAVE_UNKNOWN_CHUNKS */ +#endif /* HANDLE_AS_UNKNOWN */ #ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED png_bytep chunk_list; /* List of png_byte[5]; the textual chunk name * followed by a PNG_HANDLE_* byte */ - int unknown_default; /* As PNG_HANDLE_* */ + unsigned int unknown_default :2; /* As PNG_HANDLE_* */ unsigned int num_chunk_list; /* Number of entries in the list */ -#endif - - /* USER TRANSFORM SUPPORT */ -#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED - png_user_transform_ptr read_user_transform_fn; /* user read transform */ -#endif -#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED - png_user_transform_ptr write_user_transform_fn; /* user write transform */ -#endif -#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED - png_voidp user_transform_ptr; /* user supplied data for the above */ - png_byte user_transform_depth; /* bit depth of user transformed pixels */ - png_byte user_transform_channels; /* channels in user transformed pixels */ -#endif - - /* READ TRANSFORM SUPPORT - * - * Quite a lot of things can be done to the original image data on read, and - * most of these are configurable. The data required by the configurable - * read transforms should be stored here. The png_color_16 and png_color_8 - * structures have low alignment requirements and odd sizes, so may cause - * misalignment when present. Member alignment is as follows: - * - * png_color_16 png_uint_16 - * png_color_8 png_byte - */ - /* GAMMA/BACKGROUND/ALPHA-MODE/RGB-TO-GRAY/tRNS/sBIT - * - * These things are all interrelated because they need some or all of the - * gamma tables. Some attempt has been made below to order these members by - * size, so that as little padding as possible is required. - */ -#ifdef PNG_READ_GAMMA_SUPPORTED - png_bytep gamma_table; /* gamma table for 8-bit depth files */ - png_uint_16p gamma_16_table; /* gamma table for 16-bit depth files */ - -#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) ||\ - defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) - png_bytep gamma_from_1; /* converts from 1.0 to screen */ - png_uint_16p gamma_to_1; /* converts from file to 1.0 */ - png_uint_16p gamma_16_from_1; /* converts from 1.0 to screen */ - png_uint_16p gamma_16_to_1; /* converts from file to 1.0 */ -#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ -#endif /* PNG_READ_GAMMA_SUPPORTED */ - -#if defined(PNG_READ_tRNS_SUPPORTED) || \ - defined(PNG_READ_BACKGROUND_SUPPORTED) || \ - defined(PNG_READ_EXPAND_SUPPORTED) - png_bytep trans_alpha; /* alpha values for paletted files */ -#endif - - /* Integer values */ -#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) - png_fixed_point background_gamma; -#endif -#ifdef PNG_READ_GAMMA_SUPPORTED - png_fixed_point screen_gamma; /* screen gamma value (display_exponent) */ - int gamma_shift; /* number of "insignificant" bits in 16-bit gamma */ -#endif - - /* png_color_16 */ -#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) - png_color_16 background; /* background color in screen gamma space */ - png_color_16 background_1; /* background normalized to gamma 1.0 */ -#endif -#if defined(PNG_READ_tRNS_SUPPORTED) || \ - defined(PNG_READ_BACKGROUND_SUPPORTED) || \ - defined(PNG_READ_EXPAND_SUPPORTED) - png_color_16 trans_color; /* transparent color for non-paletted files */ -#endif - - /* png_uint_16 */ -#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED - png_uint_16 rgb_to_gray_red_coeff; - png_uint_16 rgb_to_gray_green_coeff; - /* The blue coefficient is calculated from the above */ -#endif - - /* png_color_8 */ -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_sBIT_SUPPORTED) - png_color_8 sig_bit; /* significant bits in each available channel */ -#endif - - /* png_byte */ -#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) - png_byte background_gamma_type; -#endif -#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED - png_byte rgb_to_gray_status; -#endif - - /* SHIFT - both READ_SHIFT and WRITE_SHIFT */ -#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) - png_color_8 shift; /* shift for significant bit tranformation */ -#endif - - /* FILLER SUPPORT (pixel expansion or read, contraction on write) */ -#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) - png_uint_16 filler; /* filler bytes for pixel expansion */ -#endif - - /* QUANTIZE (convert to color-mapped) */ -#ifdef PNG_READ_QUANTIZE_SUPPORTED - png_bytep palette_lookup; /* lookup table for quantizing */ - png_bytep quantize_index; /* index translation for palette files */ - png_bytep quantize_sort; /* working sort array */ - png_bytep index_to_palette; /* where the original index currently is in the - * palette - */ - png_bytep palette_to_index; /* which original index points to this palette - * color - */ -#endif - - /* MNG SUPPORT */ -#ifdef PNG_MNG_FEATURES_SUPPORTED - png_uint_32 mng_features_permitted; - png_byte filter_type; -#endif - - /* Options */ -#ifdef PNG_SET_OPTION_SUPPORTED - png_byte options; /* On/off state (up to 4 options) */ -#endif +#endif /* SET_UNKNOWN_CHUNKS */ /* COMPRESSION AND DECOMPRESSION SUPPORT. * * zlib expects a 'zstream' as the fundamental control structure, it allows * all the parameters to be passed as one pointer. */ - z_stream zstream; /* decompression structure */ + png_uint_32 zowner; /* ID (chunk type) of zlib owner, 0 if none */ +# ifdef PNG_WRITE_SUPPORTED + png_zlib_statep zlib_state; /* State of zlib compression */ +# endif /* WRITE */ +# ifdef PNG_READ_SUPPORTED + z_stream zstream; /* decompression structure */ + unsigned int zstream_ended:1; /* no more zlib output available [read] */ + unsigned int zstream_error:1; /* zlib error message has been output [read] */ +# endif /* READ */ +# ifdef PNG_PROGRESSIVE_READ_SUPPORTED + unsigned int zstream_eod :1; /* all the required uncompressed data has been + * received; set by the zstream using code for + * its own purposes. [progressive read] */ +# endif /* PROGRESSIVE_READ */ +# ifdef PNG_BENIGN_ERRORS_SUPPORTED + unsigned int benign_error_action :2; + unsigned int app_warning_action :2; + unsigned int app_error_action :2; +# ifdef PNG_READ_SUPPORTED + unsigned int IDAT_error_action :2; +# endif /* READ */ +# endif /* BENIGN_ERRORS */ +# ifdef PNG_READ_SUPPORTED + /* CRC checking actions, one for critical chunks, one for ancillary + * chunks. + */ + unsigned int critical_crc :2; + unsigned int ancillary_crc :2; + unsigned int current_crc :2; /* Cache of one or other of the above */ +# endif +# ifdef PNG_SET_OPTION_SUPPORTED +# ifdef PNG_READ_SUPPORTED + unsigned int maximum_inflate_window :1U; +# endif /* READ */ + unsigned int skip_sRGB_profile_check :1U; +# endif /* SET_OPTION */ -#ifdef PNG_READ_SUPPORTED - /* These, and IDAT_read_size below, control how much input and output (at - * most) is available to zlib. + /* MNG SUPPORT */ +#ifdef PNG_MNG_FEATURES_SUPPORTED + unsigned int mng_features_permitted :3; +#endif /* MNG_FEATURES */ + + /* SCRATCH buffers, used when control returns to the application or a read + * loop. */ - png_uint_32 idat_size; /* current IDAT size for read */ - png_alloc_size_t read_buffer_size; /* current size of the buffer */ -#endif - -#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED - int zlib_text_level; /* holds zlib compression level */ - int zlib_text_method; /* holds zlib compression method */ - int zlib_text_window_bits; /* holds zlib compression window bits */ - int zlib_text_mem_level; /* holds zlib compression memory level */ - int zlib_text_strategy; /* holds zlib compression strategy */ -#endif - -#ifdef PNG_WRITE_SUPPORTED - int zlib_level; /* holds zlib compression level */ - int zlib_method; /* holds zlib compression method */ - int zlib_window_bits; /* holds zlib compression window bits */ - int zlib_mem_level; /* holds zlib compression memory level */ - int zlib_strategy; /* holds zlib compression strategy */ - - int zlib_set_level; /* Actual values set into the zstream on write */ - int zlib_set_method; - int zlib_set_window_bits; - int zlib_set_mem_level; - int zlib_set_strategy; - - png_compression_bufferp zbuffer_list; /* Created on demand during write */ - uInt zbuffer_size; /* size of the actual zlib buffer */ -#endif - -#ifdef PNG_SEQUENTIAL_READ_SUPPORTED - uInt IDAT_read_size; /* limit on read buffer size for IDAT */ -#endif +# ifdef PNG_READ_SUPPORTED + png_byte scratch[PNG_ROW_BUFFER_SIZE+16U]; +# endif /* READ */ }; #endif /* PNGSTRUCT_H */ diff --git a/ext/libpng17/pngtest.c b/ext/libpng17/pngtest.c index 9add642142..d872d803e7 100644 --- a/ext/libpng17/pngtest.c +++ b/ext/libpng17/pngtest.c @@ -1,8 +1,8 @@ /* pngtest.c - a simple test program to test libpng * - * Last changed in libpng 1.6.11 [(PENDING RELEASE)] - * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * Last changed in libpng 1.6.26 [(PENDING RELEASE)] + * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -31,11 +31,6 @@ * of files at once by typing "pngtest -m file1.png file2.png ..." */ -#ifdef _MSC_VER -#pragma warning (disable:4996) -#pragma warning (disable:4244) -#endif - #define _POSIX_SOURCE 1 #include @@ -66,10 +61,11 @@ defined PNG_READ_sBIT_SUPPORTED &&\ defined PNG_READ_sCAL_SUPPORTED &&\ defined PNG_READ_sRGB_SUPPORTED &&\ + defined PNG_READ_sPLT_SUPPORTED &&\ defined PNG_READ_tEXt_SUPPORTED &&\ defined PNG_READ_tIME_SUPPORTED &&\ defined PNG_READ_zTXt_SUPPORTED &&\ - defined PNG_WRITE_INTERLACING_SUPPORTED + (defined PNG_WRITE_INTERLACING_SUPPORTED || PNG_LIBPNG_VER >= 10700) #ifdef PNG_ZLIB_HEADER # include PNG_ZLIB_HEADER /* defined by pnglibconf.h from 1.7 */ @@ -106,6 +102,10 @@ typedef FILE * png_FILE_p; # define SINGLE_ROWBUF_ALLOC /* Makes buffer overruns easier to nail */ #endif +#ifndef PNG_UNUSED +# define PNG_UNUSED(param) (void)param; +#endif + /* Turn on CPU timing #define PNGTEST_TIMING */ @@ -123,6 +123,22 @@ static float t_start, t_stop, t_decode, t_encode, t_misc; #define PNG_tIME_STRING_LENGTH 29 static int tIME_chunk_present = 0; static char tIME_string[PNG_tIME_STRING_LENGTH] = "tIME chunk is not present"; + +#if PNG_LIBPNG_VER < 10619 +#define png_convert_to_rfc1123_buffer(ts, t) tIME_to_str(read_ptr, ts, t) + +static int +tIME_to_str(png_structp png_ptr, png_charp ts, png_const_timep t) +{ + png_const_charp str = png_convert_to_rfc1123(png_ptr, t); + + if (str == NULL) + return 0; + + strcpy(ts, str); + return 1; +} +#endif /* older libpng */ #endif static int verbose = 0; @@ -190,16 +206,14 @@ write_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass) #ifdef PNG_READ_USER_TRANSFORM_SUPPORTED -/* Example of using user transform callback (we don't transform anything, - * but merely examine the row filters. We set this to 256 rather than - * 5 in case illegal filter values are present.) +/* Example of using a user transform callback (doesn't do anything at present). */ -static png_uint_32 filters_used[256]; static void PNGCBAPI -count_filters(png_structp png_ptr, png_row_infop row_info, png_bytep data) +read_user_callback(png_structp png_ptr, png_row_infop row_info, png_bytep data) { - if (png_ptr != NULL && row_info != NULL) - ++filters_used[*(data - 1)]; + PNG_UNUSED(png_ptr) + PNG_UNUSED(row_info) + PNG_UNUSED(data) } #endif @@ -226,96 +240,97 @@ count_zero_samples(png_structp png_ptr, png_row_infop row_info, png_bytep data) * png_byte pixel_depth bits per pixel (depth*channels) */ - /* Counts the number of zero samples (or zero pixels if color_type is 3 */ + /* Counts the number of zero samples (or zero pixels if color_type is 3 */ - if (row_info->color_type == 0 || row_info->color_type == 3) - { - int pos = 0; - png_uint_32 n, nstop; + if (row_info->color_type == 0 || row_info->color_type == 3) + { + int pos = 0; + png_uint_32 n, nstop; - for (n = 0, nstop=row_info->width; nbit_depth == 1) - { - if (((*dp << pos++ ) & 0x80) == 0) - zero_samples++; + for (n = 0, nstop=row_info->width; nbit_depth == 1) + { + if (((*dp << pos++ ) & 0x80) == 0) + zero_samples++; - if (pos == 8) - { - pos = 0; - dp++; - } - } + if (pos == 8) + { + pos = 0; + dp++; + } + } - if (row_info->bit_depth == 2) - { - if (((*dp << (pos+=2)) & 0xc0) == 0) - zero_samples++; + if (row_info->bit_depth == 2) + { + if (((*dp << (pos+=2)) & 0xc0) == 0) + zero_samples++; - if (pos == 8) - { - pos = 0; - dp++; - } - } + if (pos == 8) + { + pos = 0; + dp++; + } + } - if (row_info->bit_depth == 4) - { - if (((*dp << (pos+=4)) & 0xf0) == 0) - zero_samples++; + if (row_info->bit_depth == 4) + { + if (((*dp << (pos+=4)) & 0xf0) == 0) + zero_samples++; - if (pos == 8) - { - pos = 0; - dp++; - } - } + if (pos == 8) + { + pos = 0; + dp++; + } + } - if (row_info->bit_depth == 8) - if (*dp++ == 0) - zero_samples++; + if (row_info->bit_depth == 8) + if (*dp++ == 0) + zero_samples++; - if (row_info->bit_depth == 16) - { - if ((*dp | *(dp+1)) == 0) - zero_samples++; - dp+=2; - } - } - } - else /* Other color types */ - { - png_uint_32 n, nstop; - int channel; - int color_channels = row_info->channels; - if (row_info->color_type > 3)color_channels--; + if (row_info->bit_depth == 16) + { + if ((*dp | *(dp+1)) == 0) + zero_samples++; + dp+=2; + } + } + } + else /* Other color types */ + { + png_uint_32 n, nstop; + int channel; + int color_channels = row_info->channels; + if (row_info->color_type > 3) + color_channels--; - for (n = 0, nstop=row_info->width; nbit_depth == 8) - if (*dp++ == 0) - zero_samples++; + for (n = 0, nstop=row_info->width; nbit_depth == 8) + if (*dp++ == 0) + zero_samples++; - if (row_info->bit_depth == 16) - { - if ((*dp | *(dp+1)) == 0) - zero_samples++; + if (row_info->bit_depth == 16) + { + if ((*dp | *(dp+1)) == 0) + zero_samples++; - dp+=2; - } - } - if (row_info->color_type > 3) - { - dp++; - if (row_info->bit_depth == 16) - dp++; - } - } - } + dp+=2; + } + } + if (row_info->color_type > 3) + { + dp++; + if (row_info->bit_depth == 16) + dp++; + } + } + } } -#endif /* PNG_WRITE_USER_TRANSFORM_SUPPORTED */ +#endif /* WRITE_USER_TRANSFORM */ #ifndef PNG_STDIO_SUPPORTED /* START of code to validate stdio-free compilation */ @@ -330,10 +345,10 @@ count_zero_samples(png_structp png_ptr, png_row_infop row_info, png_bytep data) #ifdef PNG_IO_STATE_SUPPORTED void pngtest_check_io_state(png_structp png_ptr, png_size_t data_length, - png_uint_32 io_op); + png_uint_32 io_op); void pngtest_check_io_state(png_structp png_ptr, png_size_t data_length, - png_uint_32 io_op) + png_uint_32 io_op) { png_uint_32 io_state = png_get_io_state(png_ptr); int err = 0; @@ -424,7 +439,7 @@ pngtest_write_data(png_structp png_ptr, png_bytep data, png_size_t length) pngtest_check_io_state(png_ptr, length, PNG_IO_WRITING); #endif } -#endif /* !PNG_STDIO_SUPPORTED */ +#endif /* !STDIO */ /* This function is called when there is a warning, but the library thinks * it can continue anyway. Replacement functions don't have to do anything @@ -473,7 +488,7 @@ pngtest_error(png_structp png_ptr, png_const_charp message) #if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG /* Allocate memory. For reasonable files, size should never exceed - * 64K. However, zlib may allocate more then 64K if you don't tell + * 64K. However, zlib may allocate more than 64K if you don't tell * it not to. See zconf.h and png.h for more information. zlib does * need to allocate exactly 64K, so whatever you call here must * have the ability to do that. @@ -517,7 +532,7 @@ PNGCBAPI png_debug_malloc(png_structp png_ptr, png_alloc_size_t size) memory_infop pinfo; png_set_mem_fn(png_ptr, NULL, NULL, NULL); pinfo = (memory_infop)png_malloc(png_ptr, - (sizeof *pinfo)); + (sizeof *pinfo)); pinfo->size = size; current_allocation += size; total_allocation += size; @@ -547,7 +562,7 @@ PNGCBAPI png_debug_malloc(png_structp png_ptr, png_alloc_size_t size) if (verbose != 0) printf("png_malloc %lu bytes at %p\n", (unsigned long)size, - pinfo->pointer); + pinfo->pointer); return (png_voidp)(pinfo->pointer); } @@ -569,6 +584,7 @@ png_debug_free(png_structp png_ptr, png_voidp ptr) } /* Unlink the element from the list. */ + if (pinformation != NULL) { memory_infop *ppinfo = &pinformation; @@ -585,15 +601,14 @@ png_debug_free(png_structp png_ptr, png_voidp ptr) /* We must free the list element too, but first kill the memory that is to be freed. */ memset(ptr, 0x55, pinfo->size); - if (pinfo != NULL) - free(pinfo); + free(pinfo); pinfo = NULL; break; } if (pinfo->next == NULL) { - fprintf(STDERR, "Pointer %x not found\n", (unsigned int)ptr); + fprintf(STDERR, "Pointer %p not found\n", ptr); break; } @@ -609,7 +624,7 @@ png_debug_free(png_structp png_ptr, png_voidp ptr) free(ptr); ptr = NULL; } -#endif /* PNG_USER_MEM_SUPPORTED && PNG_DEBUG */ +#endif /* USER_MEM && DEBUG */ /* END of code to test memory allocation/deallocation */ @@ -648,16 +663,16 @@ set_location(png_structp png_ptr, struct user_chunk_data *data, int what) { int location; - if ((data->location[0] & what) || (data->location[1] & what)) + if ((data->location[0] & what) != 0 || (data->location[1] & what) != 0) return 0; /* already have one of these */ - /* Find where we are (the code below zeros info_ptr to indicate that the + /* Find where we are (the code below zeroes info_ptr to indicate that the * chunks before the first IDAT have been read.) */ if (data->info_ptr == NULL) /* after IDAT */ location = what | after_IDAT; - else if (png_get_valid(png_ptr, data->info_ptr, PNG_INFO_PLTE)) + else if (png_get_valid(png_ptr, data->info_ptr, PNG_INFO_PLTE) != 0) location = what | before_IDAT; else @@ -704,7 +719,7 @@ read_user_chunk_callback(png_struct *png_ptr, png_unknown_chunkp chunk) if (chunk->data[0] != 0 && chunk->data[0] != 1) return (-1); /* Invalid mode */ - if (set_location(png_ptr, my_user_chunk_data, have_sTER)) + if (set_location(png_ptr, my_user_chunk_data, have_sTER) != 0) { my_user_chunk_data->sTER_mode=chunk->data[0]; return (1); @@ -723,7 +738,7 @@ read_user_chunk_callback(png_struct *png_ptr, png_unknown_chunkp chunk) if (chunk->size != 9) return (-1); /* Error return */ - if (!set_location(png_ptr, my_user_chunk_data, have_vpAg)) + if (set_location(png_ptr, my_user_chunk_data, have_vpAg) == 0) return (0); /* duplicate vpAg */ my_user_chunk_data->vpAg_width = png_get_uint_31(png_ptr, chunk->data); @@ -754,9 +769,9 @@ write_vpAg_chunk(png_structp write_ptr) if (verbose != 0) fprintf(STDERR, " vpAg = %lu x %lu, units = %d\n", - (unsigned long)user_chunk_data.vpAg_width, - (unsigned long)user_chunk_data.vpAg_height, - user_chunk_data.vpAg_units); + (unsigned long)user_chunk_data.vpAg_width, + (unsigned long)user_chunk_data.vpAg_height, + user_chunk_data.vpAg_units); png_save_uint_32(vpag_chunk_data, user_chunk_data.vpAg_width); png_save_uint_32(vpag_chunk_data + 4, user_chunk_data.vpAg_height); @@ -784,8 +799,8 @@ write_chunks(png_structp write_ptr, int location) write_vpAg_chunk(write_ptr); } } -#endif /* PNG_WRITE_SUPPORTED */ -#else /* !PNG_READ_USER_CHUNKS_SUPPORTED */ +#endif /* WRITE */ +#else /* !READ_USER_CHUNKS */ # define write_chunks(pp,loc) ((void)0) #endif /* END of code to demonstrate user chunk support */ @@ -796,8 +811,8 @@ write_chunks(png_structp write_ptr, int location) */ #ifdef PNG_TEXT_SUPPORTED static void -pngtest_check_text_support(png_const_structp png_ptr, png_textp text_ptr, - int num_text) +pngtest_check_text_support(png_structp png_ptr, png_textp text_ptr, + int num_text) { while (num_text > 0) { @@ -809,6 +824,8 @@ pngtest_check_text_support(png_const_structp png_ptr, png_textp text_ptr, case PNG_TEXT_COMPRESSION_zTXt: # ifndef PNG_WRITE_zTXt_SUPPORTED ++unsupported_chunks; + /* In libpng 1.7 this now does an app-error, so stop it: */ + text_ptr[num_text].compression = PNG_TEXT_COMPRESSION_NONE; # endif break; @@ -816,6 +833,7 @@ pngtest_check_text_support(png_const_structp png_ptr, png_textp text_ptr, case PNG_ITXT_COMPRESSION_zTXt: # ifndef PNG_WRITE_iTXt_SUPPORTED ++unsupported_chunks; + text_ptr[num_text].compression = PNG_TEXT_COMPRESSION_NONE; # endif break; @@ -842,16 +860,19 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) png_structp write_ptr; png_infop write_info_ptr; png_infop write_end_info_ptr; +#ifdef PNG_WRITE_FILTER_SUPPORTED int interlace_preserved = 1; -#else +#endif /* WRITE_FILTER */ +#else /* !WRITE */ png_structp write_ptr = NULL; png_infop write_info_ptr = NULL; png_infop write_end_info_ptr = NULL; -#endif +#endif /* !WRITE */ png_bytep row_buf; png_uint_32 y; png_uint_32 width, height; - int num_pass = 1, pass; + volatile int num_passes; + int pass; int bit_depth, color_type; row_buf = NULL; @@ -873,26 +894,26 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) pngtest_debug("Allocating read and write structures"); #if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG read_ptr = - png_create_read_struct_2(PNG_LIBPNG_VER_STRING, NULL, - NULL, NULL, NULL, png_debug_malloc, png_debug_free); + png_create_read_struct_2(PNG_LIBPNG_VER_STRING, NULL, + NULL, NULL, NULL, png_debug_malloc, png_debug_free); #else read_ptr = - png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); #endif png_set_error_fn(read_ptr, &error_parameters, pngtest_error, - pngtest_warning); + pngtest_warning); #ifdef PNG_WRITE_SUPPORTED #if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG write_ptr = - png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, - NULL, NULL, NULL, png_debug_malloc, png_debug_free); + png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, + NULL, NULL, NULL, png_debug_malloc, png_debug_free); #else write_ptr = - png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); #endif png_set_error_fn(write_ptr, &error_parameters, pngtest_error, - pngtest_warning); + pngtest_warning); #endif pngtest_debug("Allocating read_info, write_info and end_info structures"); read_info_ptr = png_create_info_struct(read_ptr); @@ -905,7 +926,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) #ifdef PNG_READ_USER_CHUNKS_SUPPORTED init_callback_info(read_info_ptr); png_set_read_user_chunk_fn(read_ptr, &user_chunk_data, - read_user_chunk_callback); + read_user_chunk_callback); #endif #ifdef PNG_SETJMP_SUPPORTED @@ -943,15 +964,16 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) #endif #endif +#ifdef PNG_BENIGN_ERRORS_SUPPORTED if (strict != 0) { /* Treat png_benign_error() as errors on read */ png_set_benign_errors(read_ptr, 0); -#ifdef PNG_WRITE_SUPPORTED +# ifdef PNG_WRITE_SUPPORTED /* Treat them as errors on write */ png_set_benign_errors(write_ptr, 0); -#endif +# endif /* if strict is not set, then app warnings and errors are treated as * warnings in release builds, but not in unstable builds; this can be @@ -964,10 +986,15 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) /* Allow application (pngtest) errors and warnings to pass */ png_set_benign_errors(read_ptr, 1); -#ifdef PNG_WRITE_SUPPORTED + /* Turn off CRC and ADLER32 checking while reading */ + png_set_crc_action(read_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); + +# ifdef PNG_WRITE_SUPPORTED png_set_benign_errors(write_ptr, 1); -#endif +# endif + } +#endif /* BENIGN_ERRORS */ pngtest_debug("Initializing input and output streams"); #ifdef PNG_STDIO_SUPPORTED @@ -980,9 +1007,9 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) # ifdef PNG_WRITE_SUPPORTED png_set_write_fn(write_ptr, (png_voidp)fpout, pngtest_write_data, # ifdef PNG_WRITE_FLUSH_SUPPORTED - pngtest_flush); + pngtest_flush); # else - NULL); + NULL); # endif # endif #endif @@ -1004,14 +1031,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) } #ifdef PNG_READ_USER_TRANSFORM_SUPPORTED - { - int i; - - for (i = 0; i<256; i++) - filters_used[i] = 0; - - png_set_read_user_transform_fn(read_ptr, count_filters); - } + png_set_read_user_transform_fn(read_ptr, read_user_callback); #endif #ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED zero_samples = 0; @@ -1029,11 +1049,11 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) */ #ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED png_set_keep_unknown_chunks(read_ptr, PNG_HANDLE_CHUNK_ALWAYS, - NULL, 0); + NULL, 0); #endif #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS, - NULL, 0); + NULL, 0); #endif #endif @@ -1054,43 +1074,43 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) int interlace_type, compression_type, filter_type; if (png_get_IHDR(read_ptr, read_info_ptr, &width, &height, &bit_depth, - &color_type, &interlace_type, &compression_type, &filter_type)) + &color_type, &interlace_type, &compression_type, &filter_type) != 0) { png_set_IHDR(write_ptr, write_info_ptr, width, height, bit_depth, - color_type, interlace_type, compression_type, filter_type); -#ifndef PNG_READ_INTERLACING_SUPPORTED - /* num_pass will not be set below, set it here if the image is - * interlaced: what happens is that write interlacing is *not* turned - * on an the partial interlaced rows are written directly. + color_type, interlace_type, compression_type, filter_type); + /* num_passes may not be available below if interlace support is not + * provided by libpng for both read and write. */ switch (interlace_type) { case PNG_INTERLACE_NONE: - num_pass = 1; + num_passes = 1; break; case PNG_INTERLACE_ADAM7: - num_pass = 7; - break; + num_passes = 7; + break; default: - png_error(read_ptr, "invalid interlace type"); - /*NOT REACHED*/ + png_error(read_ptr, "invalid interlace type"); + /*NOT REACHED*/ } -#endif } + + else + png_error(read_ptr, "png_get_IHDR failed"); } #ifdef PNG_FIXED_POINT_SUPPORTED #ifdef PNG_cHRM_SUPPORTED { png_fixed_point white_x, white_y, red_x, red_y, green_x, green_y, blue_x, - blue_y; + blue_y; if (png_get_cHRM_fixed(read_ptr, read_info_ptr, &white_x, &white_y, - &red_x, &red_y, &green_x, &green_y, &blue_x, &blue_y)) + &red_x, &red_y, &green_x, &green_y, &blue_x, &blue_y) != 0) { png_set_cHRM_fixed(write_ptr, write_info_ptr, white_x, white_y, red_x, - red_y, green_x, green_y, blue_x, blue_y); + red_y, green_x, green_y, blue_x, blue_y); } } #endif @@ -1098,7 +1118,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) { png_fixed_point gamma; - if (png_get_gAMA_fixed(read_ptr, read_info_ptr, &gamma)) + if (png_get_gAMA_fixed(read_ptr, read_info_ptr, &gamma) != 0) png_set_gAMA_fixed(write_ptr, write_info_ptr, gamma); } #endif @@ -1107,13 +1127,13 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) #ifdef PNG_cHRM_SUPPORTED { double white_x, white_y, red_x, red_y, green_x, green_y, blue_x, - blue_y; + blue_y; if (png_get_cHRM(read_ptr, read_info_ptr, &white_x, &white_y, &red_x, - &red_y, &green_x, &green_y, &blue_x, &blue_y)) + &red_y, &green_x, &green_y, &blue_x, &blue_y) != 0) { png_set_cHRM(write_ptr, write_info_ptr, white_x, white_y, red_x, - red_y, green_x, green_y, blue_x, blue_y); + red_y, green_x, green_y, blue_x, blue_y); } } #endif @@ -1121,7 +1141,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) { double gamma; - if (png_get_gAMA(read_ptr, read_info_ptr, &gamma)) + if (png_get_gAMA(read_ptr, read_info_ptr, &gamma) != 0) png_set_gAMA(write_ptr, write_info_ptr, gamma); } #endif @@ -1135,10 +1155,10 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) int compression_type; if (png_get_iCCP(read_ptr, read_info_ptr, &name, &compression_type, - &profile, &proflen)) + &profile, &proflen) != 0) { png_set_iCCP(write_ptr, write_info_ptr, name, compression_type, - profile, proflen); + profile, proflen); } } #endif @@ -1146,7 +1166,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) { int intent; - if (png_get_sRGB(read_ptr, read_info_ptr, &intent)) + if (png_get_sRGB(read_ptr, read_info_ptr, &intent) != 0) png_set_sRGB(write_ptr, write_info_ptr, intent); } #endif @@ -1154,14 +1174,14 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) png_colorp palette; int num_palette; - if (png_get_PLTE(read_ptr, read_info_ptr, &palette, &num_palette)) + if (png_get_PLTE(read_ptr, read_info_ptr, &palette, &num_palette) != 0) png_set_PLTE(write_ptr, write_info_ptr, palette, num_palette); } #ifdef PNG_bKGD_SUPPORTED { png_color_16p background; - if (png_get_bKGD(read_ptr, read_info_ptr, &background)) + if (png_get_bKGD(read_ptr, read_info_ptr, &background) != 0) { png_set_bKGD(write_ptr, write_info_ptr, background); } @@ -1171,7 +1191,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) { png_uint_16p hist; - if (png_get_hIST(read_ptr, read_info_ptr, &hist)) + if (png_get_hIST(read_ptr, read_info_ptr, &hist) != 0) png_set_hIST(write_ptr, write_info_ptr, hist); } #endif @@ -1181,7 +1201,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) int unit_type; if (png_get_oFFs(read_ptr, read_info_ptr, &offset_x, &offset_y, - &unit_type)) + &unit_type) != 0) { png_set_oFFs(write_ptr, write_info_ptr, offset_x, offset_y, unit_type); } @@ -1195,10 +1215,10 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) int type, nparams; if (png_get_pCAL(read_ptr, read_info_ptr, &purpose, &X0, &X1, &type, - &nparams, &units, ¶ms)) + &nparams, &units, ¶ms) != 0) { png_set_pCAL(write_ptr, write_info_ptr, purpose, X0, X1, type, - nparams, units, params); + nparams, units, params); } } #endif @@ -1207,7 +1227,8 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) png_uint_32 res_x, res_y; int unit_type; - if (png_get_pHYs(read_ptr, read_info_ptr, &res_x, &res_y, &unit_type)) + if (png_get_pHYs(read_ptr, read_info_ptr, &res_x, &res_y, + &unit_type) != 0) png_set_pHYs(write_ptr, write_info_ptr, res_x, res_y, unit_type); } #endif @@ -1215,7 +1236,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) { png_color_8p sig_bit; - if (png_get_sBIT(read_ptr, read_info_ptr, &sig_bit)) + if (png_get_sBIT(read_ptr, read_info_ptr, &sig_bit) != 0) png_set_sBIT(write_ptr, write_info_ptr, sig_bit); } #endif @@ -1227,7 +1248,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) double scal_width, scal_height; if (png_get_sCAL(read_ptr, read_info_ptr, &unit, &scal_width, - &scal_height)) + &scal_height) != 0) { png_set_sCAL(write_ptr, write_info_ptr, unit, scal_width, scal_height); } @@ -1239,7 +1260,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) png_charp scal_width, scal_height; if (png_get_sCAL_s(read_ptr, read_info_ptr, &unit, &scal_width, - &scal_height)) + &scal_height) != 0) { png_set_sCAL_s(write_ptr, write_info_ptr, unit, scal_width, scal_height); @@ -1248,6 +1269,19 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) #endif #endif #endif + +#ifdef PNG_sPLT_SUPPORTED + { + png_sPLT_tp entries; + + int num_entries = (int) png_get_sPLT(read_ptr, read_info_ptr, &entries); + if (num_entries) + { + png_set_sPLT(write_ptr, write_info_ptr, entries, num_entries); + } + } +#endif + #ifdef PNG_TEXT_SUPPORTED { png_textp text_ptr; @@ -1267,7 +1301,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) for (i=0; i 0) + pass_height = PNG_PASS_ROWS(height, pass); + + else + pass_height = 0; + } + + else /* not interlaced */ + pass_height = height; +# else +# define pass_height height +# endif + pngtest_debug1("Writing row data for pass %d", pass); - for (y = 0; y < height; y++) + for (y = 0; y < pass_height; y++) { #ifndef SINGLE_ROWBUF_ALLOC pngtest_debug2("Allocating row buffer (pass %d, y = %u)...", pass, y); - row_buf = (png_bytep)png_malloc(read_ptr, - png_get_rowbytes(read_ptr, read_info_ptr)); - pngtest_debug2("\t0x%08lx (%u bytes)", (unsigned long)row_buf, - png_get_rowbytes(read_ptr, read_info_ptr)); + row_buf = (png_bytep)png_malloc(read_ptr, + png_get_rowbytes(read_ptr, read_info_ptr)); + + pngtest_debug2("\t0x%08lx (%lu bytes)", (unsigned long)row_buf, + (unsigned long)png_get_rowbytes(read_ptr, read_info_ptr)); #endif /* !SINGLE_ROWBUF_ALLOC */ png_read_rows(read_ptr, (png_bytepp)&row_buf, NULL, 1); @@ -1408,7 +1476,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) t_encode += (t_stop - t_start); t_start = t_stop; #endif -#endif /* PNG_WRITE_SUPPORTED */ +#endif /* WRITE */ #ifndef SINGLE_ROWBUF_ALLOC pngtest_debug2("Freeing row buffer (pass %d, y = %u)", pass, y); @@ -1449,7 +1517,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) for (i=0; i 0) { fprintf(STDERR, "\n %s: unsupported chunks (%d)%s", - inname, unsupported_chunks, strict ? ": IGNORED --strict!" : ""); + inname, unsupported_chunks, strict ? ": IGNORED --strict!" : ""); } # endif else if (warning_count > 0) { fprintf(STDERR, "\n %s: %d libpng warnings found", - inname, warning_count); + inname, warning_count); if (strict != 0) return (1); @@ -1603,7 +1671,8 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) return (1); } -#ifdef PNG_WRITE_SUPPORTED /* else nothing was written */ +#if defined (PNG_WRITE_SUPPORTED) /* else nothing was written */ &&\ + defined (PNG_WRITE_FILTER_SUPPORTED) if (interlace_preserved != 0) /* else the files will be changed */ { for (;;) @@ -1618,18 +1687,19 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) if (num_in != num_out) { fprintf(STDERR, "\nFiles %s and %s are of a different size\n", - inname, outname); + inname, outname); if (wrote_question == 0 && unsupported_chunks == 0) { fprintf(STDERR, - " Was %s written with the same maximum IDAT chunk size (%d bytes),", - inname, PNG_ZBUF_SIZE); + " Was %s written with the same maximum IDAT" + " chunk size (%d bytes),", + inname, PNG_ZBUF_SIZE); fprintf(STDERR, - "\n filtering heuristic (libpng default), compression"); + "\n filtering heuristic (libpng default), compression"); fprintf(STDERR, - " level (zlib default),\n and zlib version (%s)?\n\n", - ZLIB_VERSION); + " level (zlib default),\n and zlib version (%s)?\n\n", + ZLIB_VERSION); wrote_question = 1; } @@ -1649,17 +1719,18 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) if (memcmp(inbuf, outbuf, num_in)) { fprintf(STDERR, "\nFiles %s and %s are different\n", inname, - outname); + outname); if (wrote_question == 0 && unsupported_chunks == 0) { fprintf(STDERR, - " Was %s written with the same maximum IDAT chunk size (%d bytes),", + " Was %s written with the same maximum" + " IDAT chunk size (%d bytes),", inname, PNG_ZBUF_SIZE); fprintf(STDERR, - "\n filtering heuristic (libpng default), compression"); + "\n filtering heuristic (libpng default), compression"); fprintf(STDERR, - " level (zlib default),\n and zlib version (%s)?\n\n", + " level (zlib default),\n and zlib version (%s)?\n\n", ZLIB_VERSION); wrote_question = 1; } @@ -1680,7 +1751,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) } } } -#endif /* PNG_WRITE_SUPPORTED */ +#endif /* WRITE && WRITE_FILTER */ FCLOSE(fpin); FCLOSE(fpout); @@ -1703,17 +1774,19 @@ main(int argc, char *argv[]) int multiple = 0; int ierror = 0; + png_structp dummy_ptr; + fprintf(STDERR, "\n Testing libpng version %s\n", PNG_LIBPNG_VER_STRING); fprintf(STDERR, " with zlib version %s\n", ZLIB_VERSION); fprintf(STDERR, "%s", png_get_copyright(NULL)); /* Show the version of libpng used in building the library */ fprintf(STDERR, " library (%lu):%s", - (unsigned long)png_access_version_number(), - png_get_header_version(NULL)); + (unsigned long)png_access_version_number(), + png_get_header_version(NULL)); /* Show the version of libpng used in building the application */ fprintf(STDERR, " pngtest (%lu):%s", (unsigned long)PNG_LIBPNG_VER, - PNG_HEADER_VERSION_STRING); + PNG_HEADER_VERSION_STRING); /* Do some consistency checking on the memory allocation settings, I'm * not sure this matters, but it is nice to know, the first of these @@ -1731,7 +1804,7 @@ main(int argc, char *argv[]) if (strcmp(png_libpng_ver, PNG_LIBPNG_VER_STRING)) { fprintf(STDERR, - "Warning: versions are different between png.h and png.c\n"); + "Warning: versions are different between png.h and png.c\n"); fprintf(STDERR, " png.h version: %s\n", PNG_LIBPNG_VER_STRING); fprintf(STDERR, " png.c version: %s\n\n", png_libpng_ver); ++ierror; @@ -1785,19 +1858,20 @@ main(int argc, char *argv[]) } } - if (!multiple && argc == 3 + verbose) - outname = argv[2 + verbose]; + if (multiple == 0 && argc == 3 + verbose) + outname = argv[2 + verbose]; - if ((!multiple && argc > 3 + verbose) || (multiple && argc < 2)) + if ((multiple == 0 && argc > 3 + verbose) || + (multiple != 0 && argc < 2)) { - fprintf(STDERR, - "usage: %s [infile.png] [outfile.png]\n\t%s -m {infile.png}\n", - argv[0], argv[0]); - fprintf(STDERR, - " reads/writes one PNG file (without -m) or multiple files (-m)\n"); - fprintf(STDERR, - " with -m %s is used as a temporary file\n", outname); - exit(1); + fprintf(STDERR, + "usage: %s [infile.png] [outfile.png]\n\t%s -m {infile.png}\n", + argv[0], argv[0]); + fprintf(STDERR, + " reads/writes one PNG file (without -m) or multiple files (-m)\n"); + fprintf(STDERR, + " with -m %s is used as a temporary file\n", outname); + exit(1); } if (multiple != 0) @@ -1810,30 +1884,24 @@ main(int argc, char *argv[]) { int kerror; fprintf(STDERR, "\n Testing %s:", argv[i]); +#if PNG_DEBUG > 0 + fprintf(STDERR, "\n"); +#endif kerror = test_one_file(argv[i], outname); if (kerror == 0) { -#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED - int k; -#endif #ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED fprintf(STDERR, "\n PASS (%lu zero samples)\n", - (unsigned long)zero_samples); + (unsigned long)zero_samples); #else fprintf(STDERR, " PASS\n"); #endif -#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED - for (k = 0; k<256; k++) - if (filters_used[k]) - fprintf(STDERR, " Filter %d was used %lu times\n", - k, (unsigned long)filters_used[k]); -#endif #ifdef PNG_TIME_RFC1123_SUPPORTED - if (tIME_chunk_present != 0) - fprintf(STDERR, " tIME = %s\n", tIME_string); + if (tIME_chunk_present != 0) + fprintf(STDERR, " tIME = %s\n", tIME_string); - tIME_chunk_present = 0; -#endif /* PNG_TIME_RFC1123_SUPPORTED */ + tIME_chunk_present = 0; +#endif /* TIME_RFC1123 */ } else @@ -1844,20 +1912,20 @@ main(int argc, char *argv[]) #if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG if (allocation_now != current_allocation) fprintf(STDERR, "MEMORY ERROR: %d bytes lost\n", - current_allocation - allocation_now); + current_allocation - allocation_now); if (current_allocation != 0) { memory_infop pinfo = pinformation; fprintf(STDERR, "MEMORY ERROR: %d bytes still allocated\n", - current_allocation); + current_allocation); while (pinfo != NULL) { - fprintf(STDERR, " %lu bytes at %x\n", - (unsigned long)pinfo->size, - (unsigned int)pinfo->pointer); + fprintf(STDERR, " %lu bytes at %p\n", + (unsigned long)pinfo->size, + pinfo->pointer); pinfo = pinfo->next; } } @@ -1865,13 +1933,13 @@ main(int argc, char *argv[]) } #if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG fprintf(STDERR, " Current memory allocation: %10d bytes\n", - current_allocation); + current_allocation); fprintf(STDERR, " Maximum memory allocation: %10d bytes\n", - maximum_allocation); + maximum_allocation); fprintf(STDERR, " Total memory allocation: %10d bytes\n", - total_allocation); + total_allocation); fprintf(STDERR, " Number of allocations: %10d\n", - num_allocations); + num_allocations); #endif } @@ -1891,7 +1959,12 @@ main(int argc, char *argv[]) status_dots_requested = 0; if (i == 0 || verbose == 1 || ierror != 0) + { fprintf(STDERR, "\n Testing %s:", inname); +#if PNG_DEBUG > 0 + fprintf(STDERR, "\n"); +#endif + } kerror = test_one_file(inname, outname); @@ -1899,32 +1972,28 @@ main(int argc, char *argv[]) { if (verbose == 1 || i == 2) { -#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED - int k; -#endif #ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED fprintf(STDERR, "\n PASS (%lu zero samples)\n", - (unsigned long)zero_samples); + (unsigned long)zero_samples); #else fprintf(STDERR, " PASS\n"); #endif -#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED - for (k = 0; k<256; k++) - if (filters_used[k]) - fprintf(STDERR, " Filter %d was used %lu times\n", - k, (unsigned long)filters_used[k]); -#endif #ifdef PNG_TIME_RFC1123_SUPPORTED if (tIME_chunk_present != 0) fprintf(STDERR, " tIME = %s\n", tIME_string); -#endif /* PNG_TIME_RFC1123_SUPPORTED */ +#endif /* TIME_RFC1123 */ } } else { if (verbose == 0 && i != 2) + { fprintf(STDERR, "\n Testing %s:", inname); +#if PNG_DEBUG > 0 + fprintf(STDERR, "\n"); +#endif + } fprintf(STDERR, " FAIL\n"); ierror += kerror; @@ -1932,19 +2001,19 @@ main(int argc, char *argv[]) #if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG if (allocation_now != current_allocation) fprintf(STDERR, "MEMORY ERROR: %d bytes lost\n", - current_allocation - allocation_now); + current_allocation - allocation_now); if (current_allocation != 0) { memory_infop pinfo = pinformation; fprintf(STDERR, "MEMORY ERROR: %d bytes still allocated\n", - current_allocation); + current_allocation); while (pinfo != NULL) { - fprintf(STDERR, " %lu bytes at %x\n", - (unsigned long)pinfo->size, (unsigned int)pinfo->pointer); + fprintf(STDERR, " %lu bytes at %p\n", + (unsigned long)pinfo->size, pinfo->pointer); pinfo = pinfo->next; } } @@ -1952,13 +2021,13 @@ main(int argc, char *argv[]) } #if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG fprintf(STDERR, " Current memory allocation: %10d bytes\n", - current_allocation); + current_allocation); fprintf(STDERR, " Maximum memory allocation: %10d bytes\n", - maximum_allocation); + maximum_allocation); fprintf(STDERR, " Total memory allocation: %10d bytes\n", - total_allocation); + total_allocation); fprintf(STDERR, " Number of allocations: %10d\n", - num_allocations); + num_allocations); #endif } @@ -1967,13 +2036,13 @@ main(int argc, char *argv[]) t_misc += (t_stop - t_start); t_start = t_stop; fprintf(STDERR, " CPU time used = %.3f seconds", - (t_misc+t_decode+t_encode)/(float)CLOCKS_PER_SEC); + (t_misc+t_decode+t_encode)/(float)CLOCKS_PER_SEC); fprintf(STDERR, " (decoding %.3f,\n", - t_decode/(float)CLOCKS_PER_SEC); + t_decode/(float)CLOCKS_PER_SEC); fprintf(STDERR, " encoding %.3f ,", - t_encode/(float)CLOCKS_PER_SEC); + t_encode/(float)CLOCKS_PER_SEC); fprintf(STDERR, " other %.3f seconds)\n\n", - t_misc/(float)CLOCKS_PER_SEC); + t_misc/(float)CLOCKS_PER_SEC); #endif if (ierror == 0) @@ -1982,6 +2051,24 @@ main(int argc, char *argv[]) else fprintf(STDERR, " libpng FAILS test\n"); + dummy_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + fprintf(STDERR, " Default limits:\n"); + fprintf(STDERR, " width_max = %lu\n", + (unsigned long) png_get_user_width_max(dummy_ptr)); + fprintf(STDERR, " height_max = %lu\n", + (unsigned long) png_get_user_height_max(dummy_ptr)); + if (png_get_chunk_cache_max(dummy_ptr) == 0) + fprintf(STDERR, " cache_max = unlimited\n"); + else + fprintf(STDERR, " cache_max = %lu\n", + (unsigned long) png_get_chunk_cache_max(dummy_ptr)); + if (png_get_chunk_malloc_max(dummy_ptr) == 0) + fprintf(STDERR, " malloc_max = unlimited\n"); + else + fprintf(STDERR, " malloc_max = %lu\n", + (unsigned long) png_get_chunk_malloc_max(dummy_ptr)); + png_destroy_read_struct(&dummy_ptr, NULL, NULL); + return (int)(ierror != 0); } #else @@ -1989,11 +2076,11 @@ int main(void) { fprintf(STDERR, - " test ignored because libpng was not built with read support\n"); + " test ignored because libpng was not built with read support\n"); /* And skip this test */ return PNG_LIBPNG_VER < 10600 ? 0 : 77; } #endif /* Generate a compiler error if there is an old png.h in the search path. */ -typedef png_libpng_version_1_7_0beta35 Your_png_h_is_not_version_1_7_0beta35; +typedef png_libpng_version_1_7_0beta90 Your_png_h_is_not_version_1_7_0beta90; diff --git a/ext/libpng17/pngtrans.c b/ext/libpng17/pngtrans.c index 22499e49c2..3b62749832 100644 --- a/ext/libpng17/pngtrans.c +++ b/ext/libpng17/pngtrans.c @@ -1,8 +1,8 @@ /* pngtrans.c - transforms the data in a row (used by both readers and writers) * - * Last changed in libpng 1.6.11 [(PENDING RELEASE)] - * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * Last changed in libpng 1.7.0 [(PENDING RELEASE)] + * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -10,88 +10,971 @@ * For conditions of distribution and use, see the disclaimer * and license in png.h */ - #include "pngpriv.h" +#define PNG_SRC_FILE PNG_SRC_FILE_pngtrans -#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#ifdef _XOPEN_SOURCE +# include +#endif /* for swab */ -#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) -/* Turn on BGR-to-RGB mapping */ -void PNGAPI -png_set_bgr(png_structrp png_ptr) +/* Memory format enquiries */ +#ifdef PNG_GAMMA_SUPPORTED +static png_fixed_point +memory_gamma(png_const_structrp png_ptr) { - png_debug(1, "in png_set_bgr"); +# ifdef PNG_READ_GAMMA_SUPPORTED +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + if (png_ptr->read_struct) + return png_ptr->row_gamma; +# endif /* TRANSFORM_MECH */ +# endif /* READ_GAMMA */ - if (png_ptr == NULL) - return; + /* Else either no READ_GAMMA support or this is a write struct; in both + * cases there are no gamma transforms. In the write case the set of the + * gamma in the info may not have been copied to the png_struct. + */ +# if defined(PNG_GAMMA_SUPPORTED) && defined(PNG_READ_SUPPORTED) + if ((png_ptr->colorspace.flags & + (PNG_COLORSPACE_INVALID|PNG_COLORSPACE_HAVE_GAMMA)) == + PNG_COLORSPACE_HAVE_GAMMA) + return png_ptr->colorspace.gamma; +# else /* !(GAMMA && READ) */ + PNG_UNUSED(png_ptr) +# endif /* !(GAMMA && READ) */ - png_ptr->transformations |= PNG_BGR; + /* '0' means the value is not know: */ + return 0; } -#endif +#endif /* GAMMA */ -#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) -/* Turn on 16 bit byte swapping */ -void PNGAPI -png_set_swap(png_structrp png_ptr) +unsigned int PNGAPI +png_memory_format(png_structrp png_ptr) { - png_debug(1, "in png_set_swap"); - - if (png_ptr == NULL) - return; - - if (png_ptr->bit_depth == 16) - png_ptr->transformations |= PNG_SWAP_BYTES; -} -#endif - -#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) -/* Turn on pixel packing */ -void PNGAPI -png_set_packing(png_structrp png_ptr) -{ - png_debug(1, "in png_set_packing"); - - if (png_ptr == NULL) - return; - - if (png_ptr->bit_depth < 8) + /* The in-memory format as a bitmask of PNG_FORMAT_FLAG_ values. All the + * flags listed below are used. If PNG_FORMAT_FLAG_INVALID is set the + * following caveats apply to the interpretation of PNG_FORMAT_FLAG_LINEAR: + * + * The gamma may differ from the sRGB (!LINEAR) or 1.0 (LINEAR). Call + * png_memory_gamma to find the correct value. + * + * The channel depth may differ from 8 (!LINEAR) or 16 (LINEAR). Call + * png_memory_channel_depth to find the correct value. + * + * It is only valid to call these APIS *after* either png_read_update_info + * or png_start_read_image on read or after the first row of an image has + * been written on write. + */ + if (png_ptr != NULL) { - png_ptr->transformations |= PNG_PACK; -# ifdef PNG_WRITE_SUPPORTED - png_ptr->usr_bit_depth = 8; +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + unsigned int format = png_ptr->row_format; +# else /* !TRANSFORM_MECH */ + unsigned int format = PNG_FORMAT_FROM_COLOR_TYPE(png_ptr->color_type); +# endif /* !TRANSFORM_MECH */ + + if (png_ptr->read_struct) /* else no way to find the gamma! */ + { +# ifdef PNG_GAMMA_SUPPORTED +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + unsigned int bit_depth = png_ptr->row_bit_depth; +# else /* !TRANSFORM_MECH */ + unsigned int bit_depth = png_ptr->bit_depth; +# endif /* !TRANSFORM_MECH */ + + /* Now work out whether this is a valid simplified API format. */ + switch (bit_depth) + { + case 8U: + { + png_fixed_point gamma = memory_gamma(png_ptr); + + if (!PNG_GAMMA_IS_sRGB(gamma)) + format |= PNG_FORMAT_FLAG_INVALID; + } + break; + + case 16: + if (memory_gamma(png_ptr) == PNG_GAMMA_LINEAR) + { + static const union + { + png_uint_16 u16; + png_byte u8[2]; + } sex = { 1U }; + + format |= PNG_FORMAT_FLAG_LINEAR; + + /* But the memory layout of the 16-bit quantities must also + * match; we need swapped data on LSB platforms. + */ + if (sex.u8[0] == ((format & PNG_FORMAT_FLAG_SWAPPED) != 0)) + break; /* ok */ + } + + /* FALL THROUGH*/ + default: /* bit depth not supported for simplified API */ + format |= PNG_FORMAT_FLAG_INVALID; + break; + } +# else /* !GAMMA */ + /* We have no way of knowing if the gamma value matches that + * expected by the simplified API so mark the format as invalid: + */ + format |= PNG_FORMAT_FLAG_INVALID; +# endif + } /* read_struct */ + + return format; + } + + return 0; +} + +unsigned int PNGAPI png_memory_channel_depth(png_structrp png_ptr) +{ + /* The actual depth of each channel in the image. */ + if (png_ptr != NULL) + { +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + return png_ptr->row_bit_depth; +# else + return png_ptr->bit_depth; # endif } -} -#endif -#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) -/* Turn on packed pixel swapping */ -void PNGAPI -png_set_packswap(png_structrp png_ptr) + return 0; +} + +#ifdef PNG_GAMMA_SUPPORTED +png_fixed_point PNGAPI +png_memory_gamma(png_structrp png_ptr) { - png_debug(1, "in png_set_packswap"); - - if (png_ptr == NULL) - return; - - if (png_ptr->bit_depth < 8) - png_ptr->transformations |= PNG_PACKSWAP; + /* The actual gamma of the image data, scaled by 100,000. This is the + * encoding gamma, e.g. 1/2.2 for sRGB. If the gamma is unknown this will + * return 0. + * + * On write this invariably returns 0; libpng does not change the gamma of + * the data on write. + * + * Note that this is not always the exact inverse of the 'screen gamma' + * passed to png_set_gamma; internal optimizations remove attempts to make + * small changes to the gamma value. This function returns the actual + * output value. + */ + return (png_ptr != NULL) ? memory_gamma(png_ptr) : 0; } -#endif +#endif /* GAMMA */ -#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) -void PNGAPI -png_set_shift(png_structrp png_ptr, png_const_color_8p true_bits) +/* These are general purpose APIs that deal with the row buffer format in both + * the read and write case. The png_struct::row_* members describe the + * in-memory format of the image data based on the transformations requested by + * the application. + */ +#ifdef PNG_TRANSFORM_MECH_SUPPORTED +png_voidp /* PRIVATE */ +png_transform_cast_check(png_const_structp png_ptr, unsigned int src_line, + png_transformp tr, size_t size) { - png_debug(1, "in png_set_shift"); + /* Given a pointer to a transform, 'tr' validate that the underlying derived + * class has size 'size' using the tr->size field and return the same + * pointer. If there is a size mismatch the function does an affirm using + * the given line number. + */ + if (tr->size != size) + png_affirm(png_ptr, param_deb("transform upcast") src_line); - if (png_ptr == NULL) - return; + return tr; +} - png_ptr->transformations |= PNG_SHIFT; - png_ptr->shift = *true_bits; +void /* PRIAVE */ +png_transform_free(png_const_structrp png_ptr, png_transformp *list) +{ + if (*list != NULL) + { + png_transform_free(png_ptr, &(*list)-> next); + if ((*list)->free != NULL) + (*list)->free(png_ptr, *list); + png_free(png_ptr, *list); + *list = NULL; + } +} + +/* Utility to initialize a png_transform_control for read or write. */ +void /* PRIVATE */ +png_init_transform_control(png_transform_controlp tc, png_structp png_ptr) +{ + png_byte bd; /* bit depth of the row */ + png_byte cd; /* bit depth of color information */ + + memset(tc, 0, sizeof *tc); + tc->png_ptr = png_ptr; /* ALIAS */ + tc->sp = tc->dp = NULL; + tc->width = 0; + +# ifdef PNG_READ_GAMMA_SUPPORTED + /* The file gamma is set by png_set_gamma, as well as being read from the + * input PNG gAMA chunk, if present, we don't have a png_info so can't get + * the set_gAMA value but this doesn't matter because on read the gamma + * value is in png_struct::colorspace and on write it isn't used. + */ + if ((png_ptr->colorspace.flags & + (PNG_COLORSPACE_INVALID|PNG_COLORSPACE_HAVE_GAMMA)) == + PNG_COLORSPACE_HAVE_GAMMA) + { + tc->gamma = png_ptr->colorspace.gamma; + debug(tc->gamma > 0); + } + + else + { + /* There is no input gamma, so there should be no overall gamma + * correction going on. This test works because the various things + * that set an output gamma also default the input gamma. + */ + debug(png_ptr->row_gamma == 0); + } +# endif + + /* Validate bit depth and color type here */ + cd = bd = png_ptr->bit_depth; + + switch (png_ptr->color_type) + { + case PNG_COLOR_TYPE_GRAY: + affirm(bd == 1U || bd == 2U || bd == 4U || bd == 8U || bd == 16U); + tc->format = 0U; + break; + + case PNG_COLOR_TYPE_PALETTE: + affirm(bd == 1U || bd == 2U || bd == 4U || bd == 8U); + tc->format = PNG_FORMAT_FLAG_COLORMAP | PNG_FORMAT_FLAG_COLOR; + cd = 8U; + break; + + case PNG_COLOR_TYPE_GRAY_ALPHA: + affirm(bd == 8U || bd == 16U); + tc->format = PNG_FORMAT_FLAG_ALPHA; + break; + + case PNG_COLOR_TYPE_RGB: + affirm(bd == 8U || bd == 16U); + tc->format = PNG_FORMAT_FLAG_COLOR; + break; + + case PNG_COLOR_TYPE_RGB_ALPHA: + affirm(bd == 8U || bd == 16U); + tc->format = PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_ALPHA; + break; + + default: + impossible("PNG color type"); + } + + tc->bit_depth = bd; + tc->range = 0; + + /* Preset the sBIT data to full precision/handled. */ + tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A = cd; +# ifdef PNG_READ_sBIT_SUPPORTED + { + int handled = 1; + + if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0) + { + png_byte c = png_ptr->sig_bit.red; + if (c > 0 && c < cd) + { + tc->sBIT_R = c; + handled = 0; + } + + c = png_ptr->sig_bit.green; + if (c > 0 && c < cd) + { + tc->sBIT_G = c; + handled = 0; + } + + c = png_ptr->sig_bit.blue; + if (c > 0 && c < cd) + { + tc->sBIT_B = c; + handled = 0; + } + } + + else /* grayscale */ + { + png_byte c = png_ptr->sig_bit.gray; + if (c > 0 && c < cd) + { + tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = c; + handled = 0; + } + } + + /* The palette-mapped format doesn't store alpha information, an + * omission in the spec that is difficult to fix. Notice that + * 'handled' is not cleared below, this is because the alpha channel is + * always linear, so the sBIT_A value can always be treated as a + * precision value. + */ + if ((png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0) + { + png_byte c = png_ptr->sig_bit.alpha; + if (c > 0 && c < cd) + tc->sBIT_A = c; + } + + /* If 'handled' did not get cleared there is no sBIT information. */ + if (handled) + tc->invalid_info = PNG_INFO_sBIT; + } +# else /* !READ_sBIT */ + /* No sBIT information */ + tc->invalid_info = PNG_INFO_sBIT; +# endif /* !READ_sBIT */ +} + +png_transformp /*PRIVATE*/ +png_add_transform(png_structrp png_ptr, size_t size, png_transform_fn fn, + unsigned int order) +{ + /* Add a transform. This is a minimal implementation; the order is just + * controlled by 'order', the result is a point to the new transform, or + * to an existing one if one was already in the list. + */ + png_transformp *p = &png_ptr->transform_list; + + while (*p != NULL && (*p)->order < order) + p = &(*p)->next; + + if (size == 0) + size = sizeof (png_transform); + + else + affirm(size >= sizeof (png_transform)); + + if (*p == NULL || (*p)->order > order) + { + png_transformp t; + + t = png_voidcast(png_transformp, png_malloc(png_ptr, size)); + memset(t, 0, size); /* zeros out the extra data too */ + /* *p comes after the new entry, t: */ + t->next = *p; + t->fn = fn; + t->free = NULL; + t->order = order; + t->size = 0xFFFFU & size; + *p = t; + return t; + } + + else /* (*p)->order matches order, return *p */ + { + affirm((*p)->fn == fn && (*p)->order == order && (*p)->size == size); + return *p; + } +} + +png_transformp /* PRIVATE */ +png_push_transform(png_structrp png_ptr, size_t size, png_transform_fn fn, + png_transformp *transform, png_transform_controlp tc) +{ + png_transformp tr = *transform; + unsigned int order = tr->order; + + /* Basic loop detection: */ + affirm(fn != NULL && tr->fn != fn); + + /* Move the following transforms up: */ + { + unsigned int old_order = order; + + do + { + tr->order = ++old_order; + tr = tr->next; + } + while (tr != NULL && tr->order == old_order); + + affirm(tr == NULL || tr->order > old_order); + } + + *transform = png_add_transform(png_ptr, size, fn, order); + + if (tc != NULL) + fn(transform, tc); + + return *transform; +} + +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +static png_transformp +png_find_transform(png_const_structrp png_ptr, unsigned int order) + /* Find a transform with the given order, or return NULL. Currently only + * used here. + */ +{ + png_transformp p = png_ptr->transform_list; + + for (;;) + { + if (p == NULL || p->order > order) + return NULL; + + if (p->order == order) + return p; + + p = p->next; + } +} +#endif /* USER_TRANSFORM_PTR */ + +static void +remove_transform(png_const_structp png_ptr, png_transformp *transform) + /* Remove a transform on a running list */ +{ + png_transformp tp = *transform; + png_transformp next = tp->next; + + *transform = next; + tp->next = NULL; + png_transform_free(png_ptr, &tp); +} + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +void /* PRIVATE */ +png_remove_transform(png_const_structp png_ptr, png_transformp *transform) +{ + remove_transform(png_ptr, transform); +} +#endif /* READ_TRANSFORMS */ + +static unsigned int +run_transform_list_forwards(png_transform_controlp tc, png_transformp *start, + png_transformp end/*NULL for whole list*/) + /* Called from the init code and below, the caller must initialize 'tc' */ +{ + png_const_structp png_ptr = tc->png_ptr; + unsigned int max_depth = PNG_TC_PIXEL_DEPTH(*tc); + + /* Caller guarantees that *start is non-NULL */ + debug(*start != NULL); + + do + { + if ((*start)->fn != NULL) + (*start)->fn(start, tc); + + if ((*start)->fn == NULL) /* delete this transform */ + remove_transform(png_ptr, start); + + else + { + /* Handle the initialization of the maximum pixel depth. */ + unsigned int tc_depth = PNG_TC_PIXEL_DEPTH(*tc); + + if (tc_depth > max_depth) + max_depth = tc_depth; + + /* Advance to the next transform. */ + start = &(*start)->next; + } + } + while (*start != NULL && *start != end); + + /* This only goes wrong if 'end' was non-NULL and not in the list: */ + debug(*start == end); + + return max_depth; +} + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +unsigned int /* PRIVATE */ +png_run_this_transform_list_forwards(png_transform_controlp tc, + png_transformp *start, png_transformp end) +{ + return run_transform_list_forwards(tc, start, end); +} +#endif /* READ_TRANSFORMS */ + +#ifdef PNG_READ_SUPPORTED +unsigned int /* PRIVATE */ +png_run_transform_list_forwards(png_structp png_ptr, png_transform_controlp tc) +{ + if (png_ptr->transform_list != NULL) + return run_transform_list_forwards(tc, &png_ptr->transform_list, NULL); + + else + return PNG_PIXEL_DEPTH(*png_ptr); +} +#endif /* READ */ + +#ifdef PNG_WRITE_SUPPORTED /* only used from pngwrite.c */ +static unsigned int +run_transform_list_backwards(png_transform_controlp tc, png_transformp *list) +{ + png_const_structp png_ptr = tc->png_ptr; + unsigned int max_depth = 0; + + if ((*list)->next != NULL) + max_depth = run_transform_list_backwards(tc, &(*list)->next); + + /* Note that the above might change (*list)->next, but it can't change + * *list itself. + */ + if ((*list)->fn != NULL) + (*list)->fn(list, tc); + + /* If that set 'fn' to NULL this transform must be removed; this is how + * (*list)->next gets changed in our caller: + */ + if ((*list)->fn == NULL) + remove_transform(png_ptr, list); + + else + { + unsigned int depth = PNG_TC_PIXEL_DEPTH(*tc); + + if (depth > max_depth) + max_depth = depth; + } + + return max_depth; +} + +void /* PRIVATE */ +png_run_transform_list_backwards(png_structp png_ptr, png_transform_controlp tc) +{ + if (png_ptr->transform_list != NULL) + { + /* This doesn't take account of the base PNG depth, but that shouldn't + * matter, it's just a check: + */ + unsigned int max_depth = + run_transform_list_backwards(tc, &png_ptr->transform_list); + + /* Better late than never (if this fires a memory overwrite has happened): + */ + affirm(max_depth <= png_ptr->row_max_pixel_depth); + } +} +#endif /* WRITE */ + +static unsigned int +init_transform_mech(png_structrp png_ptr, png_transform_control *tc, int start) + /* Called each time to run the transform list once during initialization. */ +{ + png_init_transform_control(tc, png_ptr); + tc->init = start ? PNG_TC_INIT_FORMAT : PNG_TC_INIT_FINAL; +# ifdef PNG_READ_TRANSFORMS_SUPPORTED + if (png_ptr->read_struct) + return png_read_init_transform_mech(png_ptr, tc); + else +# endif + return run_transform_list_forwards(tc, &png_ptr->transform_list, NULL); +} +#endif /* TRANSFORM_MECH */ + +#ifdef PNG_PALETTE_MAX_SUPPORTED +static int +set_palette_max(png_structrp png_ptr, png_transformp tr, unsigned int max, + unsigned int format_max) + /* Called whenever a new maximum pixel value is found */ +{ + /* One of these must be true: */ +# ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED + if (max >= (tr->args & 0x1FFU) && !png_ptr->palette_index_check_issued) + { + /* In 1.7 only issue the error/warning by default; the 'check' API is + * used to enable/disable the check. Assume that if the app enabled it + * then the app will be checking the result with get_palette_max in + * read. In write an error results unless the check is disabled. + */ + if (png_ptr->palette_index_check == PNG_PALETTE_CHECK_DEFAULT +# ifdef PNG_WRITE_SUPPORTED + || (!png_ptr->read_struct && + png_ptr->palette_index_check != PNG_PALETTE_CHECK_OFF) +# endif /* WRITE */ + ) + { +# ifdef PNG_READ_SUPPORTED +# ifdef PNG_WRITE_SUPPORTED + if (png_ptr->read_struct) +# endif /* WRITE */ + png_chunk_benign_error(png_ptr, "palette index too large"); +# ifdef PNG_WRITE_SUPPORTED + else +# endif +# endif /* READ */ +# ifdef PNG_WRITE_SUPPORTED + png_error(png_ptr, "palette index too large"); +# endif /* WRITE */ + } + + png_ptr->palette_index_check_issued = 1; + } +# endif /* CHECK_FOR_INVALID_INDEX */ +# ifdef PNG_GET_PALETTE_MAX_SUPPORTED + png_ptr->palette_index_max = png_check_byte(png_ptr, max); +# endif + + if (max == format_max) + { + tr->fn = NULL; /* no point continuing once the max has been seen */ + return 1; /* stop */ + } + + return 0; /* keep going */ +} + +static void +palette_max_1bpp(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_uint_32 width = tc->width; + + while (width >= 8) + { + if (*sp++) break; + width -= 8; + } + + if (width < 8) + { + if (width == 0 || + (*sp & (((1U<png_ptr, *tr, 1U, 1U); +} + +static void +palette_max_2bpp(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_uint_32 width = tc->width; + const png_uint_32 args = (*tr)->args; + unsigned int max = args >> 24; /* saved maximum */ + + while (width > 0) + { + png_uint_32 input = 0U, test; + unsigned int new_max; + + /* This just skips 0 bytes: */ + while (width > 0) + { + unsigned int next = *sp++; + + /* There may be partial pixels at the end, just remove the absent + * pixels with a right shift: + */ + if (width >= 4) + width -= 4; + else + next >>= (4U-width) * 2U, width = 0; + + if (next) + { + input = (input << 8) | next; + if ((input & 0xFF000000U) != 0) + break; + } + } + + test = input & 0xAAAAAAAAU; + + if (test != 0) + { + if ((input & (test >> 1)) != 0) + new_max = 3U; /* both bits set in at least one pixel */ + + else if (max < 2U) + new_max = 2U; + + else + continue; /* no change to max */ + } + + else /* test is 0 */ if (input != 0 && max == 0) + new_max = 1U; + + else /* input is 0, or max is at least 1 */ + continue; + + /* new_max is greater than max: */ + if (set_palette_max(tc->png_ptr, *tr, new_max, 3U)) + return; + + /* Record new_max: */ + max = new_max; + } + + /* End of input, check the next line. */ + (*tr)->args = (max << 24) + (args & 0xFFFFFFU); +} + +static void +palette_max_4bpp(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_uint_32 width = tc->width; + const png_uint_32 args = (*tr)->args; + unsigned int max = args >> 24; /* saved maximum */ + + while (width > 0) + { + unsigned int input = *sp++; + + if (width >= 2) + width -= 2; + else + input >>= 1, width = 0; + + if ((input & 0xFU) > max) + max = input & 0xFU; + + if (((input >> 4) & 0xFU) > max) + max = (input >> 4) & 0xFU; + } + + if (max > (args >> 24)) + { + if (set_palette_max(tc->png_ptr, *tr, max, 15U)) + return; + + (*tr)->args = (max << 24) + (args & 0xFFFFFFU); + } +} + +static void +palette_max_8bpp(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_uint_32 width = tc->width; + const png_uint_32 args = (*tr)->args; + unsigned int max = args >> 24; /* saved maximum */ + + while (width > 0) + { + unsigned int input = *sp++; + + if (input > max) + max = input; + + --width; + } + + if (max > (args >> 24)) + { + if (set_palette_max(tc->png_ptr, *tr, max, 255U)) + return; + + (*tr)->args = (max << 24) + (args & 0xFFFFFFU); + } +} + +static void +palette_max_init(png_transformp *tr, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + affirm((tc->format & PNG_FORMAT_FLAG_COLORMAP) != 0); + debug(tc->init); + + if (tc->init == PNG_TC_INIT_FINAL) + { + /* Record the palette depth to check here along with the running total + * in the top 8 bits (initially 0, which is always valid). + */ + (*tr)->args = png_ptr->num_palette; + + switch (tc->bit_depth) + { + case 1: (*tr)->fn = palette_max_1bpp; break; + case 2: (*tr)->fn = palette_max_2bpp; break; + case 4: (*tr)->fn = palette_max_4bpp; break; + case 8: (*tr)->fn = palette_max_8bpp; break; + default:impossible("palette bit depth"); + } + + png_ptr->palette_index_have_max = 1U; + } +# undef png_ptr +} +#endif /* PALETTE_MAX */ + +#ifdef PNG_GET_PALETTE_MAX_SUPPORTED +int PNGAPI +png_get_palette_max(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && png_ptr->palette_index_have_max) + return png_ptr->palette_index_max; + + /* This indicates to the caller that the information is not available: */ + return -1; + PNG_UNUSED(info_ptr) +} +#endif /* GET_PALETTE_MAX */ + +void /* PRIVATE */ +png_init_row_info(png_structrp png_ptr) +{ + /* PNG pixels never exceed 64 bits in depth: */ + const png_byte png_depth = + png_check_bits(png_ptr, PNG_PIXEL_DEPTH(*png_ptr), 7U); + +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + /* The palette index check stuff is *on* automatically. To handle this + * add it here, if it is supported. + */ +# ifdef PNG_PALETTE_MAX_SUPPORTED + /* The logic here is a little complex because of the plethora of + * #defines controlling this stuff. + */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE/* fast escape */ && ( +# if defined (PNG_READ_GET_PALETTE_MAX_SUPPORTED) ||\ + defined (PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) + (png_ptr->read_struct +# ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED + && (png_ptr->palette_index_check == PNG_PALETTE_CHECK_ON || + (png_ptr->palette_index_check == PNG_PALETTE_CHECK_DEFAULT + && png_ptr->num_palette < (1U << png_ptr->bit_depth))) +# endif /* READ_CHECK_FOR_INVALID_INDEX */ + ) +# else /* no READ support */ + 0 +# endif /* READ checks */ + || +# if defined (PNG_WRITE_GET_PALETTE_MAX_SUPPORTED) ||\ + defined (PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED) + (!png_ptr->read_struct +# ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED + && (png_ptr->palette_index_check == PNG_PALETTE_CHECK_ON || + (png_ptr->palette_index_check == PNG_PALETTE_CHECK_DEFAULT + && png_ptr->num_palette < (1U << png_ptr->bit_depth))) +# endif /* WRITE_CHECK_FOR_INVALID_INDEX */ + ) +# else /* no WRITE support */ + 0 +# endif /* WRITE checks */ + )) + png_add_transform(png_ptr, 0/*size*/, palette_max_init, + PNG_TR_CHECK_PALETTE); +# endif + + /* Application transforms may change the format of the data or, when + * producing interlaced images, the number of pixels in a line. This code + * determines the maximum pixel depth required and allows transformations + * a chance to initialize themselves. + */ + if (png_ptr->transform_list != NULL) + { + png_transform_control tc; + + (void)init_transform_mech(png_ptr, &tc, 1/*start*/); + + png_ptr->row_format = png_check_bits(png_ptr, tc.format, PNG_RF_BITS); + affirm(tc.bit_depth <= 32); + png_ptr->row_bit_depth = png_check_bits(png_ptr, tc.bit_depth, 6); + png_ptr->row_range = png_check_bits(png_ptr, tc.range, 3); +# ifdef PNG_READ_GAMMA_SUPPORTED + png_ptr->row_gamma = tc.gamma; +# endif /* READ_GAMMA */ + + /* The above may have cancelled all the transforms in the list. */ + if (png_ptr->transform_list != NULL) + { + /* Run the transform list again, also forward, and accumulate the + * maximum pixel depth. At this point the transforms can swap + * out their initialization code. + */ + unsigned int max_depth = + init_transform_mech(png_ptr, &tc, 0/*final*/); + + /* init_transform_mech is expected to take the input depth into + * account: + */ + debug(max_depth >= png_depth); + if (max_depth < png_depth) + max_depth = png_depth; + affirm(max_depth <= (png_ptr->read_struct ? 128U : 64U)); + +# ifdef PNG_READ_TRANSFORMS_SUPPORTED + /* Set this now because it only gets resolved finally at this + * point. + */ + png_ptr->invalid_info = tc.invalid_info; +# endif /* READ_TRANSFORMS */ + + /* And check the transform fields: */ + affirm(png_ptr->row_format == tc.format && + png_ptr->row_range == tc.range && + png_ptr->row_bit_depth == tc.bit_depth); +# ifdef PNG_READ_GAMMA_SUPPORTED + affirm(png_ptr->row_gamma == tc.gamma); +# endif /* READ_GAMMA */ + + png_ptr->row_max_pixel_depth = + png_check_bits(png_ptr, max_depth, 8U); + + /* On 'read' input_depth is the PNG pixel depth and output_depth is + * the depth of the pixels passed to the application, but on 'write' + * the transform list is reversed so output_depth is the PNG depth + * and input_depth the application depth. + */ + { + const png_byte app_depth = + png_check_bits(png_ptr, PNG_TC_PIXEL_DEPTH(tc), 8U); + + affirm(app_depth <= max_depth); + + if (png_ptr->read_struct) + { + png_ptr->row_input_pixel_depth = png_depth; + png_ptr->row_output_pixel_depth = app_depth; + } + + else + { + png_ptr->row_input_pixel_depth = app_depth; + png_ptr->row_output_pixel_depth = png_depth; + } + + return; /* to skip the default settings below */ + } + } + } + + else /* png_ptr->transform_list == NULL */ + { + png_ptr->row_format = png_check_bits(png_ptr, + PNG_FORMAT_FROM_COLOR_TYPE(png_ptr->color_type), PNG_RF_BITS); + png_ptr->row_bit_depth = png_check_bits(png_ptr, png_ptr->bit_depth, + 6); + png_ptr->row_range = 0; +# ifdef PNG_READ_GAMMA_SUPPORTED + if ((png_ptr->colorspace.flags & + (PNG_COLORSPACE_INVALID|PNG_COLORSPACE_HAVE_GAMMA)) == + PNG_COLORSPACE_HAVE_GAMMA) + png_ptr->row_gamma = png_ptr->colorspace.gamma; +# endif /* READ_GAMMA */ +# ifdef PNG_READ_TRANSFORMS_SUPPORTED + png_ptr->invalid_info = 0U; +# endif /* READ_TRANSFORMS */ + } +# endif /* TRANSFORM_MECH */ + + /* We get here if there are no transforms therefore no change to the pixel + * bit depths. + */ + png_ptr->row_output_pixel_depth = png_ptr->row_max_pixel_depth = + png_ptr->row_input_pixel_depth = png_depth; } -#endif #if defined(PNG_READ_INTERLACING_SUPPORTED) || \ defined(PNG_WRITE_INTERLACING_SUPPORTED) @@ -100,732 +983,2511 @@ png_set_interlace_handling(png_structrp png_ptr) { png_debug(1, "in png_set_interlace handling"); - if (png_ptr && png_ptr->interlaced) + if (png_ptr != 0) { - png_ptr->transformations |= PNG_INTERLACE; - return (7); + if (png_ptr->read_struct) + { +# ifdef PNG_READ_INTERLACING_SUPPORTED + if (png_ptr->interlaced) + { + png_ptr->do_interlace = 1; + return PNG_INTERLACE_ADAM7_PASSES; + } + + return 1; +# else /* !READ_INTERLACING */ + png_app_error(png_ptr, "no de-interlace support"); + /* return 0 below */ +# endif /* !READ_INTERLACING */ + } + + else /* write */ + { +# ifdef PNG_WRITE_INTERLACING_SUPPORTED + if (png_ptr->interlaced) + { + png_ptr->do_interlace = 1; + return PNG_INTERLACE_ADAM7_PASSES; + } + + return 1; +# else /* !WRITE_INTERLACING */ + png_app_error(png_ptr, "no interlace support"); + /* return 0 below */ +# endif /* !WRITE_INTERLACING */ + } } - return (1); + /* API CHANGE: 1.7.0: returns 0 if called with a NULL png_ptr */ + return 0; } -#endif +#endif /* READ_INTERLACING || WRITE_INTERLACING */ + +#ifdef PNG_MNG_READ_FEATURES_SUPPORTED +/* Undoes intrapixel differencing, this is called immediately after the PNG + * filter has been undone. + */ +static void +png_do_read_intrapixel_RGB8(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_uint_32 width = tc->width; + + tc->sp = dp; + + /* TAKE CARE: dp and sp may be the same, in which case the assignments to *dp + * are overwriting sp[] + */ + do + { + *dp++ = PNG_BYTE(sp[0] + sp[1]); /* red+green */ + *dp++ = *++sp; /* green */ + *dp++ = PNG_BYTE(sp[0] + sp[1]); /* green+blue */ + sp += 2; + } + while (--width > 0); + +# define png_ptr (tc->png_ptr) + UNTESTED +# undef png_ptr + PNG_UNUSED(tr) +} + +static void +png_do_read_intrapixel_RGBA8(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_uint_32 width = tc->width; + + tc->sp = dp; + + do + { + *dp++ = PNG_BYTE(sp[0] + sp[1]); /* red+green */ + *dp++ = *++sp; /* green */ + *dp++ = PNG_BYTE(sp[0] + sp[1]); /* green+blue */ + sp += 2; + *dp++ = *sp++; /* alpha */ + } + while (--width > 0); + +# define png_ptr (tc->png_ptr) + UNTESTED +# undef png_ptr + PNG_UNUSED(tr) +} + +static void +png_do_read_intrapixel_RGB16(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_uint_32 width = tc->width; + + tc->sp = dp; + + /* The input consists of 16-bit values and, by examination of the code + * (please, someone, check; I didn't read the spec) the differencing is done + * against the 16-bit green value. + */ + do + { + unsigned int red = png_get_uint_16(sp + 0); + unsigned int green = png_get_uint_16(sp + 2); + unsigned int blue = png_get_uint_16(sp + 4); + sp += 6; + + red += green; + blue += green; + + *dp++ = PNG_BYTE(red >> 8); + *dp++ = PNG_BYTE(red); + *dp++ = PNG_BYTE(green >> 8); + *dp++ = PNG_BYTE(green); + *dp++ = PNG_BYTE(blue >> 8); + *dp++ = PNG_BYTE(blue); + } + while (--width > 0); + +# define png_ptr (tc->png_ptr) + UNTESTED +# undef png_ptr + PNG_UNUSED(tr) +} + +static void +png_do_read_intrapixel_RGBA16(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_uint_32 width = tc->width; + + tc->sp = dp; + + /* As above but copy the alpha over too. */ + do + { + unsigned int red = png_get_uint_16(sp + 0); + unsigned int green = png_get_uint_16(sp + 2); + unsigned int blue = png_get_uint_16(sp + 4); + sp += 6; + + red += green; + blue += green; + + *dp++ = PNG_BYTE(red >> 8); + *dp++ = PNG_BYTE(red); + *dp++ = PNG_BYTE(green >> 8); + *dp++ = PNG_BYTE(green); + *dp++ = PNG_BYTE(blue >> 8); + *dp++ = PNG_BYTE(blue); + *dp++ = *sp++; + *dp++ = *sp++; /* alpha */ + } + while (--width > 0); + +# define png_ptr (tc->png_ptr) + UNTESTED +# undef png_ptr + PNG_UNUSED(tr) +} + +static void +png_init_read_intrapixel(png_transformp *tr, png_transform_controlp tc) +{ + /* Double check the permitted MNG features in case the app turned the feature + * on then off again. Also make sure the color type is acceptable; it must + * be RGB or RGBA. + */ + png_const_structp png_ptr = tc->png_ptr; + + if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 && + (png_ptr->filter_method == PNG_INTRAPIXEL_DIFFERENCING) && + (tc->format & (PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_COLORMAP)) == + PNG_FORMAT_FLAG_COLOR) + { + if (tc->init == PNG_TC_INIT_FINAL) switch (PNG_TC_PIXEL_DEPTH(*tc)) + { + case 24: (*tr)->fn = png_do_read_intrapixel_RGB8; break; + case 32: (*tr)->fn = png_do_read_intrapixel_RGBA8; break; + case 48: (*tr)->fn = png_do_read_intrapixel_RGB16; break; + case 64: (*tr)->fn = png_do_read_intrapixel_RGBA16; break; + default: impossible("bit depth"); + } + } + + else + (*tr)->fn = NULL; +} +#endif /* MNG_READ_FEATURES_SUPPORTED */ + +#ifdef PNG_MNG_WRITE_FEATURES_SUPPORTED +/* This is just the forward direction of the above: + * + * red := red - green + * blue:= blue- green + * + * Alpha is not changed. + */ +static void +png_do_write_intrapixel_RGB8(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_uint_32 width = tc->width; + + tc->sp = dp; + + /* TAKE CARE: dp and sp may be the same, in which case the assignments to *dp + * are overwriting sp[] + */ + do + { + *dp++ = PNG_BYTE(sp[0] - sp[1]); /* red-green */ + *dp++ = *++sp; /* green */ + *dp++ = PNG_BYTE(sp[0] - sp[1]); /* green-blue */ + sp += 2; + } + while (--width > 0); + +# define png_ptr (tc->png_ptr) + UNTESTED +# undef png_ptr + PNG_UNUSED(tr) +} + +static void +png_do_write_intrapixel_RGBA8(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_uint_32 width = tc->width; + + tc->sp = dp; + + do + { + *dp++ = PNG_BYTE(sp[0] - sp[1]); /* red-green */ + *dp++ = *++sp; /* green */ + *dp++ = PNG_BYTE(sp[0] - sp[1]); /* green-blue */ + sp += 2; + *dp++ = *sp++; /* alpha */ + } + while (--width > 0); + +# define png_ptr (tc->png_ptr) + UNTESTED +# undef png_ptr + PNG_UNUSED(tr) +} + +static void +png_do_write_intrapixel_RGB16(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_uint_32 width = tc->width; + + tc->sp = dp; + + do + { + unsigned int red = png_get_uint_16(sp + 0); + unsigned int green = png_get_uint_16(sp + 2); + unsigned int blue = png_get_uint_16(sp + 4); + sp += 6; + + red -= green; + blue -= green; + + *dp++ = PNG_BYTE(red >> 8); + *dp++ = PNG_BYTE(red); + *dp++ = PNG_BYTE(green >> 8); + *dp++ = PNG_BYTE(green); + *dp++ = PNG_BYTE(blue >> 8); + *dp++ = PNG_BYTE(blue); + } + while (--width > 0); + +# define png_ptr (tc->png_ptr) + UNTESTED +# undef png_ptr + PNG_UNUSED(tr) +} + +static void +png_do_write_intrapixel_RGBA16(png_transformp *tr, png_transform_controlp tc) +{ + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_uint_32 width = tc->width; + + tc->sp = dp; + + /* As above but copy the alpha over too. */ + do + { + unsigned int red = png_get_uint_16(sp + 0); + unsigned int green = png_get_uint_16(sp + 2); + unsigned int blue = png_get_uint_16(sp + 4); + sp += 6; + + red -= green; + blue -= green; + + *dp++ = PNG_BYTE(red >> 8); + *dp++ = PNG_BYTE(red); + *dp++ = PNG_BYTE(green >> 8); + *dp++ = PNG_BYTE(green); + *dp++ = PNG_BYTE(blue >> 8); + *dp++ = PNG_BYTE(blue); + *dp++ = *sp++; + *dp++ = *sp++; /* alpha */ + } + while (--width > 0); + +# define png_ptr (tc->png_ptr) + UNTESTED +# undef png_ptr + PNG_UNUSED(tr) +} + +static void +png_init_write_intrapixel(png_transformp *tr, png_transform_controlp tc) +{ + /* Write filter_method 64 (intrapixel differencing) only if: + * + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED, and; + * 2. Libpng did not write a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams), + * and; + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64, and; + * 4. The filter_method is 64, and; + * 5. The color_type is RGB or RGBA + */ + png_const_structp png_ptr = tc->png_ptr; + + if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 && + (png_ptr->filter_method == PNG_INTRAPIXEL_DIFFERENCING) && + (tc->format & (PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_COLORMAP)) == + PNG_FORMAT_FLAG_COLOR) + { + if (tc->init == PNG_TC_INIT_FINAL) switch (PNG_TC_PIXEL_DEPTH(*tc)) + { + case 24: (*tr)->fn = png_do_write_intrapixel_RGB8; break; + case 32: (*tr)->fn = png_do_write_intrapixel_RGBA8; break; + case 48: (*tr)->fn = png_do_write_intrapixel_RGB16; break; + case 64: (*tr)->fn = png_do_write_intrapixel_RGBA16; break; + default: impossible("bit depth"); + } + } + + else + (*tr)->fn = NULL; +} +#endif /* MNG_WRITE_FEATURES */ + +#ifdef PNG_MNG_FEATURES_SUPPORTED +png_uint_32 PNGAPI +png_permit_mng_features(png_structrp png_ptr, png_uint_32 mng_features) +{ + if (png_ptr != NULL) + { +# ifdef PNG_MNG_READ_FEATURES_SUPPORTED + if ((mng_features & PNG_FLAG_MNG_FILTER_64) != 0) + png_add_transform(png_ptr, 0/*size*/, png_init_read_intrapixel, + PNG_TR_MNG_INTRAPIXEL); +# else /* !MNG_READ_FEATURES */ + if (png_ptr->read_struct) + { + png_app_error(png_ptr, "MNG not supported on read"); + return; + } +# endif /* !MNG_READ_FEATURES */ + +# ifdef PNG_MNG_WRITE_FEATURES_SUPPORTED + if ((mng_features & PNG_FLAG_MNG_FILTER_64) != 0) + png_add_transform(png_ptr, 0/*size*/, png_init_write_intrapixel, + PNG_TR_MNG_INTRAPIXEL); +# else /* !MNG_WRITE_FEATURES */ + if (!png_ptr->read_struct) + { + png_app_error(png_ptr, "MNG not supported on write"); + return; + } +# endif /* !MNG_WRITE_FEATURES */ + + return png_ptr->mng_features_permitted = + mng_features & PNG_ALL_MNG_FEATURES; + } + + return 0; +} +#endif /* MNG_FEATURES */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) ||\ + defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) ||\ + defined(PNG_READ_SWAP_ALPHA_SUPPORTED) ||\ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) ||\ + defined(PNG_READ_FILLER_SUPPORTED) ||\ + defined(PNG_WRITE_FILLER_SUPPORTED) ||\ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) ||\ + defined(PNG_READ_STRIP_16_TO_8_SUPPORTED) ||\ + defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) ||\ + defined(PNG_READ_EXPAND_16_SUPPORTED) ||\ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +/* This is a generic transform which manipulates the bytes in an input row. The + * manipulations supported are: + * + * Channel addition (alpha or filler) + * Channel removal (alpha or filler) + * Channel swaps - RGB to BGR, alpha/filler from last to first and vice versa + * + * The output is described in blocks of output pixel size 4-bit codes encoded + * as follows: + * + * 0 Advance the source pointer by the source pixel size, start the + * code list again. This code doesn't actually exist; it is simply + * the result of emptying the code list. + * 1..3 An error (ignored; treated like 0) + * 4..7 Put filler[code-4] into the output + * 8..15 Put source byte[code-8] in the output + * + * The codes are held in a png_uint_32 parameter. transform->args is used by + * the init routine to work out the required codes. The format change is a mask + * which is XORed with the tc format. Note that the init routine works out + * whether to work from the beginning or end of the row and the codes are always + * stored LSB first in the order needed. + */ +typedef struct +{ + png_transform tr; + png_uint_32 codes; /* As above */ + unsigned int format; /* format after transform */ + unsigned int bit_depth; /* bit depth after transform */ + png_byte filler[4]; /* Filler or alpha bytes, LSB first (see below) */ +} png_transform_byte_op; + +static void +png_do_byte_ops_up(png_transformp *transform, png_transform_controlp tc) + /* Row width is unchanged or decreasing */ +{ +# define png_ptr (tc->png_ptr) + png_transform_byte_op *tr = + png_transform_cast(png_transform_byte_op, *transform); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + const unsigned int sp_advance = PNG_TC_PIXEL_DEPTH(*tc) >> 3; + const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + + debug(tc->bit_depth == 8 || tc->bit_depth == 16); + debug((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0); + + tc->sp = tc->dp; + tc->format = tr->format; + tc->bit_depth = tr->bit_depth; + + /* 'output' is a 32-byte buffer that is used to delay writes for 16 bytes, + * avoiding overwrite when source and destination buffers are the same. + * 'hwm' is either 32 or 16, initially '32', when the byte counter 'i' + * reaches 'hwm' the last-but-one 16 bytes are written; the bytes + * [hwm..hwm+15] modulo 32. hwm is then swapped to hwm+16 mod 32 and i + * continues to advance. i is always below hwm. + * + * At the end the whole remaining buffer from hwm to i is written. + */ + { + const png_uint_32 codes = tr->codes; + png_uint_32 code = codes; + unsigned int i, hwm; /* buffer index and high-water-mark */ + png_byte output[32]; + + hwm = 32; + + for (i=0;;) + { + unsigned int next_code = code & 0xf; + + if (next_code >= 8) + output[i++] = sp[next_code-8]; + + else if (next_code >= 4) + output[i++] = tr->filler[next_code - 4]; + + else /* end code */ + { + sp += sp_advance; + + if (sp >= ep) + break; /* i may be == hwm at this point. */ + + code = codes; + continue; /* no ouput produced, skip the check */ + } + + code >>= 4; /* find the next code */ + + if (i == hwm) + { + hwm &= 0x10U; /* 0 or 16 */ + memcpy(dp, output + hwm, 16); + dp += 16; + i = hwm; /* reset i if hwm was 32 */ + /* hwm is only ever 16 or 32: */ + hwm += 16; + } + } + + /* Write from hwm to (i-1), the delay means there is always something to + * write. + */ + hwm &= 0x10U; + if (hwm == 16) + { + debug(i <= 16); + memcpy(dp, output + hwm, 16); + dp += 16; + } + + if (i > 0) + memcpy(dp, output, i); + +# ifndef PNG_RELEASE_BUILD + dp += i; + /* The macro expansion exceeded the limit on ANSI strings, so split it: + */ + dp -= PNG_TC_ROWBYTES(*tc); + debug(dp == tc->dp); +# endif + } + + debug(sp == ep); +# undef png_ptr +} + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +static void +png_do_byte_ops_down(png_transformp *transform, png_transform_controlp tc) + /* Row width is increasing */ +{ +# define png_ptr (tc->png_ptr) + png_transform_byte_op *tr = + png_transform_cast(png_transform_byte_op, *transform); + const png_const_bytep ep = png_voidcast(png_const_bytep, tc->sp); + const unsigned int sp_advance = PNG_TC_PIXEL_DEPTH(*tc) >> 3; + png_const_bytep sp = ep + PNG_TC_ROWBYTES(*tc); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_alloc_size_t dest_rowbytes; + + debug(tc->bit_depth == 8U || tc->bit_depth == 16U); + debug((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0U); + + tc->sp = tc->dp; + tc->format = tr->format; + tc->bit_depth = tr->bit_depth; + dest_rowbytes = PNG_TC_ROWBYTES(*tc); + dp += dest_rowbytes; + + /* In this case the 32-byte buffer is written downwards with a writes delayed + * by 16 bytes as before. 'hwm' is lower than i; 0 or 16. + */ + { + const png_uint_32 codes = tr->codes; + png_uint_32 code = codes; + unsigned int size, hwm, i; + png_byte output[32] = { 0 }; + + /* This catches an empty codes array, which would cause all the input to + * be skipped and, potentially, a garbage output[] to be written (once) to + * *dp. + */ + affirm((codes & 0xFU) >= 4U); + + /* Align the writes to a 16-byte multiple from the start of the + * destination buffer: + */ + size = dest_rowbytes & 0xFU; + if (size == 0U) size = 16U; + i = size+16U; + sp -= sp_advance; /* Move 1 pixel back */ + hwm = 0U; + + for (;;) + { + unsigned int next_code = code & 0xFU; + + if (next_code >= 8U) + output[--i] = sp[next_code-8U]; + + else if (next_code >= 4U) + output[--i] = tr->filler[next_code - 4U]; + + else /* end code */ + { + sp -= sp_advance; + + if (sp < ep) + break; + + code = codes; + continue; /* no ouput produced, skip the check */ + } + + code >>= 4; /* find the next code */ + + if (i == hwm) + { + /* A partial copy comes at the beginning to align the copies to a + * 16-byte boundary. The bytes to be written are the bytes + * i+16..(hwm-1) except that the partial buffer may reduce this. + */ + dp -= size; + hwm ^= 0x10U; /* == i+16 mod 32 */ + memcpy(dp, output + hwm, size); + size = 16U; + if (i == 0U) i = 32U; + } + } + + /* The loop above only exits with an exit code, so 'i' has been checked + * against 'hwm' before and, because of the alignment, i will always be + * either 16 or 32: + */ + debug((i == 16U || i == 32U) & (((i & 0x10U)^0x10U) == hwm)); + debug(sp+sp_advance == ep); + + /* At the end the bytes i..(hwm-1) need to be written, with the proviso + * that 'size' will be less than 16 for short rows. If 'size' is still a + * short value then the range to be written is output[i..16+(size-1)], + * otherwise (size == 16) either this is the first write and a full 32 + * bytes will be written (hwm == 0, i == 32) or 16 bytes need to be + * written. + */ + if (size < 16U) + { + debug(i == 16U); + dp -= size; + memcpy(dp, output + i, size); + } + + else /* size == 16 */ + { + debug(size == 16U); + + /* Write i..(hwm-1); 16 or 32 bytes, however if 32 bytes are written + * they are contiguous and i==0. + * + * hwm is 0 or 16, i is 16 or 32, swap 0 and 32: + */ + if (hwm == 0U) hwm = 32U; + if (i == 32U) i = 0U; + affirm(i < hwm); + debug(hwm == i+16U || (i == 0U && hwm == 32U)); + + hwm -= i; + dp -= hwm; + memcpy(dp, output+i, hwm); + } + } + + debug(dp == png_upcast(png_bytep, tc->dp)); + +# undef png_ptr +} +#endif /* READ_TRANSFORMS */ + +/* 16 bit byte swapping */ +static void +png_do_bswap(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_byte_op *tr = + png_transform_cast(png_transform_byte_op, *transform); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + const png_alloc_size_t rowbytes = PNG_TC_ROWBYTES(*tc); + + tc->format = tr->format; + tc->bit_depth = tr->bit_depth; + tc->sp = dp; + +# ifdef _XOPEN_SOURCE + /* byte swapping often has incredibly fast implementations because of the + * importance in handling ethernet traffic. X/Open defines swab() for + * this purpose and it is widely supported and normally incredibly fast: + */ + debug((rowbytes & 1) == 0); + swab(sp, dp, rowbytes); +# else /* !_XOPEN_SOURCE */ + { + const png_const_bytep ep = sp + rowbytes - 1; + + while (sp < ep) + { + png_byte b0 = *sp++; + *dp++ = *sp++; + *dp++ = b0; + } + + debug(sp == ep+1); /* even number of bytes */ + } +# endif + + PNG_UNUSED(transform) +# undef png_ptr +} + +/* The following flags, store in tr->args, are set by the relevant PNGAPI + * png_set calls then resolved below. + */ +#define PNG_BO_STRIP_ALPHA 0x0001U /* Remove an alpha channel (read only) */ +#define PNG_BO_CHOP_16_TO_8 0x0002U /* Chop 16-bit to 8-bit channels */ +#define PNG_BO_GRAY_TO_RGB 0x0004U /* G <-> RGB; replicate channels */ +/* QUANTIZE happens here */ +#define PNG_BO_EXPAND_16 0x0008U /* Expand 8-bit channels to 16-bit */ +#define PNG_BO_BGR 0x0010U /* RGB <-> BGR */ +#define PNG_BO_FILLER 0x0020U /* Add a filler/alpha */ +#define PNG_BO_SWAP_ALPHA 0x0040U /* xA <-> Ax; alpha swap */ +#define PNG_BO_SWAP_16 0x0080U /* 16-bit channel byte swapping */ + +/* The following are additional flags to qualify the transforms: */ +#define PNG_BO_FILLER_ALPHA 0x4000U /* The filler is an alpha value */ +#define PNG_BO_FILLER_FIRST 0x8000U /* The filler comes first */ + +static void +png_init_byte_ops(png_transformp *transform, png_transform_controlp tc) +{ + /* In the absence of png_set_quantize none of the above operations apply to a + * palette row except indirectly; they may apply if the palette was expanded, + * but this happens earlier in the pipeline. + * + * In the presence of png_set_quantize the rules are considerably more + * complex. In libpng 1.6.0 the following operations occur before + * png_do_quantize: + * + * PNG_BO_GRAY_TO_RGB (png_do_gray_to_rgb, but only sometimes) + * PNG_BO_STRIP_ALPHA (png_do_strip_channel; removes alpha) + * encode_alpha + * scale_16_to_8 + * PNG_BO_CHOP_16_TO_8 (png_do_chop) + * + * The following occur afterward: + * + * PNG_BO_EXPAND_16 (png_do_expand_16) + * PNG_BO_GRAY_TO_RGB (png_do_gray_to_rgb, normally) + * PNG_BO_BGR (png_do_bgr) + * PNG_BO_FILLER (png_do_read_filler) + * PNG_BO_SWAP_ALPHA (png_do_read_swap_alpha) + * PNG_BO_SWAP_16 (png_do_swap; 16-bit byte swap) + * + * The gray to RGB operation needs to occur early for GA or gray+tRNS images + * where the pixels are being composed on a non-gray value. For the moment + * we assume that if this is necessary the following 'init' code will see RGB + * at this point. + * + * The quantize operation operates only if: + * + * 1) tc->bit_depth is 8 + * 2) The color type exactly matches that required by the parameters to + * png_set_quantize; it can be RGB, RGBA or palette, but + * png_set_quantize (not the init routine) determines this. + * + * To avoid needing to know this here the two stage initialization is used + * with two transforms, one pre-quantization the other post. In the first + * stage the correct row format and depth is set up. In the second stage the + * pre-quantization transform looks for a post-quantization transform + * immediately following and, if it exists, transfers its flags to that. + */ + png_structp png_ptr = tc->png_ptr; + png_transform_byte_op *tr = + png_transform_cast(png_transform_byte_op, *transform); + png_uint_32 args = tr->tr.args; + const unsigned int png_format = tc->format; + unsigned int format = png_format; /* memory format */ + const unsigned int png_bit_depth = tc->bit_depth; + unsigned int bit_depth = png_bit_depth; /* memory bit depth */ + + debug(tc->init); + + /* Channel swaps do not occur on COLORMAP format data at present because the + * COLORMAP is limited to 1 byte per pixel (so there is nothing to + * manipulate). Likewise for low bit depth gray, however the code below may + * widen 8-bit gray to RGB. + */ + if ((png_format & PNG_FORMAT_FLAG_COLORMAP) != 0U || png_bit_depth < 8U) + { + tr->tr.fn = NULL; + return; + } + + /* This will normally happen in TC_INIT_FORMAT, but if there is a + * png_do_quantize operation which doesn't apply (this is unlikely) it will + * happen in TC_INIT_FINAL. + */ + if (tr->tr.next != NULL && tr->tr.next->order == PNG_TR_CHANNEL_POSTQ) + { + debug(tr->tr.order == PNG_TR_CHANNEL_PREQ); + + /* So we can merge this transform into the next one, note that because the + * PNG_BO_FILLER operation is POSTQ we don't need to copy anything other + * than the flags. + */ + debug((args & tr->tr.next->args) == 0U); + tr->tr.next->args |= args; + tr->tr.fn = NULL; + return; + } + + /* Else compact the flags for this transform - this is done in both + * TC_INIT_FORMAT and TC_INIT_FINAL because it is safer that way; the copy + * above shouldn't actually affect the results but might result in TO8 and + * TO16 cancelling each other because they are in separate transforms before + * the merge above. + * + * QUIET API CHANGE: + * For compatiblity with earlier versions of libpng these tests need to + * occur in the same order as the earlier transforms; 'TO8' combined with + * 'TO16' did actually do something to 16-bit data, however now it just + * preserves the original bit depth. + */ + if ((args & PNG_BO_STRIP_ALPHA) != 0U) + { + if ((format & PNG_FORMAT_FLAG_ALPHA) != 0U) + format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA); + + else + args &= PNG_BIC_MASK(PNG_BO_STRIP_ALPHA); + } + + if ((args & PNG_BO_CHOP_16_TO_8) != 0U) + { + /* This is the quiet API CHANGE; in fact it isn't necessary, but it + * seems likely that requesting both operations is a mistake: + */ + if ((args & PNG_BO_EXPAND_16) != 0U) + args &= PNG_BIC_MASK(PNG_BO_CHOP_16_TO_8|PNG_BO_EXPAND_16); + + else if (bit_depth == 16U) + { + bit_depth = 8U; + + /* This also makes the tRNS chunk unusable: */ + tc->invalid_info |= PNG_INFO_tRNS+PNG_INFO_hIST+PNG_INFO_pCAL; + /* These need further processing: PNG_INFO_sBIT, PNG_INFO_bKGD */ + } + + else + args &= PNG_BIC_MASK(PNG_BO_CHOP_16_TO_8); + } + + /* QUANTIZE happens here */ + + if ((args & PNG_BO_EXPAND_16) != 0U) + { + /* This only does the 8 to 16-bit part of the expansion by multiply by + * 65535/255 (257) using byte replication. The cases of low bit depth + * gray being expanded to 16-bit have to be handled separately. + */ + if (bit_depth == 8U) + bit_depth = 16U; + + else + args &= PNG_BIC_MASK(PNG_BO_EXPAND_16); + } + + if ((args & PNG_BO_GRAY_TO_RGB) != 0U) + { + if ((format & PNG_FORMAT_FLAG_COLOR) == 0U) + format |= PNG_FORMAT_FLAG_COLOR; + + else + args &= PNG_BIC_MASK(PNG_BO_GRAY_TO_RGB); + } + + if ((args & PNG_BO_BGR) != 0U) + { + /* This does not happen on colormaps: */ + if ((format & PNG_FORMAT_FLAG_COLOR) != 0U && !tc->palette) + format |= PNG_FORMAT_FLAG_BGR; + + else + args &= PNG_BIC_MASK(PNG_BO_BGR); + } + + if ((args & PNG_BO_FILLER) != 0U) + { + if ((format & PNG_FORMAT_FLAG_ALPHA) == 0U) + { + format |= PNG_FORMAT_FLAG_ALPHA; + tc->channel_add = 1U; + /* And SWAP_ALPHA did not occur, because prior to 1.7.0 the filler op + * did not set ALPHA in the color type, so use SWAP_ALPHA to handle the + * before/after filler location. + * + * NOTE: this occurs twice, once in TC_START and once in TC_FINAL, but + * that is ok, the operations are idempotent. + * + * For colormaps (tc->palette set) the filler will just end up setting + * all the tRNS entries and PNG_BO_SWAP_ALPHA will be cancelled below. + */ + if ((args & PNG_BO_FILLER_FIRST) != 0U) + args |= PNG_BO_SWAP_ALPHA; + + else + args &= PNG_BIC_MASK(PNG_BO_SWAP_ALPHA); + + if (!(args & PNG_BO_FILLER_ALPHA)) /* filler is not alpha */ + format |= PNG_FORMAT_FLAG_AFILLER; + } + + else + args &= PNG_BIC_MASK(PNG_BO_FILLER); + } + + if ((args & PNG_BO_SWAP_ALPHA) != 0U) + { + /* This does not happen on color maps: */ + if ((format & PNG_FORMAT_FLAG_ALPHA) != 0U && !tc->palette) + format |= PNG_FORMAT_FLAG_AFIRST; + + else + args &= PNG_BIC_MASK(PNG_BO_SWAP_ALPHA); + } + + if ((args & PNG_BO_SWAP_16) != 0U) + { + if (bit_depth == 16U) + format |= PNG_FORMAT_FLAG_SWAPPED; + + else + args &= PNG_BIC_MASK(PNG_BO_SWAP_16); + } + + if (args != 0U) + { + /* At the end (TC_INIT_FINAL) work out the mapping array using the codes + * defined above and store the format and bit depth changes (as changes, + * so they will work either forward or backward). The filler array must + * be set up by the png_set API. + */ + if (tc->init == PNG_TC_INIT_FINAL) + { + const unsigned int png_pixel_size = PNG_TC_PIXEL_DEPTH(*tc) >> 3; + + tc->format = format; + tc->bit_depth = bit_depth; + + { + const unsigned int memory_pixel_size = PNG_TC_PIXEL_DEPTH(*tc) >> 3; + unsigned int code_size, src_size; + int go_down; + png_byte codes[8]; + + /* The codes array maps the PNG format into the memory format + * assuming the mapping works upwards in the address space. + * Initially ignore the bit depth and just work on the first four + * bytes. + */ + codes[0] = 8U; + codes[1] = 9U; + codes[2] = 10U; + codes[3] = 11U; + codes[7] = codes[6] = codes[5] = codes[4] = 0U/*error*/; + + /* PNG_BO_STRIP_ALPHA: handled by memory_pixel_size */ + /* PNG_BO_CHOP_16_TO_8: handled below */ + /* PNG_BO_EXPAND_16: handled below */ + + if ((args & PNG_BO_GRAY_TO_RGB) != 0U) + { + codes[3] = 9U; /* alpha, if present */ + codes[2] = codes[1] = 8U; + +# ifdef PNG_READ_tRNS_SUPPORTED + /* Gray to RGB, so copy the tRNS G value into r,g,b: */ + if (png_ptr->num_trans == 1U) + png_ptr->trans_color.blue = + png_ptr->trans_color.green = + png_ptr->trans_color.red = + png_ptr->trans_color.gray; +# endif /* READ_tRNS */ + } + + /* 'BGR' and gray-to-RGB are mutually exclusive; with gray-to-RGB + * codes[0] == codes[2] == 8 + */ + else if ((args & PNG_BO_BGR) != 0U) + { + codes[0] = 10U; + codes[2] = 8U; + } + + if ((args & PNG_BO_FILLER) != 0U) + { + /* The filler alway goes after; for a 'before' filler the code + * above turns on SWAP_ALPHA too. The gray-to-RGB transform has + * happened already, so the location of the filler channel is + * given by 'format': + */ + if ((format & PNG_FORMAT_FLAG_COLOR) != 0U) + codes[3] = 4U; /* low byte of filler */ + + else + codes[1] = 4U; + } + + if ((args & PNG_BO_SWAP_ALPHA) != 0U) + { + if ((format & PNG_FORMAT_FLAG_COLOR) != 0U) + { + /* BGR may have swapped the early codes. gray-to-RGB may have + * set them all to '8': + */ + png_byte acode = codes[3]; + codes[3] = codes[2]; + codes[2] = codes[1]; + codes[1] = codes[0]; + codes[0] = acode; + } + + else /* GA format */ + codes[0] = codes[1], codes[1] = 8U; + } + + /* PNG_BO_SWAP_16: 16-bit only, handled below */ + + /* Now the 16-bit dependent stuff. */ + if ((args & PNG_BO_CHOP_16_TO_8) != 0U) + { + /* 16-bit input, 8-bit output, happens before FILLER so the + * filler must be an 8-bit value. Apart from a filler code (4 in + * this case) the code must be adjusted from byte 'x' to byte + * '2x' to select the MSB of each 16-bit channel. + * + * We must use PNG_FORMAT_CHANNELS here because the memory pixel + * size might (in the future) include a TO16 operation. + */ + unsigned int i = PNG_FORMAT_CHANNELS(format); + + while (i > 0U) + { + unsigned int code = codes[--i]; + + if (code > 8U) /* 8, 4 need not change */ + codes[i] = PNG_BYTE(8U+2U*(code-8U)); + } + } + + if ((args & PNG_BO_EXPAND_16) != 0U) + { + /* Don't expect this with CHOP, but it will work, setting the low + * 8-bits of each 16-bit value to the high bits. + */ + unsigned int i = PNG_FORMAT_CHANNELS(format); + + while (i > 0U) + { + png_byte code = codes[--i]; + + /* BSWAP is after FILLER, however the data passed in is a + * machine native png_uint_16. We don't know until this init + * routine whether the data is an 8 or 16-bit value because we + * don't know the full set of transforms the app will apply + * when the png_set_filler API is called. + * + * This means that the data in tr->filler[] needs to have the + * low bits in a known place, so the code here puts the low 8 + * bits in filler[0], code 4. Hence the following: + */ + if (code == 4U) + codes[2U*i/*MSB*/] = 5U, codes[2U*i+1U/*LSB*/] = 4U; + + else + codes[2U*i] = codes[2U*i+1U] = code; + } + +# ifdef PNG_READ_tRNS_SUPPORTED + /* We're just duplicating bytes, so the tRNS chunk can be + * maintained if present. If the tRNS is for a colormap this + * produces garbage in trans_color, but it isn't used. + */ + if (png_ptr->num_trans == 1U) + { +# define TO16(x) x = PNG_UINT_16((x & 0xFFU) * 0x101U) + TO16(png_ptr->trans_color.gray); + TO16(png_ptr->trans_color.red); + TO16(png_ptr->trans_color.green); + TO16(png_ptr->trans_color.blue); +# undef TO16 + } +# endif /* READ_tRNS */ + } + + else if (bit_depth == 16U) + { + /* 16-bit input and output. */ + unsigned int i = PNG_FORMAT_CHANNELS(format); + + while (i > 0U) + { + unsigned int code = codes[--i]; + + if (code == 4U) /* as above */ + codes[2U*i/*MSB*/] = 5U, codes[2U*i+1U/*LSB*/] = 4U; + + else + { + codes[2U*i] = PNG_BYTE(8U+2U*(code-8U)); + codes[2U*i+1U] = PNG_BYTE(8U+2U*(code-8U)+1U); + } + } + } + + if ((args & PNG_BO_SWAP_16) != 0U) + { + /* bswap the memory bytes. */ + unsigned int i; + png_byte bswap_codes[sizeof codes]; + + debug((memory_pixel_size & 1U) == 0U); + + for (i=0U; iread_struct) + { + /* There are no write transforms that add data to the PNG + * file; the 'filler' transform removes a channel, but that is + * the limit of the changes. + */ + unsigned int i = 0U; + png_byte write_codes[8U]; + + memset(write_codes, 0, sizeof write_codes); + + while (i= 8U) /* 8+index of PNG byte */ + write_codes[code-8U] = PNG_BYTE(8U+i); + /* else this is a filler byte to be removed */ + else + debug(code == 4U || code == 5U); + + ++i; + } + + code_size = png_pixel_size; + src_size = memory_pixel_size; + tr->format = png_format; + tr->bit_depth = png_bit_depth; + + /* The PNG size should always be <= to the memory size, the + * source pointer will be the memory, the destination the PNG + * format, so it should always be possible to do the upwards + * copy. + */ + go_down = png_pixel_size > memory_pixel_size; + affirm(!go_down); + memcpy(codes, write_codes, sizeof codes); + } + + else +# endif /* WRITE_TRANSFORMS */ + { + code_size = memory_pixel_size; + src_size = png_pixel_size; + tr->format = format; + tr->bit_depth = bit_depth; + go_down = png_pixel_size < memory_pixel_size; + } + + /* Record this for debugging: */ + tr->tr.args = args; + + /* For the same-pixel-size case check for a bswap; this is available + * in heavily optimized forms and is a common operation (50% of the + * time) with 16-bit PNG data, particularly given the handling in + * the simplified API. + */ + if (!go_down) + { + if (memory_pixel_size == png_pixel_size) + { + int the_same = 1; + int swapped = (memory_pixel_size & 1) == 0; /* even count */ + unsigned int i; + + for (i=0U; itr.fn = png_do_bswap; + return; + } + + else if (the_same) + impossible("not reached"); + } + + tr->tr.fn = png_do_byte_ops_up; + + /* Construct the code, forwards: */ + { + unsigned int i = code_size; + png_uint_32 code = 0U; + + while (i > 0U) + { + unsigned int next = codes[--i]; + + code <<= 4U; + + if ((next >= 8U && next < 8U+src_size) || + next == 4U || next == 5U) + code += next; + + else + impossible("invalid code (up)"); + } + + tr->codes = code; + } + } + + else /* go_down */ +# ifdef PNG_READ_TRANSFORMS_SUPPORTED + { + tr->tr.fn = png_do_byte_ops_down; + + /* Construct the code, backwards: */ + { + unsigned int i = 0U; + png_uint_32 code = 0U; + + while (i < code_size) + { + unsigned int next = codes[i++]; + + code <<= 4; + + if ((next >= 8U && next < 8U+src_size) || + next == 4U || next == 5U) + code += next; + + else + impossible("invalid code (down)"); + } + + tr->codes = code; + } + } +# else /* !READ_TRANSFORMS */ + impossible("not reached"); /* because of the affirm above */ +# endif /* !READ_TRANSFORMS */ + } + } + + else /* TC_INIT_FORMAT: just store modified 'args' */ + { + tc->format = format; + tc->bit_depth = bit_depth; + tr->tr.args = args; + } + } + + else /* the transform is not applicable */ + tr->tr.fn = NULL; +} +#endif /* SWAP poo */ + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +static void +png_init_rgb_to_gray_byte_ops(png_transformp *transform, + png_transform_controlp tc) +{ + /* This just delay initializes the function; all the transform initialization + * has been done below. + */ + (*transform)->fn = png_do_byte_ops_up; + + /* If this happens on a row do the transform immediately: */ + if (!tc->init) + png_do_byte_ops_up(transform, tc); + + else + { + /* This doing the init - update the row information here */ +# define png_ptr (tc->png_ptr) + png_transform_byte_op *tr = + png_transform_cast(png_transform_byte_op, *transform); + + debug(tc->bit_depth == 8U || tc->bit_depth == 16U); + debug((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0U && + (tc->format & PNG_FORMAT_FLAG_COLOR) != 0U); + + tc->format = tr->format; + tc->bit_depth = tr->bit_depth; +# undef png_ptr + } +} + +void /* PRIVATE */ +png_add_rgb_to_gray_byte_ops(png_structrp png_ptr, png_transform_controlp tc, + unsigned int index, unsigned int order) + /* Add a byte_ops transform to convert RGB or RGBA data to 'gray' by + * selecting just the given change [index] The transform added is added at + * 'order'. + */ +{ + png_transform_byte_op *tr = png_transform_cast(png_transform_byte_op, + png_add_transform(png_ptr, sizeof (png_transform_byte_op), + png_init_rgb_to_gray_byte_ops, order)); + + affirm((tc->format & (PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_COLORMAP)) == + PNG_FORMAT_FLAG_COLOR && + index <= 2 && tc->init == PNG_TC_INIT_FINAL); + + tr->format = tc->format & PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR); + tr->bit_depth = tc->bit_depth; + + /* For 1 byte channel [index] plus, maybe, alpha: */ + if (tc->bit_depth == 8) + tr->codes = 8U + index + + ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0 ? (8U+3U) << 4 : 0U); + + else + { + affirm(tc->bit_depth == 16); + + /* As above, but two bytes; [2*index] and [2*index+1] */ + index *= 2U; + tr->codes = (8U + index) + ((9U + index) << 4) + + ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0 ? + ((8U+6U) + ((9U+6U) << 4)) << 8 : 0U); + } +} +#endif /* READ_RGB_TO_GRAY */ + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) &&\ + defined(PNG_READ_BACKGROUND_SUPPORTED) +void /* PRIVATE */ +png_push_gray_to_rgb_byte_ops(png_transformp *transform, + png_transform_controlp tc) + /* This is an init-time utility to add appropriate byte ops to expand a + * grayscale PNG data set to RGB. + */ +{ +# define png_ptr (tc->png_ptr) + png_transformp tr = png_push_transform(png_ptr, + sizeof (png_transform_byte_op), png_init_byte_ops, transform, NULL); + + tr->args = PNG_BO_GRAY_TO_RGB; + debug(tr == *transform); + png_init_byte_ops(transform, tc); +# undef png_ptr +} +#endif /* GRAY_TO_RGB && READ_BACKGROUND */ + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +void /* PRIVATE */ +png_add_strip_alpha_byte_ops(png_structrp png_ptr) +{ + png_add_transform(png_ptr, sizeof (png_transform_byte_op), png_init_byte_ops, + PNG_TR_CHANNEL_PREQ)->args |= PNG_BO_STRIP_ALPHA; +} +#endif /* READ_STRIP_ALPHA */ + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +/* Chop 16-bit depth files to 8-bit depth */ +void PNGAPI +png_set_strip_16(png_structrp png_ptr) +{ + if (png_ptr != NULL) + png_add_transform(png_ptr, sizeof (png_transform_byte_op), + png_init_byte_ops, PNG_TR_CHANNEL_PREQ)->args |= + PNG_BO_CHOP_16_TO_8; +} +#endif /* READ_STRIP_16_TO_8 */ + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +void PNGAPI +png_set_gray_to_rgb(png_structrp png_ptr) +{ + if (png_ptr != NULL) + { + png_set_expand_gray_1_2_4_to_8(png_ptr); + png_add_transform(png_ptr, sizeof (png_transform_byte_op), + png_init_byte_ops, PNG_TR_CHANNEL_PREQ)->args |= + PNG_BO_GRAY_TO_RGB; + } +} +#endif /* READ_GRAY_TO_RGB */ + +/* QUANTIZE */ + +#ifdef PNG_READ_EXPAND_16_SUPPORTED +/* Expand to 16-bit channels. PNG_BO_EXPAND_16 also expands the tRNS chunk if + * it is present, but it requires low bit depth grayscale expanded first. This + * must also force palette to RGB. + */ +void PNGAPI +png_set_expand_16(png_structrp png_ptr) +{ + if (png_ptr != NULL) + { + png_set_expand_gray_1_2_4_to_8(png_ptr); + png_set_palette_to_rgb(png_ptr); + png_add_transform(png_ptr, sizeof (png_transform_byte_op), + png_init_byte_ops, PNG_TR_CHANNEL_POSTQ)->args |= + PNG_BO_EXPAND_16; + } +} +#endif /* READ_EXPAND_16 */ + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +void PNGAPI +png_set_bgr(png_structrp png_ptr) +{ + if (png_ptr != NULL) + { +# ifndef PNG_READ_BGR_SUPPORTED + if (png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_bgr not supported on read"); + return; + } +# endif +# ifndef PNG_WRITE_BGR_SUPPORTED + if (!png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_bgr not supported on write"); + return; + } +# endif + + png_add_transform(png_ptr, sizeof (png_transform_byte_op), + png_init_byte_ops, PNG_TR_CHANNEL_POSTQ)->args |= + PNG_BO_BGR; + } +} +#endif /* BGR */ #if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* This includes png_set_filler and png_set_add_alpha. The only difference + * between the two is that the latter resulted in PNG_COLOR_MASK_ALPHA being + * added to the info_ptr color type, if png_read_update_info was called whereas + * the former did not. + * + * Regardless of whether the added channel resulted in the change to the + * png_info color type, the SWAP_ALPHA transform was not performed, even though + * it apparently occured after the add, because PNG_COLOR_MASK_ALPHA was never + * set in the 1.6 'row_info'. + * + * Consequently 'SWAP_ALPHA' and 'FILLER' were independent; one or the other + * would occur depending on the color type (not the number of channels) prior to + * the two transforms. + * + * Prior to 1.7.0 the app could obtain information about the memory format by + * calling png_read_update_info followed by png_get_color_type and + * png_get_channels. The first would return PNG_COLOR_TYPE..._ALPHA if + * png_set_add_alpha was performed and the base type if png_set_filler was + * performed, however in both cases png_get_channels would return the extra + * channel; 2 or 4. + * + * The app could also insert a user transform callback and view the color type + * in the old "row_info" structure, however this resulted in an inconsistent + * color type because png_set_alpha did not add COLOR_MASK_ALPHA to the color + * type. + * + * Hence API CHANGE: 1.7.0, row transform callbacks now see the same color type + * as reported by png_get_color_type after png_read_update_info. + */ /* Add a filler byte on read, or remove a filler or alpha byte on write. * The filler type has changed in v0.95 to allow future 2-byte fillers * for 48-bit input data, as well as to avoid problems with some compilers * that don't like bytes as parameters. */ +static void +set_filler(png_structrp png_ptr, png_uint_32 filler, int filler_loc, int alpha) +{ + if (png_ptr != NULL) + { + if (filler_loc != PNG_FILLER_BEFORE && filler_loc != PNG_FILLER_AFTER) + { + png_app_error(png_ptr, "png_set_filler: invalid filler location"); + return; + } + +# ifndef PNG_READ_SWAP_SUPPORTED + if (png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_filler not supported on read"); + return; + } +# endif +# ifndef PNG_WRITE_SWAP_SUPPORTED + if (!png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_filler not supported on write"); + return; + } +# endif + + { + png_transform_byte_op *tr = + png_transform_cast(png_transform_byte_op, + png_add_transform(png_ptr, sizeof (png_transform_byte_op), + png_init_byte_ops, PNG_TR_CHANNEL_POSTQ)); + png_uint_32 args = PNG_BO_FILLER; + + if (filler_loc == PNG_FILLER_BEFORE) + args |= PNG_BO_FILLER_FIRST; + + if (alpha) + args |= PNG_BO_FILLER_ALPHA; + + tr->tr.args |= args; + + /* The filler must be stored LSByte first: */ + tr->filler[0] = PNG_BYTE(filler >> 0); + tr->filler[1] = PNG_BYTE(filler >> 8); + tr->filler[2] = PNG_BYTE(filler >> 16); + tr->filler[3] = PNG_BYTE(filler >> 24); + } + } +} + void PNGAPI png_set_filler(png_structrp png_ptr, png_uint_32 filler, int filler_loc) { - png_debug(1, "in png_set_filler"); - - if (png_ptr == NULL) - return; - - /* In libpng 1.6 it is possible to determine whether this is a read or write - * operation and therefore to do more checking here for a valid call. - */ - if (png_ptr->mode & PNG_IS_READ_STRUCT) - { -# ifdef PNG_READ_FILLER_SUPPORTED - /* On read png_set_filler is always valid, regardless of the base PNG - * format, because other transformations can give a format where the - * filler code can execute (basically an 8 or 16-bit component RGB or G - * format.) - * - * NOTE: usr_channels is not used by the read code! (This has led to - * confusion in the past.) The filler is only used in the read code. - */ - png_ptr->filler = (png_uint_16)filler; -# else - png_app_error(png_ptr, "png_set_filler not supported on read"); - PNG_UNUSED(filler) /* not used in the write case */ - return; -# endif - } - - else /* write */ - { -# ifdef PNG_WRITE_FILLER_SUPPORTED - /* On write the usr_channels parameter must be set correctly at the - * start to record the number of channels in the app-supplied data. - */ - switch (png_ptr->color_type) - { - case PNG_COLOR_TYPE_RGB: - png_ptr->usr_channels = 4; - break; - - case PNG_COLOR_TYPE_GRAY: - if (png_ptr->bit_depth >= 8) - { - png_ptr->usr_channels = 2; - break; - } - - else - { - /* There simply isn't any code in libpng to strip out bits - * from bytes when the components are less than a byte in - * size! - */ - png_app_error(png_ptr, - "png_set_filler is invalid for low bit depth gray output"); - return; - } - - default: - png_app_error(png_ptr, - "png_set_filler: inappropriate color type"); - return; - } -# else - png_app_error(png_ptr, "png_set_filler not supported on write"); - return; -# endif - } - - /* Here on success - libpng supports the operation, set the transformation - * and the flag to say where the filler channel is. - */ - png_ptr->transformations |= PNG_FILLER; - - if (filler_loc == PNG_FILLER_AFTER) - png_ptr->flags |= PNG_FLAG_FILLER_AFTER; - - else - png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER; + set_filler(png_ptr, filler, filler_loc, 0/*!alpha*/); } /* Added to libpng-1.2.7 */ void PNGAPI png_set_add_alpha(png_structrp png_ptr, png_uint_32 filler, int filler_loc) { - png_debug(1, "in png_set_add_alpha"); - - if (png_ptr == NULL) - return; - - png_set_filler(png_ptr, filler, filler_loc); - /* The above may fail to do anything. */ - if (png_ptr->transformations & PNG_FILLER) - png_ptr->transformations |= PNG_ADD_ALPHA; + set_filler(png_ptr, filler, filler_loc, 1/*alpha*/); } - -#endif +#endif /* FILLER */ #if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) void PNGAPI png_set_swap_alpha(png_structrp png_ptr) { - png_debug(1, "in png_set_swap_alpha"); + if (png_ptr != NULL) + { +# ifndef PNG_READ_SWAP_SUPPORTED + if (png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_swap_alpha not supported on read"); + return; + } +# endif +# ifndef PNG_WRITE_SWAP_SUPPORTED + if (!png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_swap_alpha not supported on write"); + return; + } +# endif - if (png_ptr == NULL) - return; - - png_ptr->transformations |= PNG_SWAP_ALPHA; + png_add_transform(png_ptr, sizeof (png_transform_byte_op), + png_init_byte_ops, PNG_TR_CHANNEL_POSTQ)->args |= + PNG_BO_SWAP_ALPHA; + } } -#endif +#endif /* SWAP_ALPHA */ -#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +void PNGAPI +png_set_swap(png_structrp png_ptr) +{ + if (png_ptr != NULL) + { +# ifndef PNG_READ_SWAP_SUPPORTED + if (png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_swap not supported on read"); + return; + } +# endif +# ifndef PNG_WRITE_SWAP_SUPPORTED + if (!png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_swap not supported on write"); + return; + } +# endif + + png_add_transform(png_ptr, sizeof (png_transform_byte_op), + png_init_byte_ops, PNG_TR_CHANNEL_POSTQ)->args |= + PNG_BO_SWAP_16; + } +} +#endif /* SWAP */ + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) ||\ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) ||\ + defined(PNG_READ_INVERT_ALPHA_SUPPORTED) ||\ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) ||\ + defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +static png_alloc_size_t +row_align(png_transform_controlp tc) + /* Utiltity to align the source row (sp) in a transform control; it does this + * by simply copying it to dp if it is not already aligned. As a convenience + * the utility returns the number of bytes in the row. + */ +{ + png_const_structp png_ptr = tc->png_ptr; + png_const_voidp sp = tc->sp; + png_voidp dp = tc->dp; + png_alloc_size_t rowbytes = PNG_TC_ROWBYTES(*tc); + + /* For alignment; if png_alignof is not supported by the compiler this will + * always do an initial memcpy if the source and destination are not the + * same. We can only get here for write; the read case always uses locally + * allocated buffers, only write reads from the application data directly. + */ +# ifdef png_alignof + debug(png_isaligned(dp, png_uint_32)); +# endif + if (sp != dp && !png_ptr->read_struct && !png_isaligned(sp, png_uint_32)) + { + UNTESTED + memcpy(dp, sp, rowbytes); + tc->sp = dp; + } + + return rowbytes; +} +#endif /* Stuff that needs row_align */ + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) ||\ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) ||\ + defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Bit-ops; invert bytes. This works for mono inverts too because even the low + * bit depths can be handled as bytes (since there can be no intervening + * channels). + */ +#define PNG_B_INVERT_MONO 1U +#define PNG_B_INVERT_RGB 2U /* not set, used below */ +#define PNG_B_INVERT_ALPHA 4U + +typedef struct +{ + png_transform tr; + unsigned int step0; /* initial advance on sp and dp */ + unsigned int step; /* advance after start */ + png_uint_32 mask; /* XOR mask */ +} png_transform_bit_op; + +static void +png_do_invert_all(png_transformp *transform, png_transform_controlp tc) +{ + const png_const_structp png_ptr = tc->png_ptr; + /* Invert the whole row, quickly */ + const png_const_voidp dp_end = png_upcast(png_bytep, tc->dp) + row_align(tc); + png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp); + png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp); + + tc->sp = dp; + + if (png_ptr->read_struct) + { + tc->format |= PNG_FORMAT_FLAG_RANGE; + tc->range++; + } + + else if (--(tc->range) == 0) + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_RANGE); + + while (png_upcast(void*,dp) < dp_end) + *dp++ = ~*sp++; + + PNG_UNUSED(transform) +} + +static void +png_do_invert_channel(png_transformp *transform, png_transform_controlp tc) +{ + const png_const_structp png_ptr = tc->png_ptr; + /* Invert just one channel in the row. */ + const png_transform_bit_op * const tr = + png_transform_cast(png_transform_bit_op, *transform); + const png_uint_32 mask = tr->mask; + const unsigned int step = tr->step; + const unsigned int step0 = tr->step0; + const png_const_voidp dp_end = png_upcast(png_bytep, tc->dp) + row_align(tc); + png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp); + png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp); + + tc->sp = dp; + + if (png_ptr->read_struct) + { + tc->format |= PNG_FORMAT_FLAG_RANGE; + tc->range++; + } + + else if (--(tc->range) == 0) + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_RANGE); + + if (sp == dp || step == 1) + { + sp += step0; + dp += step0; + + while (png_upcast(void*,dp) < dp_end) + *dp = *sp ^ mask, dp += step, sp += step; + } + + else /* step == 2, copy required */ + { + if (step0) /* must be 1 */ + *dp++ = *sp++; + + while (png_upcast(void*,dp) < dp_end) + { + *dp++ = *sp++ ^ mask; + if (!(png_upcast(void*,dp) < dp_end)) + break; + *dp++ = *sp++; + } + } + + PNG_UNUSED(transform) +} + +static void +png_init_invert(png_transformp *transform, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_transform_bit_op *tr = + png_transform_cast(png_transform_bit_op, *transform); + png_uint_32 invert = tr->tr.args; + png_uint_32 present; /* channels present */ + + if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) != 0) + present = 0; + + else /* not color mapped */ + { + if ((tc->format & PNG_FORMAT_FLAG_COLOR) != 0) + present = PNG_B_INVERT_RGB; + else + present = PNG_B_INVERT_MONO; + + if ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0) + present |= PNG_B_INVERT_ALPHA; + } + + /* Cannot invert things that aren't there: */ + invert &= present; + + /* If nothing can be inverted is present the transform is not applicable: */ + if (invert == 0) + (*transform)->fn = NULL; + + else + { + tc->format |= PNG_FORMAT_FLAG_RANGE; + tc->range++; + + if (tc->init == PNG_TC_INIT_FINAL) + { + /* If everything that is present is to be inverted just invert the + * whole row: + */ + if (invert == present) + (*transform)->fn = png_do_invert_all; + + else + { + /* One thing is to be inverted, G or A: */ + unsigned int channels = PNG_TC_CHANNELS(*tc); + unsigned int channel = + (tc->format & PNG_FORMAT_FLAG_AFIRST) != 0 ? 0 : channels-1; + + affirm(channels == 2 || channels == 4); + + if (invert != PNG_B_INVERT_ALPHA) + { + debug(invert == PNG_B_INVERT_MONO && channels == 2 && + present == PNG_B_INVERT_MONO+PNG_B_INVERT_ALPHA); + channel = (channels-1) - channel; + } + + affirm(tc->bit_depth == 8 || tc->bit_depth == 16); + + /* So channels[channel] is to be inverted, make a mask: */ + { + union + { + png_byte bytes[8]; + png_uint_32 words[2]; + } masks; + + memset(&masks, 0, sizeof masks); + + if (tc->bit_depth == 8) + { + /* channels is 2 or 4, channel < 4. */ + masks.bytes[channel+channels] = masks.bytes[channel] = 0xff; + tr->step = 1; + tr->mask = masks.words[0]; + tr->step0 = 0; + } + + else /* tc->bit_depth == 16 */ + { + channel <<= 1; /* in bytes */ + masks.bytes[channel+1] = masks.bytes[channel] = 0xff; + + if (channels == 2) + { + tr->step = 1; + tr->mask = masks.words[0]; + tr->step0 = 0; + } + + else /* channels == 4 */ + { + tr->step = 2; + + if (masks.words[0] == 0) + { + tr->mask = masks.words[1]; + tr->step0 = 1; + } + + else + { + tr->mask = masks.words[0]; + tr->step0 = 0; + } + } + } + } + + (*transform)->fn = png_do_invert_channel; + } + } + } +# undef png_ptr +} + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) ||\ defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) void PNGAPI png_set_invert_alpha(png_structrp png_ptr) { - png_debug(1, "in png_set_invert_alpha"); - - if (png_ptr == NULL) - return; - - png_ptr->transformations |= PNG_INVERT_ALPHA; + if (png_ptr != NULL) + { + png_add_transform(png_ptr, sizeof (png_transform_bit_op), + png_init_invert, PNG_TR_INVERT)->args |= PNG_B_INVERT_ALPHA; +# ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + /* This is necessary to avoid palette processing on write; the only + * transform that applies to colormapped images is the tRNS chunk + * invert. + */ + png_ptr->write_invert_alpha = 1U; +# endif + } } -#endif +#endif /* INVERT_ALPHA */ #if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) void PNGAPI png_set_invert_mono(png_structrp png_ptr) { - png_debug(1, "in png_set_invert_mono"); - - if (png_ptr == NULL) - return; - - png_ptr->transformations |= PNG_INVERT_MONO; + if (png_ptr != NULL) + png_add_transform(png_ptr, sizeof (png_transform_bit_op), + png_init_invert, PNG_TR_INVERT)->args |= PNG_B_INVERT_MONO; } +#endif /* INVERT */ +#endif /* INVERT_ALPHA || INVERT */ -/* Invert monochrome grayscale data */ -void /* PRIVATE */ -png_do_invert(png_row_infop row_info, png_bytep row) -{ - png_debug(1, "in png_do_invert"); - - /* This test removed from libpng version 1.0.13 and 1.2.0: - * if (row_info->bit_depth == 1 && - */ - if (row_info->color_type == PNG_COLOR_TYPE_GRAY) - { - png_bytep rp = row; - png_size_t i; - png_size_t istop = row_info->rowbytes; - - for (i = 0; i < istop; i++) - { - *rp = (png_byte)(~(*rp)); - rp++; - } - } - - else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && - row_info->bit_depth == 8) - { - png_bytep rp = row; - png_size_t i; - png_size_t istop = row_info->rowbytes; - - for (i = 0; i < istop; i += 2) - { - *rp = (png_byte)(~(*rp)); - rp += 2; - } - } - -#ifdef PNG_16BIT_SUPPORTED - else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && - row_info->bit_depth == 16) - { - png_bytep rp = row; - png_size_t i; - png_size_t istop = row_info->rowbytes; - - for (i = 0; i < istop; i += 4) - { - *rp = (png_byte)(~(*rp)); - *(rp + 1) = (png_byte)(~(*(rp + 1))); - rp += 4; - } - } -#endif -} -#endif - -#ifdef PNG_16BIT_SUPPORTED -#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) -/* Swaps byte order on 16 bit depth images */ -void /* PRIVATE */ -png_do_swap(png_row_infop row_info, png_bytep row) -{ - png_debug(1, "in png_do_swap"); - - if (row_info->bit_depth == 16) - { - png_bytep rp = row; - png_uint_32 i; - png_uint_32 istop= row_info->width * row_info->channels; - - for (i = 0; i < istop; i++, rp += 2) - { - png_byte t = *rp; - *rp = *(rp + 1); - *(rp + 1) = t; - } - } -} -#endif -#endif - -#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) -static PNG_CONST png_byte onebppswaptable[256] = { - 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, - 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, - 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, - 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, - 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, - 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, - 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, - 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, - 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, - 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, - 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, - 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, - 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, - 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, - 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, - 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, - 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, - 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, - 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, - 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, - 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, - 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, - 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, - 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, - 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, - 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, - 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, - 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, - 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, - 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, - 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, - 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF -}; - -static PNG_CONST png_byte twobppswaptable[256] = { - 0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0, - 0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0, - 0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4, - 0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4, - 0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8, - 0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8, - 0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC, - 0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC, - 0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1, - 0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1, - 0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5, - 0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5, - 0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9, - 0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9, - 0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD, - 0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD, - 0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2, - 0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2, - 0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6, - 0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6, - 0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA, - 0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA, - 0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE, - 0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE, - 0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3, - 0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3, - 0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7, - 0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7, - 0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB, - 0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB, - 0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF, - 0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF -}; - -static PNG_CONST png_byte fourbppswaptable[256] = { - 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, - 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, - 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, - 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, - 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, - 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2, - 0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, - 0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3, - 0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74, - 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4, - 0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, - 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, - 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, - 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, - 0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, - 0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, - 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, - 0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8, - 0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, - 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9, - 0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, - 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA, - 0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, - 0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB, - 0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, - 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC, - 0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, - 0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD, - 0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E, - 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE, - 0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F, - 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF -}; - -/* Swaps pixel packing order within bytes */ -void /* PRIVATE */ -png_do_packswap(png_row_infop row_info, png_bytep row) -{ - png_debug(1, "in png_do_packswap"); - - if (row_info->bit_depth < 8) - { - png_bytep rp; - png_const_bytep end, table; - - end = row + row_info->rowbytes; - - if (row_info->bit_depth == 1) - table = onebppswaptable; - - else if (row_info->bit_depth == 2) - table = twobppswaptable; - - else if (row_info->bit_depth == 4) - table = fourbppswaptable; - - else - return; - - for (rp = row; rp < end; rp++) - *rp = table[*rp]; - } -} -#endif /* PNG_READ_PACKSWAP_SUPPORTED or PNG_WRITE_PACKSWAP_SUPPORTED */ - -#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ - defined(PNG_READ_STRIP_ALPHA_SUPPORTED) -/* Remove a channel - this used to be 'png_do_strip_filler' but it used a - * somewhat weird combination of flags to determine what to do. All the calls - * to png_do_strip_filler are changed in 1.5.2 to call this instead with the - * correct arguments. - * - * The routine isn't general - the channel must be the channel at the start or - * end (not in the middle) of each pixel. +/* + * WARNING + * WARNING + * WARNING + * WARNING + * WARNING The transforms below are temporary; they can and will be + * WARNING heavily optimized before release. + * WARNING + * WARNING + * WARNING */ -void /* PRIVATE */ -png_do_strip_channel(png_row_infop row_info, png_bytep row, int at_start) +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +typedef struct { - png_bytep sp = row; /* source pointer */ - png_bytep dp = row; /* destination pointer */ - png_bytep ep = row + row_info->rowbytes; /* One beyond end of row */ + png_transform tr; + png_color_8 true_bits; +} png_transform_shift; - /* At the start sp will point to the first byte to copy and dp to where - * it is copied to. ep always points just beyond the end of the row, so - * the loop simply copies (channels-1) channels until sp reaches ep. - * - * at_start: 0 -- convert AG, XG, ARGB, XRGB, AAGG, XXGG, etc. - * nonzero -- convert GA, GX, RGBA, RGBX, GGAA, RRGGBBXX, etc. - */ +/* Shift pixel values to take advantage of whole range. Pass the + * true number of bits in bit_depth. The row should be packed + * according to tc->bit_depth. Thus, if you had a row of + * bit depth 4, but the pixels only had values from 0 to 7, you + * would pass 3 as bit_depth, and this routine would translate the + * data to 0 to 15. + * + * NOTE: this is horrible complexity for no value. Once people suggested they + * were selling 16-bit displays with 5:6:5 bits spread R:G:B but so far as I + * could determine these displays produced intermediate grey (uncolored) colors, + * which is impossible with a true 5:6:5, so most likely 5:6:5 was marketing. + */ +static unsigned int +set_shifts(unsigned int format, unsigned int bit_depth, + png_const_color_8p true_bits, int *shift_start, int *shift_dec) +{ + unsigned int channels = 0; - /* GA, GX, XG cases */ - if (row_info->channels == 2) + if ((format & (PNG_FORMAT_FLAG_ALPHA+PNG_FORMAT_FLAG_AFIRST)) == + (PNG_FORMAT_FLAG_ALPHA+PNG_FORMAT_FLAG_AFIRST)) + ++channels; /* filled in below */ + + if ((format & PNG_FORMAT_FLAG_COLOR) != 0) { - if (row_info->bit_depth == 8) - { - if (at_start != 0) /* Skip initial filler */ - ++sp; - else /* Skip initial channel and, for sp, the filler */ - sp += 2, ++dp; + unsigned int offset = /* 0 or 2 as appropriate for red */ + ((format & PNG_FORMAT_FLAG_BGR) != 0) << 1; - /* For a 1 pixel wide image there is nothing to do */ - while (sp < ep) - *dp++ = *sp, sp += 2; + shift_start[channels+offset] = bit_depth - true_bits->red; + if (shift_dec != NULL) shift_dec[channels+offset] = true_bits->red; - row_info->pixel_depth = 8; - } + shift_start[channels+1] = bit_depth - true_bits->green; + if (shift_dec != NULL) shift_dec[channels+1] = true_bits->green; - else if (row_info->bit_depth == 16) - { - if (at_start != 0) /* Skip initial filler */ - sp += 2; - else /* Skip initial channel and, for sp, the filler */ - sp += 4, dp += 2; + offset ^= 2; /* for blue */ + shift_start[channels+offset] = bit_depth - true_bits->blue; + if (shift_dec != NULL) shift_dec[channels+offset] = true_bits->blue; - while (sp < ep) - *dp++ = *sp++, *dp++ = *sp, sp += 3; - - row_info->pixel_depth = 16; - } - - else - return; /* bad bit depth */ - - row_info->channels = 1; - - /* Finally fix the color type if it records an alpha channel */ - if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - row_info->color_type = PNG_COLOR_TYPE_GRAY; + channels += 3; } - /* RGBA, RGBX, XRGB cases */ - else if (row_info->channels == 4) + else /* no color: gray */ { - if (row_info->bit_depth == 8) - { - if (at_start != 0) /* Skip initial filler */ - ++sp; - else /* Skip initial channels and, for sp, the filler */ - sp += 4, dp += 3; - - /* Note that the loop adds 3 to dp and 4 to sp each time. */ - while (sp < ep) - *dp++ = *sp++, *dp++ = *sp++, *dp++ = *sp, sp += 2; - - row_info->pixel_depth = 24; - } - - else if (row_info->bit_depth == 16) - { - if (at_start != 0) /* Skip initial filler */ - sp += 2; - else /* Skip initial channels and, for sp, the filler */ - sp += 8, dp += 6; - - while (sp < ep) - { - /* Copy 6 bytes, skip 2 */ - *dp++ = *sp++, *dp++ = *sp++; - *dp++ = *sp++, *dp++ = *sp++; - *dp++ = *sp++, *dp++ = *sp, sp += 3; - } - - row_info->pixel_depth = 48; - } - - else - return; /* bad bit depth */ - - row_info->channels = 3; - - /* Finally fix the color type if it records an alpha channel */ - if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - row_info->color_type = PNG_COLOR_TYPE_RGB; + shift_start[channels] = bit_depth - true_bits->gray; + if (shift_dec != NULL) shift_dec[channels] = true_bits->gray; + ++channels; } - else - return; /* The filler channel has gone already */ + if ((format & PNG_FORMAT_FLAG_ALPHA) != 0) + { + const unsigned int offset = + (format & PNG_FORMAT_FLAG_AFIRST) != 0 ? 0 : channels++; - /* Fix the rowbytes value. */ - row_info->rowbytes = dp-row; + shift_start[offset] = bit_depth - true_bits->alpha; + if (shift_dec != NULL) shift_dec[offset] = true_bits->alpha; + } + + return channels; } -#endif -#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) -/* Swaps red and blue bytes within a pixel */ -void /* PRIVATE */ -png_do_bgr(png_row_infop row_info, png_bytep row) +#ifdef PNG_WRITE_SHIFT_SUPPORTED +static void +png_do_shift(png_transformp *transform, png_transform_controlp tc) { - png_debug(1, "in png_do_bgr"); +# define png_ptr (tc->png_ptr) + png_transform_shift *tr = + png_transform_cast(png_transform_shift, *transform); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep dp_end = dp + PNG_TC_ROWBYTES(*tc); + + png_debug(1, "in png_do_shift"); + + if (--(tc->range) == 0) + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_RANGE); + + tc->sp = dp; - if ((row_info->color_type & PNG_COLOR_MASK_COLOR)) { - png_uint_32 row_width = row_info->width; - if (row_info->bit_depth == 8) + int shift_start[4], shift_dec[4]; + unsigned int channels = set_shifts(tc->format, tc->bit_depth, + &tr->true_bits, shift_start, shift_dec); + + debug(PNG_TC_CHANNELS(*tc) == channels); + + /* With low res depths, could only be grayscale, so one channel */ + if (tc->bit_depth < 8) { - if (row_info->color_type == PNG_COLOR_TYPE_RGB) + unsigned int mask; + + UNTESTED + affirm(channels == 1); + /* This doesn't matter but we expect to run before packswap: */ + debug(!(tc->format & PNG_FORMAT_FLAG_SWAPPED)); + + if (tr->true_bits.gray == 1 && tc->bit_depth == 2) + mask = 0x55; + + else if (tc->bit_depth == 4 && tr->true_bits.gray == 3) + mask = 0x11; + + else + mask = 0xff; + + while (dp < dp_end) { - png_bytep rp; - png_uint_32 i; + int j; + unsigned int v, out; - for (i = 0, rp = row; i < row_width; i++, rp += 3) + v = *sp++; + out = 0; + + for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0]) { - png_byte save = *rp; - *rp = *(rp + 2); - *(rp + 2) = save; - } - } + if (j > 0) + out |= v << j; - else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - { - png_bytep rp; - png_uint_32 i; - - for (i = 0, rp = row; i < row_width; i++, rp += 4) - { - png_byte save = *rp; - *rp = *(rp + 2); - *(rp + 2) = save; + else + out |= (v >> (-j)) & mask; } + + *dp++ = png_check_byte(png_ptr, out); } } -#ifdef PNG_16BIT_SUPPORTED - else if (row_info->bit_depth == 16) + else if (tc->bit_depth == 8) { - if (row_info->color_type == PNG_COLOR_TYPE_RGB) + unsigned int c = 0; + + UNTESTED + while (dp < dp_end) { - png_bytep rp; - png_uint_32 i; - for (i = 0, rp = row; i < row_width; i++, rp += 6) + int j; + unsigned int v, out; + + v = *sp++; + out = 0; + + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) { - png_byte save = *rp; - *rp = *(rp + 4); - *(rp + 4) = save; - save = *(rp + 1); - *(rp + 1) = *(rp + 5); - *(rp + 5) = save; - } - } + if (j > 0) + out |= v << j; - else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - { - png_bytep rp; - png_uint_32 i; - - for (i = 0, rp = row; i < row_width; i++, rp += 8) - { - png_byte save = *rp; - *rp = *(rp + 4); - *(rp + 4) = save; - save = *(rp + 1); - *(rp + 1) = *(rp + 5); - *(rp + 5) = save; + else + out |= v >> (-j); } + + *dp++ = png_check_byte(png_ptr, out); + if (++c == channels) c = 0; + } + } + + else /* tc->bit_depth == 16 */ + { + unsigned int c = 0, s0, s1; + + UNTESTED + if ((tc->format & PNG_FORMAT_FLAG_SWAPPED) != 0) + s0 = 0, s1 = 8; /* LSB */ + + else + s0 = 8, s1 = 0; /* MSB */ + + while (dp < dp_end) + { + int j; + unsigned int value, v; + + v = *sp++ << s0; + v += *sp++ << s1; + value = 0; + + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + value |= v << j; + + else + value |= v >> (-j); + } + + *dp++ = PNG_BYTE(value >> s0); + *dp++ = PNG_BYTE(value >> s1); } } -#endif } +# undef png_ptr } -#endif /* PNG_READ_BGR_SUPPORTED or PNG_WRITE_BGR_SUPPORTED */ +#endif /* WRITE_SHIFT */ -#if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \ - defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED) -/* Added at libpng-1.5.10 */ -void /* PRIVATE */ -png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info) +#ifdef PNG_READ_SHIFT_SUPPORTED +/* Reverse the effects of png_do_shift. This routine merely shifts the + * pixels back to their significant bits values. Thus, if you have + * a row of bit depth 8, but only 5 are significant, this will shift + * the values back to 0 through 31. + */ +static void +png_do_unshift(png_transformp *transform, png_transform_controlp tc) { - if (png_ptr->num_palette < (1 << row_info->bit_depth) && - png_ptr->num_palette > 0) /* num_palette can be 0 in MNG files */ +# define png_ptr (tc->png_ptr) + png_transform_shift *tr = + png_transform_cast(png_transform_shift, *transform); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_const_bytep dp_end = dp + PNG_TC_ROWBYTES(*tc); + + png_debug(1, "in png_do_unshift"); + + tc->range++; + tc->format |= PNG_FORMAT_FLAG_RANGE; + { - /* Calculations moved outside switch in an attempt to stop different - * compiler warnings. 'padding' is in *bits* within the last byte, it is - * an 'int' because pixel_depth becomes an 'int' in the expression below, - * and this calculation is used because it avoids warnings that other - * forms produced on either GCC or MSVC. - */ - int padding = (-row_info->pixel_depth * row_info->width) & 7; - png_bytep rp = png_ptr->row_buf + row_info->rowbytes; + int shift[4]; + unsigned int channels = set_shifts(tc->format, tc->bit_depth, + &tr->true_bits, shift, NULL); + + debug(PNG_TC_CHANNELS(*tc) == channels); - switch (row_info->bit_depth) { - case 1: + unsigned int c, have_shift; + + for (c = have_shift = 0; c < channels; ++c) { - /* in this case, all bytes must be 0 so we don't need - * to unpack the pixels except for the rightmost one. + /* A shift of more than the bit depth is an error condition but it + * gets ignored here. */ - for (; rp > png_ptr->row_buf; rp--) - { - if (*rp >> padding != 0) - png_ptr->num_palette_max = 1; - padding = 0; - } + if (shift[c] <= 0 || (unsigned)/*SAFE*/shift[c] >= tc->bit_depth) + shift[c] = 0; - break; + else + have_shift = 1; } + if (have_shift == 0) + return; + } + + /* The code below will copy sp to dp, so: */ + tc->sp = dp; + + switch (tc->bit_depth) + { + default: + /* Must be 1bpp gray: should not be here! */ + impossible("unshift bit depth"); + /* NOTREACHED */ + break; + case 2: - { - for (; rp > png_ptr->row_buf; rp--) - { - int i = ((*rp >> padding) & 0x03); - - if (i > png_ptr->num_palette_max) - png_ptr->num_palette_max = i; - - i = (((*rp >> padding) >> 2) & 0x03); - - if (i > png_ptr->num_palette_max) - png_ptr->num_palette_max = i; - - i = (((*rp >> padding) >> 4) & 0x03); - - if (i > png_ptr->num_palette_max) - png_ptr->num_palette_max = i; - - i = (((*rp >> padding) >> 6) & 0x03); - - if (i > png_ptr->num_palette_max) - png_ptr->num_palette_max = i; - - padding = 0; - } + /* Must be 2bpp gray */ + debug(channels == 1 && shift[0] == 1); + debug(!(tc->format & PNG_FORMAT_FLAG_SWAPPED)); + while (dp < dp_end) + *dp++ = (*sp++ >> 1) & 0x55; break; - } case 4: - { - for (; rp > png_ptr->row_buf; rp--) + /* Must be 4bpp gray */ + debug(channels == 1); + debug(!(tc->format & PNG_FORMAT_FLAG_SWAPPED)); { - int i = ((*rp >> padding) & 0x0f); + unsigned int gray_shift = shift[0]; + unsigned int mask = 0xf >> gray_shift; /* <= 4 bits */ - if (i > png_ptr->num_palette_max) - png_ptr->num_palette_max = i; + mask |= mask << 4; /* <= 8 bits */ - i = (((*rp >> padding) >> 4) & 0x0f); - - if (i > png_ptr->num_palette_max) - png_ptr->num_palette_max = i; - - padding = 0; + while (dp < dp_end) + *dp++ = (png_byte)/*SAFE*/((*sp++ >> gray_shift) & mask); } - break; - } case 8: - { - for (; rp > png_ptr->row_buf; rp--) + /* Single byte components, G, GA, RGB, RGBA */ { - if (*rp > png_ptr->num_palette_max) - png_ptr->num_palette_max = (int) *rp; + unsigned int channel = 0; + + while (dp < dp_end) + { + *dp++ = (png_byte)/*SAFE*/(*sp++ >> shift[channel]); + if (++channel >= channels) + channel = 0; + } } - break; - } - default: + case 16: + /* Double byte components, G, GA, RGB, RGBA */ + { + unsigned int channel = 0; + unsigned int s0, s1; + + if ((tc->format & PNG_FORMAT_FLAG_SWAPPED) != 0) + s0 = 0, s1 = 8; /* LSB */ + + else + s0 = 8, s1 = 0; /* MSB */ + + while (dp < dp_end) + { + unsigned int value = *sp++ << s0; + + value += *sp++ << s1; /* <= 16 bits */ + + value >>= shift[channel]; + if (++channel >= channels) channel = 0; + *dp++ = PNG_BYTE(value >> s0); + *dp++ = PNG_BYTE(value >> s1); + } + } break; } } + +# undef png_ptr } -#endif /* PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED */ +#endif /* READ_SHIFT */ -#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) -#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED -void PNGAPI -png_set_user_transform_info(png_structrp png_ptr, png_voidp - user_transform_ptr, int user_transform_depth, int user_transform_channels) +static void +init_shift(png_transformp *transform, png_transform_controlp tc) { - png_debug(1, "in png_set_user_transform_info"); + png_const_structp png_ptr = tc->png_ptr; - if (png_ptr == NULL) - return; - -#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED - if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 && - (png_ptr->flags & PNG_FLAG_ROW_INIT) != 0) + /* These shifts apply to the component value, not the pixel index, so skip + * palette data. In addition there is no *write* shift for palette entries; + * only a read one, so skip the write/palette case too. + */ + if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0 && + (png_ptr->read_struct || !tc->palette)) { - png_app_error(png_ptr, - "info change after png_start_read_image or png_read_update_info"); - return; + /* The only change to the format is to mark the data as having a non-PNG + * range. + */ + tc->range++; + tc->format |= PNG_FORMAT_FLAG_RANGE; + + if (tc->init == PNG_TC_INIT_FINAL) + { +# ifdef PNG_READ_SHIFT_SUPPORTED + if (png_ptr->read_struct) + { + (*transform)->fn = png_do_unshift; + return; + } +# endif +# ifdef PNG_WRITE_SHIFT_SUPPORTED + if (png_ptr->read_struct) + { + (*transform)->fn = png_do_shift; + return; + } +# endif + } } -#endif - png_ptr->user_transform_ptr = user_transform_ptr; - png_ptr->user_transform_depth = (png_byte)user_transform_depth; - png_ptr->user_transform_channels = (png_byte)user_transform_channels; + else /* transform not applicable */ + (*transform)->fn = NULL; } -#endif -/* This function returns a pointer to the user_transform_ptr associated with - * the user transform functions. The application should free any memory - * associated with this pointer before png_write_destroy and png_read_destroy - * are called. - */ -#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED -png_voidp PNGAPI -png_get_user_transform_ptr(png_const_structrp png_ptr) +void PNGAPI +png_set_shift(png_structrp png_ptr, png_const_color_8p true_bits) { - if (png_ptr == NULL) - return (NULL); + if (png_ptr != NULL && true_bits != NULL) + { +# ifndef PNG_READ_SHIFT_SUPPORTED + if (png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_shift not supported on read"); + return; + } +# endif +# ifndef PNG_WRITE_SHIFT_SUPPORTED + if (!png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_shift not supported on write"); + return; + } +# endif - return png_ptr->user_transform_ptr; + { + png_transform_shift *trs = png_transform_cast(png_transform_shift, + png_add_transform(png_ptr, sizeof (png_transform_shift), + init_shift, PNG_TR_SHIFT)); + trs->true_bits = *true_bits; + } + } } -#endif +#endif /* SHIFT */ +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Turn on pixel packing */ +void PNGAPI +png_set_packing(png_structrp png_ptr) +{ + /* The transforms aren't symmetric, so even though there is one API there are + * two internal init functions, one for read, the other write: + */ + if (png_ptr != NULL) + { + if (png_ptr->read_struct) + { +# ifdef PNG_READ_PACK_SUPPORTED + png_add_transform(png_ptr, 0/*size*/, png_init_read_pack, + PNG_TR_PACK); +# else + png_app_error(png_ptr, "png_set_packing not supported on read"); +# endif + } + + else + { +# ifdef PNG_WRITE_PACK_SUPPORTED + png_add_transform(png_ptr, 0/*size*/, png_init_write_pack, + PNG_TR_PACK); +# else + png_app_error(png_ptr, "png_set_packing not supported on write"); +# endif + } + } +} +#endif /* PACK */ + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) ||\ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Turn on pixel-swapping within a byte, this is symmetric - doing the swap + * twice produces the original value, so only one implementation is required for + * either read or write. + * + * Used to be refered to as "packswap", but pixel-swap seems more + * self-documenting. + */ +static void +png_do_swap_1bit(png_transformp *transform, png_transform_controlp tc) +{ + png_alloc_size_t rowbytes = row_align(tc); /* may change tc->sp */ + png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp); + png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp); + + tc->sp = dp; + tc->format ^= PNG_FORMAT_FLAG_SWAPPED; + + for (;;) + { + png_uint_32 s = *sp++; + s = ((s >> 1) & 0x55555555) | ((s & 0x55555555) << 1); + s = ((s >> 2) & 0x33333333) | ((s & 0x33333333) << 2); + s = ((s >> 4) & 0x0f0f0f0f) | ((s & 0x0f0f0f0f) << 4); + *dp++ = s; + if (rowbytes <= 4) break; + rowbytes -= 4; + } + + PNG_UNUSED(transform) +} + +static void +png_do_swap_2bit(png_transformp *transform, png_transform_controlp tc) +{ + png_alloc_size_t rowbytes = row_align(tc); /* may change tc->sp */ + png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp); + png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp); + + tc->sp = dp; + tc->format ^= PNG_FORMAT_FLAG_SWAPPED; + + for (;;) + { + png_uint_32 s = *sp++; + s = ((s >> 2) & 0x33333333) | ((s & 0x33333333) << 2); + s = ((s >> 4) & 0x0f0f0f0f) | ((s & 0x0f0f0f0f) << 4); + *dp++ = s; + if (rowbytes <= 4) break; + rowbytes -= 4; + } + + PNG_UNUSED(transform) +} + +static void +png_do_swap_4bit(png_transformp *transform, png_transform_controlp tc) +{ + png_alloc_size_t rowbytes = row_align(tc); /* may change tc->sp */ + png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp); + png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp); + + tc->sp = dp; + tc->format ^= PNG_FORMAT_FLAG_SWAPPED; + + for (;;) + { + png_uint_32 s = *sp++; + s = ((s >> 4) & 0x0f0f0f0f) | ((s & 0x0f0f0f0f) << 4); + *dp++ = s; + if (rowbytes <= 4) break; + rowbytes -= 4; + } + + PNG_UNUSED(transform) +} + +static void +init_packswap(png_transformp *transform, png_transform_controlp tc) +{ + png_transform_fn fn; + +# define png_ptr tc->png_ptr + debug(tc->init); +# undef png_ptr + + switch (tc->bit_depth) + { + case 1: fn = png_do_swap_1bit; break; + case 2: fn = png_do_swap_2bit; break; + case 4: fn = png_do_swap_4bit; break; + + default: /* transform not applicable */ + (*transform)->fn = NULL; + return; + } + + tc->format ^= PNG_FORMAT_FLAG_SWAPPED; + if (tc->init == PNG_TC_INIT_FINAL) + (*transform)->fn = fn; +} + +void PNGAPI +png_set_packswap(png_structrp png_ptr) +{ + if (png_ptr != NULL) + { +# ifndef PNG_READ_PACKSWAP_SUPPORTED + if (png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_packswap not supported on read"); + return; + } +# endif +# ifndef PNG_WRITE_PACKSWAP_SUPPORTED + if (!png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_packswap not supported on write"); + return; + } +# endif + + png_add_transform(png_ptr, 0/*size*/, init_packswap, PNG_TR_PIXEL_SWAP); + } +} +#endif /* PACKSWAP */ + +/* User transform handling */ #ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED png_uint_32 PNGAPI png_get_current_row_number(png_const_structrp png_ptr) { - /* See the comments in png.h - this is the sub-image row when reading and + /* See the comments in png.h - this is the sub-image row when reading an * interlaced image. */ if (png_ptr != NULL) - return png_ptr->row_number; + { + /* In the read case png_struct::row_number is the row in the final image, + * not the pass, this will return the previous row number if the row isn't + * in the pass: + */ + if (png_ptr->read_struct) + return PNG_PASS_ROWS(png_ptr->row_number+1, png_ptr->pass)-1U; + + else + return png_ptr->row_number; + } return PNG_UINT_32_MAX; /* help the app not to fail silently */ } @@ -837,7 +3499,262 @@ png_get_current_pass_number(png_const_structrp png_ptr) return png_ptr->pass; return 8; /* invalid */ } -#endif /* PNG_USER_TRANSFORM_INFO_SUPPORTED */ -#endif /* PNG_READ_USER_TRANSFORM_SUPPORTED || - PNG_WRITE_USER_TRANSFORM_SUPPORTED */ -#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ +#endif /* USER_TRANSFORM_INFO */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) ||\ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +typedef struct +{ + png_transform tr; + png_user_transform_ptr user_fn; +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED + png_voidp user_ptr; +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + unsigned int user_depth; + unsigned int user_channels; +#endif +#endif +} png_user_transform, *png_user_transformp; + +typedef const png_user_transform *png_const_user_transformp; + +static png_user_transformp +get_user_transform(png_structrp png_ptr) +{ + /* Note that in an added transform the whole transform is memset to 0, so we + * don't need to initialize anything. + */ + return png_transform_cast(png_user_transform, png_add_transform(png_ptr, + sizeof (png_user_transform), NULL/*function*/, PNG_TR_USER)); +} +#endif /* READ_USER_TRANSFORM || WRITE_USER_TRANSFORM */ + +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +png_voidp PNGAPI +png_get_user_transform_ptr(png_const_structrp png_ptr) +{ + if (png_ptr != NULL) + { + png_transformp tr = png_find_transform(png_ptr, PNG_TR_USER); + + if (tr != NULL) + { + png_user_transformp tru = png_transform_cast(png_user_transform, tr); + return tru->user_ptr; + } + } + + return NULL; +} +#endif /* USER_TRANSFORM_PTR */ + +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +void PNGAPI +png_set_user_transform_info(png_structrp png_ptr, png_voidp ptr, int depth, + int channels) +{ + if (png_ptr != NULL) + { + /* NOTE: this function only sets the user transform pointer on write, i.e. + * the depth and channels arguments are ignored. + */ + png_user_transformp tr = get_user_transform(png_ptr); + + tr->user_ptr = ptr; + +# ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + if (png_ptr->read_struct) + { + if (png_ptr->row_bit_depth == 0) + { + if (depth > 0 && depth <= 32 && channels > 0 && channels <= 4 && + (-depth & depth) == depth /* power of 2 */) + { + tr->user_depth = png_check_bits(png_ptr, depth, 6); + tr->user_channels = png_check_bits(png_ptr, channels, 3); + } + + else + png_app_error(png_ptr, "unsupported bit-depth or channels"); + } + else + png_app_error(png_ptr, + "cannot change user info after image start"); + } +# else /* !READ_USER_TRANSFORM */ + PNG_UNUSED(depth) + PNG_UNUSED(channels) +# endif /* !READ_USER_TRANSFORM */ + } +} +#endif /* USER_TRANSFORM_PTR */ + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +static void +png_do_read_user_transform(png_transformp *trIn, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + png_user_transformp tr = png_transform_cast(png_user_transform, *trIn); + + if (!tc->init && tr->user_fn != NULL) + { + png_row_info row_info; + + row_info.width = tc->width; + row_info.rowbytes = PNG_TC_ROWBYTES(*tc); + row_info.color_type = png_check_byte(png_ptr, + PNG_COLOR_TYPE_FROM_FORMAT(tc->format)); + row_info.bit_depth = png_check_byte(png_ptr, tc->bit_depth); + row_info.channels = png_check_byte(png_ptr, + PNG_FORMAT_CHANNELS(tc->format)); + row_info.bit_depth = png_check_byte(png_ptr, + PNG_TC_PIXEL_DEPTH(*tc)); + + /* TODO: fix this API, but for the moment we have to copy the row data to + * the working buffer. This is an unnecessary perf overhead when a user + * transform is used to read information without a transform or when it is + * used on write. + */ + if (tc->sp != tc->dp) + { + memcpy(tc->dp, tc->sp, PNG_TC_ROWBYTES(*tc)); + tc->sp = tc->dp; + } + + tr->user_fn(png_ptr, &row_info, png_voidcast(png_bytep, tc->dp)); + } + +# ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED + if (tr->user_depth > 0) + { + /* The read transform can modify the bit depth and number of + * channels; the interface doesn't permit anything else to be + * changed. If the information isn't set the user callback has to + * produce pixels with the correct pixel depth (otherwise the + * de-interlace won't work) but there really is no other constraint. + */ + tc->bit_depth = tr->user_depth; + + /* The API is very restricted in functionality; the user_channels + * can be changed, but the color_type can't, so the format is simply + * fixed up to match the channels. + */ + if (tr->user_channels != PNG_FORMAT_CHANNELS(tc->format)) + switch (tr->user_channels) + { + case 1: + tc->format &= + PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA); + break; + + case 2: /* has to be GA */ + tc->format &= + PNG_BIC_MASK(PNG_FORMAT_FLAG_COLORMAP+PNG_FORMAT_FLAG_COLOR); + tc->format |= PNG_FORMAT_FLAG_ALPHA; + break; + + case 3: /* has to be RGB */ + tc->format &= + PNG_BIC_MASK(PNG_FORMAT_FLAG_COLORMAP|PNG_FORMAT_FLAG_ALPHA); + tc->format |= PNG_FORMAT_FLAG_COLOR; + break; + + case 4: /* has to be RGBA */ + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLORMAP); + tc->format |= (PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA); + break; + + default: /* checked before */ + impossible("user channels"); + } + + debug(PNG_FORMAT_CHANNELS(tc->format) == tr->user_channels); + } +# endif /* USER_TRANSFORM_PTR */ +# undef png_ptr +} + +void PNGAPI +png_set_read_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr + read_user_transform_fn) +{ + /* There is no 'init' function, just the callback above to handle the + * transform. + */ + if (png_ptr != NULL) + { + if (png_ptr->read_struct) + { + png_user_transformp tr = get_user_transform(png_ptr); + + tr->user_fn = read_user_transform_fn; + tr->tr.fn = png_do_read_user_transform; + } + + else + png_app_error(png_ptr, "cannot set a read transform on write"); + } +} +#endif /* READ_USER_TRANSFORM */ + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +static void +png_do_write_user_transform(png_transformp *trIn, png_transform_controlp tc) +{ +# define png_ptr (tc->png_ptr) + /* The write side is pretty simple; call the call-back, it will make the row + * data right. + * + * BUG: we need to copy the input row (it would be a non-quiet API change + * otherwise) and we don't know how big it is because the information passed + * to png_set_user_transform_info never was used on write, but that's fine + * because the write code never did get this right, so presumably all the + * apps have worked round it... + */ + if (!tc->init) + { + png_user_transformp tr = png_transform_cast(png_user_transform, *trIn); + png_row_info row_info; + + if (tc->sp != tc->dp) /* no interlace */ + { + memcpy(tc->dp, tc->sp, PNG_TC_ROWBYTES(*tc)); + tc->sp = tc->dp; + } + + row_info.width = tc->width; + row_info.rowbytes = PNG_TC_ROWBYTES(*tc); + row_info.color_type = png_check_byte(png_ptr, + PNG_COLOR_TYPE_FROM_FORMAT(tc->format)); + row_info.bit_depth = png_check_byte(png_ptr, tc->bit_depth); + row_info.channels = png_check_byte(png_ptr, + PNG_FORMAT_CHANNELS(tc->format)); + row_info.bit_depth = png_check_byte(png_ptr, + PNG_TC_PIXEL_DEPTH(*tc)); + + /* The user function promises to give us this format: */ + tr->user_fn(png_ptr, &row_info, png_voidcast(png_bytep, tc->dp)); + } +# undef png_ptr +} + +void PNGAPI +png_set_write_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr + write_user_transform_fn) +{ + + if (png_ptr != NULL) + { + if (!png_ptr->read_struct) + { + png_user_transformp tr = get_user_transform(png_ptr); + + tr->user_fn = write_user_transform_fn; + tr->tr.fn = png_do_write_user_transform; + } + + else + png_app_error(png_ptr, "cannot set a write transform on read"); + } +} +#endif /* WRITE_USER_TRANSFORM */ diff --git a/ext/libpng17/pngwio.c b/ext/libpng17/pngwio.c index 1b525f9484..430cb29e56 100644 --- a/ext/libpng17/pngwio.c +++ b/ext/libpng17/pngwio.c @@ -1,8 +1,8 @@ /* pngwio.c - functions for data output * - * Last changed in libpng 1.6.9 [February 6, 2014] - * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * Last changed in libpng 1.7.0 [(PENDING RELEASE)] + * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -19,6 +19,7 @@ */ #include "pngpriv.h" +#define PNG_SRC_FILE PNG_SRC_FILE_pngwio #ifdef PNG_WRITE_SUPPORTED @@ -30,15 +31,24 @@ */ void /* PRIVATE */ -png_write_data(png_structrp png_ptr, png_const_bytep data, png_size_t length) +png_write_data(png_structrp png_ptr, png_const_voidp data, png_size_t length) { - /* NOTE: write_data_fn must not change the buffer! */ - if (png_ptr->write_data_fn != NULL ) - (*(png_ptr->write_data_fn))(png_ptr, png_constcast(png_bytep,data), - length); + /* This was guaranteed by prior versions of libpng, so app callbacks may + * assume it even though it isn't documented to be the case. + */ + debug(length > 0U); + + /* NOTE: write_data_fn must not change the buffer! + * This cast is required because of the API; changing the type of the + * callback would require every app to change the callback and that change + * would have to be conditional on the libpng version. + */ + if (png_ptr->rw_data_fn != NULL ) + png_ptr->rw_data_fn(png_ptr, + png_constcast(png_bytep,png_voidcast(png_const_bytep,data)), length); else - png_error(png_ptr, "Call to NULL write function"); + png_app_error(png_ptr, "No write function"); } #ifdef PNG_STDIO_SUPPORTED @@ -55,36 +65,22 @@ png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) if (png_ptr == NULL) return; - check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr)); + check = fwrite(data, 1, length, png_voidcast(png_FILE_p, png_ptr->io_ptr)); if (check != length) png_error(png_ptr, "Write Error"); } #endif -/* This function is called to output any data pending writing (normally - * to disk). After png_flush is called, there should be no data pending - * writing in any buffers. - */ #ifdef PNG_WRITE_FLUSH_SUPPORTED -void /* PRIVATE */ -png_flush(png_structrp png_ptr) -{ - if (png_ptr->output_flush_fn != NULL) - (*(png_ptr->output_flush_fn))(png_ptr); -} - # ifdef PNG_STDIO_SUPPORTED void PNGCBAPI png_default_flush(png_structp png_ptr) { - png_FILE_p io_ptr; - if (png_ptr == NULL) return; - io_ptr = png_voidcast(png_FILE_p, (png_ptr->io_ptr)); - fflush(io_ptr); + fflush(png_voidcast(png_FILE_p, (png_ptr->io_ptr))); } # endif #endif @@ -125,44 +121,26 @@ png_set_write_fn(png_structrp png_ptr, png_voidp io_ptr, if (png_ptr == NULL) return; - png_ptr->io_ptr = io_ptr; - -#ifdef PNG_STDIO_SUPPORTED - if (write_data_fn != NULL) - png_ptr->write_data_fn = write_data_fn; - - else - png_ptr->write_data_fn = png_default_write_data; -#else - png_ptr->write_data_fn = write_data_fn; -#endif - -#ifdef PNG_WRITE_FLUSH_SUPPORTED -# ifdef PNG_STDIO_SUPPORTED - - if (output_flush_fn != NULL) - png_ptr->output_flush_fn = output_flush_fn; - - else - png_ptr->output_flush_fn = png_default_flush; - -# else - png_ptr->output_flush_fn = output_flush_fn; -# endif -#else - PNG_UNUSED(output_flush_fn) -#endif /* PNG_WRITE_FLUSH_SUPPORTED */ - -#ifdef PNG_READ_SUPPORTED - /* It is an error to read while writing a png file */ - if (png_ptr->read_data_fn != NULL) + if (png_ptr->read_struct) { - png_ptr->read_data_fn = NULL; - - png_warning(png_ptr, - "Can't set both read_data_fn and write_data_fn in the" - " same structure"); + png_app_error(png_ptr, "cannot set a write function on a read struct"); + return; } -#endif + + if (write_data_fn == NULL) + { + png_app_error(png_ptr, + "API change: png_set_write_fn requires a function"); + return; + } + + png_ptr->io_ptr = io_ptr; + png_ptr->rw_data_fn = write_data_fn; +# ifdef PNG_WRITE_FLUSH_SUPPORTED + if (output_flush_fn != NULL) + png_ptr->output_flush_fn = output_flush_fn; +# else + PNG_UNUSED(output_flush_fn) +# endif /* WRITE_FLUSH */ } -#endif /* PNG_WRITE_SUPPORTED */ +#endif /* WRITE */ diff --git a/ext/libpng17/pngwrite.c b/ext/libpng17/pngwrite.c index b43a16fc07..77d1de69c0 100644 --- a/ext/libpng17/pngwrite.c +++ b/ext/libpng17/pngwrite.c @@ -2,7 +2,7 @@ /* pngwrite.c - general routines to write a PNG file * * Last changed in libpng 1.7.0 [(PENDING RELEASE)] - * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -11,15 +11,12 @@ * and license in png.h */ -#ifdef _MSC_VER -#pragma warning (disable:4996) -#pragma warning (disable:4244) -#endif - #include "pngpriv.h" -#if defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) +#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED # include -#endif +#endif /* SIMPLIFIED_WRITE_STDIO */ + +#define PNG_SRC_FILE PNG_SRC_FILE_pngwrite #ifdef PNG_WRITE_SUPPORTED @@ -27,9 +24,9 @@ /* Write out all the unknown chunks for the current given location */ static void write_unknown_chunks(png_structrp png_ptr, png_const_inforp info_ptr, - unsigned int where) + unsigned int where) { - if (info_ptr->unknown_chunks_num) + if (info_ptr->unknown_chunks_num != 0) { png_const_unknown_chunkp up; @@ -38,7 +35,7 @@ write_unknown_chunks(png_structrp png_ptr, png_const_inforp info_ptr, for (up = info_ptr->unknown_chunks; up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; ++up) - if (up->location & where) + if ((up->location & where) != 0) { /* If per-chunk unknown chunk handling is enabled use it, otherwise * just write the chunks the application has set. @@ -64,17 +61,80 @@ write_unknown_chunks(png_structrp png_ptr, png_const_inforp info_ptr, (keep == PNG_HANDLE_CHUNK_AS_DEFAULT && png_ptr->unknown_default == PNG_HANDLE_CHUNK_ALWAYS))) #endif - { - /* TODO: review, what is wrong with a zero length unknown chunk? */ - if (up->size == 0) - png_warning(png_ptr, "Writing zero-length unknown chunk"); - png_write_chunk(png_ptr, up->name, up->data, up->size); - } } } } -#endif /* PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED */ +#endif /* WRITE_UNKNOWN_CHUNKS */ + +#ifdef PNG_WRITE_TEXT_SUPPORTED +static void +png_write_text(png_structrp png_ptr, png_const_inforp info_ptr, png_byte where) + /* Text chunk helper */ +{ + int i; + + /* Check to see if we need to write text chunks */ + for (i = 0; i < info_ptr->num_text; i++) + { + png_debug2(2, "Writing text chunk %d, type %d", i, + info_ptr->text[i].compression); + + /* Text chunks are written at info_ptr->text[i].location, skip the chunk + * if we are not writing at that location: + */ + if ((info_ptr->text[i].location & where) == 0U) + continue; + + switch (info_ptr->text[i].compression) + { + case PNG_ITXT_COMPRESSION_NONE: + case PNG_ITXT_COMPRESSION_zTXt: +# ifdef PNG_WRITE_iTXt_SUPPORTED + /* Write international chunk */ + png_write_iTXt(png_ptr, info_ptr->text[i].compression, + info_ptr->text[i].key, info_ptr->text[i].lang, + info_ptr->text[i].lang_key, info_ptr->text[i].text); +# else /* !WRITE_iTXT */ + png_app_error(png_ptr, "Unable to write international text"); +# endif /* !WRITE_iTXT */ + break; + + case PNG_TEXT_COMPRESSION_zTXt: +# ifdef PNG_WRITE_zTXt_SUPPORTED + /* Write compressed chunk */ + png_write_zTXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, info_ptr->text[i].compression); +# else /* !WRITE_zTXT */ + png_app_error(png_ptr, "Unable to write compressed text"); +# endif /* !WRITE_zTXT */ + break; + + case PNG_TEXT_COMPRESSION_NONE: +# ifdef PNG_WRITE_tEXt_SUPPORTED + /* Write uncompressed chunk */ + png_write_tEXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0); +# else /* !WRITE_tEXt */ + /* Can't get here TODO: why not? */ + png_app_error(png_ptr, "Unable to write uncompressed text"); +# endif /* !WRITE_tEXt */ + break; + + default: + /* This is an internal error because the libpng checking should + * never manage to set any 'compression' except the above values. + */ + impossible("invalid text compression"); + } + + /* The chunk was written, record where. This allows the location to have + * multiple bits set; the first successful write freezes the location. + */ + info_ptr->text[i].location = where; + } +} +#endif /* WRITE_TEXT */ /* Writes all the PNG information. This is the suggested way to use the * library. If you have a new chunk to add, make a function to write it, @@ -93,257 +153,240 @@ png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr) if (png_ptr == NULL || info_ptr == NULL) return; - if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) { - /* Write PNG signature */ - png_write_sig(png_ptr); + int color_type = PNG_COLOR_TYPE_FROM_FORMAT(info_ptr->format); -#ifdef PNG_MNG_FEATURES_SUPPORTED - if ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) && \ - (png_ptr->mng_features_permitted)) - { - png_warning(png_ptr, "MNG features are not allowed in a PNG datastream"); - png_ptr->mng_features_permitted = 0; - } -#endif + /* Write PNG signature; doesn't set PNG_HAVE_PNG_SIGNATURE if it has + * already been written (or rather, if at least 3 bytes have already been + * written; undocumented wackiness, it means the 'PNG' at the start can be + * replace by, e.g. "FOO" or "BAR" or "MNG"). + */ + png_write_sig(png_ptr); - /* Write IHDR information. */ - png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height, - info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type, - info_ptr->filter_type, -#ifdef PNG_WRITE_INTERLACING_SUPPORTED - info_ptr->interlace_type -#else - 0 -#endif - ); +# ifdef PNG_MNG_FEATURES_SUPPORTED + if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) != 0 && + png_ptr->mng_features_permitted != 0) + { + png_app_error(png_ptr, + "MNG features are not allowed in a PNG datastream"); + /* Recovery: disable MNG features: */ + png_ptr->mng_features_permitted = 0; + } +# endif /* MNG_FEATURES */ - /* The rest of these check to see if the valid field has the appropriate - * flag set, and if it does, writes the chunk. - * - * 1.6.0: COLORSPACE support controls the writing of these chunks too, and - * the chunks will be written if the WRITE routine is there and information - * is available in the COLORSPACE. (See png_colorspace_sync_info in png.c - * for where the valid flags get set.) - * - * Under certain circumstances the colorspace can be invalidated without - * syncing the info_struct 'valid' flags; this happens if libpng detects and - * error and calls png_error while the color space is being set, yet the - * application continues writing the PNG. So check the 'invalid' flag here - * too. - */ -#ifdef PNG_GAMMA_SUPPORTED -# ifdef PNG_WRITE_gAMA_SUPPORTED - if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) && - (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_gAMA) && - (info_ptr->valid & PNG_INFO_gAMA)) - png_write_gAMA_fixed(png_ptr, info_ptr->colorspace.gamma); -# endif -#endif + /* Write IHDR information. */ + png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, color_type, info_ptr->compression_type, + info_ptr->filter_type, info_ptr->interlace_type); -#ifdef PNG_COLORSPACE_SUPPORTED - /* Write only one of sRGB or an ICC profile. If a profile was supplied - * and it matches one of the known sRGB ones issue a warning. - */ -# ifdef PNG_WRITE_iCCP_SUPPORTED - if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) && - (info_ptr->valid & PNG_INFO_iCCP)) - { +# ifdef PNG_WRITE_TRANSFORMS_SUPPORTED + /* This are used for checking later on: */ + png_ptr->info_format = info_ptr->format; +# endif /* WRITE_TRANSFORMS */ + + /* This sets the flag that prevents re-entry to the 'before PLTE' case: */ + affirm((png_ptr->mode & PNG_HAVE_IHDR) != 0); + + /* The rest of these check to see if the valid field has the appropriate + * flag set, and if it does, writes the chunk. + * + * 1.6.0: COLORSPACE support controls the writing of these chunks too, and + * the chunks will be written if the WRITE routine is there and + * information is available in the COLORSPACE. (See + * png_colorspace_sync_info in png.c for where the valid flags get set.) + * + * Under certain circumstances the colorspace can be invalidated without + * syncing the info_struct 'valid' flags; this happens if libpng detects + * an error and calls png_error while the color space is being set, yet + * the application continues writing the PNG. So check the 'invalid' + * flag here too. + */ +# ifdef PNG_WRITE_tIME_SUPPORTED + if ((info_ptr->valid & PNG_INFO_tIME) != 0 && + (info_ptr->time_location & PNG_HAVE_IHDR) != 0) + png_write_tIME(png_ptr, &(info_ptr->mod_time)); +# endif /* WRITE_tIME */ + +# ifdef PNG_WRITE_gAMA_SUPPORTED /* enables GAMMA */ + if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 && + (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_gAMA) != 0 && + (info_ptr->valid & PNG_INFO_gAMA) != 0) + { + /* This is the inverse of the test in png.c: */ + affirm(info_ptr->colorspace.gamma >= 16 && + info_ptr->colorspace.gamma <= 625000000); + png_write_gAMA_fixed(png_ptr, info_ptr->colorspace.gamma); + } +# endif /* WRITE_gAMA */ + + /* Write only one of sRGB or an ICC profile. If a profile was supplied + * and it matches one of the known sRGB ones issue a warning. + */ +# ifdef PNG_WRITE_iCCP_SUPPORTED /* enables COLORSPACE, GAMMA */ + if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 && + (info_ptr->valid & PNG_INFO_iCCP) != 0) + { +# ifdef PNG_WRITE_sRGB_SUPPORTED + /* The app must have supplied an sRGB iCCP profile (and one that + * is recognized and therefore known to be correct) so we write + * that profile, even though it increases the size of the PNG + * significantly. A warning is reasonable: + */ + if ((info_ptr->valid & PNG_INFO_sRGB) != 0) + png_app_warning(png_ptr, + "profile matches sRGB but writing iCCP instead"); +# endif /* WRITE_sRGB */ + + png_write_iCCP(png_ptr, info_ptr->iccp_name, + info_ptr->iccp_profile); + } # ifdef PNG_WRITE_sRGB_SUPPORTED - if (info_ptr->valid & PNG_INFO_sRGB) - png_app_warning(png_ptr, - "profile matches sRGB but writing iCCP instead"); -# endif + else /* iCCP not written */ +# endif /* WRITE_sRGB */ +# endif /* WRITE_iCCP */ - png_write_iCCP(png_ptr, info_ptr->iccp_name, - info_ptr->iccp_profile); - } -# ifdef PNG_WRITE_sRGB_SUPPORTED - else +# ifdef PNG_WRITE_sRGB_SUPPORTED /* enables COLORSPACE, GAMMA */ + if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 && + (info_ptr->valid & PNG_INFO_sRGB) != 0) + png_write_sRGB(png_ptr, info_ptr->colorspace.rendering_intent); +# endif /* WRITE_sRGB */ + +# ifdef PNG_WRITE_sBIT_SUPPORTED + if ((info_ptr->valid & PNG_INFO_sBIT) != 0) + png_write_sBIT(png_ptr, &(info_ptr->sig_bit), color_type); +# endif /* WRITE_sBIT */ + +# ifdef PNG_WRITE_cHRM_SUPPORTED /* enables COLORSPACE */ + if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 && + (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) != 0 && + (info_ptr->valid & PNG_INFO_cHRM) != 0) + png_write_cHRM_fixed(png_ptr, &info_ptr->colorspace.end_points_xy); +# endif /* WRITE_cHRM */ + +# ifdef PNG_WRITE_TEXT_SUPPORTED + if (info_ptr->num_text > 0) + png_write_text(png_ptr, info_ptr, PNG_HAVE_IHDR); +# endif /* WRITE_TEXT */ + +# ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + /* The third arugment must encode only one bit, otherwise chunks will + * be written twice because the test in write_unknown_chunks is + * 'location & where'. + */ + write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_IHDR); # endif -# endif - -# ifdef PNG_WRITE_sRGB_SUPPORTED - if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) && - (info_ptr->valid & PNG_INFO_sRGB)) - png_write_sRGB(png_ptr, info_ptr->colorspace.rendering_intent); -# endif /* WRITE_sRGB */ -#endif /* COLORSPACE */ - -#ifdef PNG_WRITE_sBIT_SUPPORTED - if (info_ptr->valid & PNG_INFO_sBIT) - png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type); -#endif - -#ifdef PNG_COLORSPACE_SUPPORTED -# ifdef PNG_WRITE_cHRM_SUPPORTED - if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) && - (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) && - (info_ptr->valid & PNG_INFO_cHRM)) - png_write_cHRM_fixed(png_ptr, &info_ptr->colorspace.end_points_xy); -# endif -#endif - -#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED - write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_IHDR); -#endif - - png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE; } + + else /* 1.7.0: flag multiple calls; previously ignored */ + png_app_error(png_ptr, + "png_write_info_before_PLTE called more than once"); } void PNGAPI png_write_info(png_structrp png_ptr, png_const_inforp info_ptr) { -#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) - int i; -#endif - png_debug(1, "in png_write_info"); if (png_ptr == NULL || info_ptr == NULL) return; - png_write_info_before_PLTE(png_ptr, info_ptr); + if ((png_ptr->mode & (PNG_HAVE_PLTE+PNG_HAVE_IDAT)) != 0) + { + png_app_error(png_ptr, "late call to png_write_info"); + return; + } - if (info_ptr->valid & PNG_INFO_PLTE) - png_write_PLTE(png_ptr, info_ptr->palette, - (png_uint_32)info_ptr->num_palette); + /* The app may do this for us, and in 1.7.0 multiple calls are flagged as an + * application error, so this code must check: + */ + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_write_info_before_PLTE(png_ptr, info_ptr); - else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + if ((info_ptr->valid & PNG_INFO_PLTE) != 0) + png_write_PLTE(png_ptr, info_ptr->palette, info_ptr->num_palette); + + /* Validate the consistency of the PNG being produced; a palette must have + * been written if a palette mapped PNG is to be valid: + */ + if ((png_ptr->mode & PNG_HAVE_PLTE) == 0 && + png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) png_error(png_ptr, "Valid palette required for paletted images"); -#ifdef PNG_WRITE_tRNS_SUPPORTED - if (info_ptr->valid & PNG_INFO_tRNS) - { -#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED - /* Invert the alpha channel (in tRNS) */ - if ((png_ptr->transformations & PNG_INVERT_ALPHA) && - info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + /* But always set the mode flag because without this we don't know when to + * write the post-palette text or unknown chunks. + */ + png_ptr->mode |= PNG_HAVE_PLTE; + +# ifdef PNG_WRITE_tRNS_SUPPORTED + if ((info_ptr->valid & PNG_INFO_tRNS) !=0) { - int j, jend; - - jend = info_ptr->num_trans; - if (jend > PNG_MAX_PALETTE_LENGTH) - jend = PNG_MAX_PALETTE_LENGTH; - - for (j = 0; jtrans_alpha[j] = - (png_byte)(255 - info_ptr->trans_alpha[j]); + png_write_tRNS(png_ptr, info_ptr->trans_alpha, + &(info_ptr->trans_color), info_ptr->num_trans, + PNG_COLOR_TYPE_FROM_FORMAT(info_ptr->format)); } -#endif - png_write_tRNS(png_ptr, info_ptr->trans_alpha, &(info_ptr->trans_color), - info_ptr->num_trans, info_ptr->color_type); - } -#endif -#ifdef PNG_WRITE_bKGD_SUPPORTED - if (info_ptr->valid & PNG_INFO_bKGD) - png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type); -#endif +# endif /* WRITE_tRNS */ -#ifdef PNG_WRITE_hIST_SUPPORTED - if (info_ptr->valid & PNG_INFO_hIST) - png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette); -#endif +# ifdef PNG_WRITE_bKGD_SUPPORTED + if ((info_ptr->valid & PNG_INFO_bKGD) != 0) + png_write_bKGD(png_ptr, &(info_ptr->background), + PNG_COLOR_TYPE_FROM_FORMAT(info_ptr->format)); +# endif /* WRITE_bKGD */ -#ifdef PNG_WRITE_oFFs_SUPPORTED - if (info_ptr->valid & PNG_INFO_oFFs) - png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset, - info_ptr->offset_unit_type); -#endif +# ifdef PNG_WRITE_hIST_SUPPORTED + if ((info_ptr->valid & PNG_INFO_hIST) != 0) + png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette); +# endif /* WRITE_hIST */ -#ifdef PNG_WRITE_pCAL_SUPPORTED - if (info_ptr->valid & PNG_INFO_pCAL) - png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0, - info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams, - info_ptr->pcal_units, info_ptr->pcal_params); -#endif +# ifdef PNG_WRITE_oFFs_SUPPORTED + if ((info_ptr->valid & PNG_INFO_oFFs) != 0) + png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset, + info_ptr->offset_unit_type); +# endif /* WRITE_oFFs */ -#ifdef PNG_WRITE_sCAL_SUPPORTED - if (info_ptr->valid & PNG_INFO_sCAL) - png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit, - info_ptr->scal_s_width, info_ptr->scal_s_height); -#endif /* sCAL */ +# ifdef PNG_WRITE_pCAL_SUPPORTED + if ((info_ptr->valid & PNG_INFO_pCAL) != 0) + png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0, + info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams, + info_ptr->pcal_units, info_ptr->pcal_params); +# endif /* WRITE_pCAL */ -#ifdef PNG_WRITE_pHYs_SUPPORTED - if (info_ptr->valid & PNG_INFO_pHYs) - png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit, - info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type); -#endif /* pHYs */ +# ifdef PNG_WRITE_sCAL_SUPPORTED + if ((info_ptr->valid & PNG_INFO_sCAL) != 0) + png_write_sCAL_s(png_ptr, info_ptr->scal_unit, info_ptr->scal_s_width, + info_ptr->scal_s_height); +# endif /* WRITE_sCAL */ -#ifdef PNG_WRITE_tIME_SUPPORTED - if (info_ptr->valid & PNG_INFO_tIME) - { - png_write_tIME(png_ptr, &(info_ptr->mod_time)); - png_ptr->mode |= PNG_WROTE_tIME; - } -#endif /* tIME */ +# ifdef PNG_WRITE_pHYs_SUPPORTED + if ((info_ptr->valid & PNG_INFO_pHYs) != 0) + png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit, + info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type); +# endif /* WRITE_pHYs */ -#ifdef PNG_WRITE_sPLT_SUPPORTED - if (info_ptr->valid & PNG_INFO_sPLT) - for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) - png_write_sPLT(png_ptr, info_ptr->splt_palettes + i); -#endif /* sPLT */ +# ifdef PNG_WRITE_tIME_SUPPORTED + if ((info_ptr->valid & PNG_INFO_tIME) != 0 && + (info_ptr->time_location & PNG_HAVE_PLTE) != 0) + png_write_tIME(png_ptr, &(info_ptr->mod_time)); +# endif /* WRITE_tIME */ -#ifdef PNG_WRITE_TEXT_SUPPORTED - /* Check to see if we need to write text chunks */ - for (i = 0; i < info_ptr->num_text; i++) - { - png_debug2(2, "Writing header text chunk %d, type %d", i, - info_ptr->text[i].compression); - /* An internationalized chunk? */ - if (info_ptr->text[i].compression > 0) +# ifdef PNG_WRITE_sPLT_SUPPORTED + if ((info_ptr->valid & PNG_INFO_sPLT) != 0) { -#ifdef PNG_WRITE_iTXt_SUPPORTED - /* Write international chunk */ - png_write_iTXt(png_ptr, - info_ptr->text[i].compression, - info_ptr->text[i].key, - info_ptr->text[i].lang, - info_ptr->text[i].lang_key, - info_ptr->text[i].text); -#else - png_warning(png_ptr, "Unable to write international text"); -#endif - /* Mark this chunk as written */ - info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; - } + int i; - /* If we want a compressed text chunk */ - else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt) - { -#ifdef PNG_WRITE_zTXt_SUPPORTED - /* Write compressed chunk */ - png_write_zTXt(png_ptr, info_ptr->text[i].key, - info_ptr->text[i].text, 0, - info_ptr->text[i].compression); -#else - png_warning(png_ptr, "Unable to write compressed text"); -#endif - /* Mark this chunk as written */ - info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; + for (i = 0; i < info_ptr->splt_palettes_num; i++) + png_write_sPLT(png_ptr, info_ptr->splt_palettes + i); } +# endif /* WRITE_sPLT */ - else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) - { -#ifdef PNG_WRITE_tEXt_SUPPORTED - /* Write uncompressed chunk */ - png_write_tEXt(png_ptr, info_ptr->text[i].key, - info_ptr->text[i].text, - 0); - /* Mark this chunk as written */ - info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; -#else - /* Can't get here */ - png_warning(png_ptr, "Unable to write uncompressed text"); -#endif - } - } -#endif /* tEXt */ +# ifdef PNG_WRITE_TEXT_SUPPORTED + if (info_ptr->num_text > 0) + png_write_text(png_ptr, info_ptr, PNG_HAVE_PLTE); +# endif /* WRITE_TEXT */ -#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED - write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_PLTE); -#endif +# ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_PLTE); +# endif /* WRITE_UNKNOWN_CHUNKS */ } /* Writes the end of the PNG file. If you don't want to write comments or @@ -359,89 +402,76 @@ png_write_end(png_structrp png_ptr, png_inforp info_ptr) if (png_ptr == NULL) return; - if (!(png_ptr->mode & PNG_HAVE_IDAT)) - png_error(png_ptr, "No IDATs written into file"); + if ((png_ptr->mode & + (PNG_HAVE_IHDR+PNG_HAVE_IDAT+PNG_AFTER_IDAT+PNG_HAVE_IEND)) != + (PNG_HAVE_IHDR+PNG_HAVE_IDAT+PNG_AFTER_IDAT)) + { + /* Out of place png_write_end: */ + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_error(png_ptr, "Missing call to png_write_info"); -#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED - if (png_ptr->num_palette_max > png_ptr->num_palette) - png_benign_error(png_ptr, "Wrote palette index exceeding num_palette"); -#endif + else if ((png_ptr->mode & PNG_HAVE_IDAT) == 0 && png_ptr->zowner == 0) + { + /* TODO: write unknown IDAT here, for the moment allow the app to write + * IDAT then call write_end: + */ + png_app_error(png_ptr, "No IDATs written into file"); + png_ptr->mode |= PNG_HAVE_IDAT+PNG_AFTER_IDAT; + } + + else if ((png_ptr->mode & PNG_AFTER_IDAT) == 0) + { + affirm(png_ptr->zowner == png_IDAT); + png_error(png_ptr, "incomplete PNG image"); /* unrecoverable */ + } + + else if ((png_ptr->mode & PNG_HAVE_IEND) != 0) + { + png_app_error(png_ptr, "multiple calls to png_write_end"); + return; + } + + else + impossible("not reached"); + } + + /* And double check that the image rows were all written; this is actually + * a harmless error on an interlaced image because the image rows with + * data were all passed in or the above check would not work. + * + * Don't do this if the IDAT came from unknowns (TBD) or the app, above. + * + * The check depends on the precise logic in png_write_row. + */ + else if (png_ptr->pass != 7U) + png_app_error(png_ptr, "png_write_row not called to last row"); + + else + debug(png_ptr->row_number == 0U); /* See if user wants us to write information chunks */ if (info_ptr != NULL) { -#ifdef PNG_WRITE_TEXT_SUPPORTED - int i; /* local index variable */ -#endif -#ifdef PNG_WRITE_tIME_SUPPORTED - /* Check to see if user has supplied a time chunk */ - if ((info_ptr->valid & PNG_INFO_tIME) && - !(png_ptr->mode & PNG_WROTE_tIME)) - png_write_tIME(png_ptr, &(info_ptr->mod_time)); +# ifdef PNG_WRITE_tIME_SUPPORTED + /* Check to see if user has supplied a time chunk */ + if ((info_ptr->valid & PNG_INFO_tIME) != 0 && + (info_ptr->time_location & PNG_AFTER_IDAT) != 0) + png_write_tIME(png_ptr, &(info_ptr->mod_time)); +# endif -#endif -#ifdef PNG_WRITE_TEXT_SUPPORTED - /* Loop through comment chunks */ - for (i = 0; i < info_ptr->num_text; i++) - { - png_debug2(2, "Writing trailer text chunk %d, type %d", i, - info_ptr->text[i].compression); - /* An internationalized chunk? */ - if (info_ptr->text[i].compression > 0) - { -#ifdef PNG_WRITE_iTXt_SUPPORTED - /* Write international chunk */ - png_write_iTXt(png_ptr, - info_ptr->text[i].compression, - info_ptr->text[i].key, - info_ptr->text[i].lang, - info_ptr->text[i].lang_key, - info_ptr->text[i].text); -#else - png_warning(png_ptr, "Unable to write international text"); -#endif - /* Mark this chunk as written */ - info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; - } +# ifdef PNG_WRITE_TEXT_SUPPORTED + if (info_ptr->num_text > 0) + png_write_text(png_ptr, info_ptr, PNG_AFTER_IDAT); +# endif /* WRITE_TEXT */ - else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt) - { -#ifdef PNG_WRITE_zTXt_SUPPORTED - /* Write compressed chunk */ - png_write_zTXt(png_ptr, info_ptr->text[i].key, - info_ptr->text[i].text, 0, - info_ptr->text[i].compression); -#else - png_warning(png_ptr, "Unable to write compressed text"); -#endif - /* Mark this chunk as written */ - info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; - } - - else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) - { -#ifdef PNG_WRITE_tEXt_SUPPORTED - /* Write uncompressed chunk */ - png_write_tEXt(png_ptr, info_ptr->text[i].key, - info_ptr->text[i].text, 0); -#else - png_warning(png_ptr, "Unable to write uncompressed text"); -#endif - - /* Mark this chunk as written */ - info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; - } - } -#endif -#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED - write_unknown_chunks(png_ptr, info_ptr, PNG_AFTER_IDAT); -#endif +# ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + write_unknown_chunks(png_ptr, info_ptr, PNG_AFTER_IDAT); +# endif } - png_ptr->mode |= PNG_AFTER_IDAT; - /* Write end of PNG file */ png_write_IEND(png_ptr); + /* This flush, added in libpng-1.0.8, removed from libpng-1.0.9beta03, * and restored again in libpng-1.2.30, may cause some applications that * do not set png_ptr->output_flush_fn to crash. If your application @@ -449,11 +479,12 @@ png_write_end(png_structrp png_ptr, png_inforp info_ptr) * PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED defined, and report the event to * png-mng-implement at lists.sf.net . */ -#ifdef PNG_WRITE_FLUSH_SUPPORTED -# ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED - png_flush(png_ptr); +# ifdef PNG_WRITE_FLUSH_SUPPORTED +# ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED + if (png_ptr->output_flush_fn != NULL) + png_ptr->output_flush_fn(png_ptr); +# endif # endif -#endif } #ifdef PNG_CONVERT_tIME_SUPPORTED @@ -462,12 +493,12 @@ png_convert_from_struct_tm(png_timep ptime, PNG_CONST struct tm * ttime) { png_debug(1, "in png_convert_from_struct_tm"); - ptime->year = (png_uint_16)(1900 + ttime->tm_year); - ptime->month = (png_byte)(ttime->tm_mon + 1); - ptime->day = (png_byte)ttime->tm_mday; - ptime->hour = (png_byte)ttime->tm_hour; - ptime->minute = (png_byte)ttime->tm_min; - ptime->second = (png_byte)ttime->tm_sec; + ptime->year = png_check_u16(0/*TODO: fixme*/, 1900 + ttime->tm_year); + ptime->month = png_check_byte(0/*TODO: fixme*/, ttime->tm_mon + 1); + ptime->day = png_check_byte(0/*TODO: fixme*/, ttime->tm_mday); + ptime->hour = png_check_byte(0/*TODO: fixme*/, ttime->tm_hour); + ptime->minute = png_check_byte(0/*TODO: fixme*/, ttime->tm_min); + ptime->second = png_check_byte(0/*TODO: fixme*/, ttime->tm_sec); } void PNGAPI @@ -489,7 +520,7 @@ png_create_write_struct,(png_const_charp user_png_ver, png_voidp error_ptr, { #ifndef PNG_USER_MEM_SUPPORTED png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr, - error_fn, warn_fn, NULL, NULL, NULL); + error_fn, warn_fn, NULL, NULL, NULL); #else return png_create_write_struct_2(user_png_ver, error_ptr, error_fn, warn_fn, NULL, NULL, NULL); @@ -502,86 +533,516 @@ png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED) { png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr, - error_fn, warn_fn, mem_ptr, malloc_fn, free_fn); -#endif /* PNG_USER_MEM_SUPPORTED */ + error_fn, warn_fn, mem_ptr, malloc_fn, free_fn); +#endif /* USER_MEM */ if (png_ptr != NULL) { - /* Set the zlib control values to defaults; they can be overridden by the - * application after the struct has been created. - */ - png_ptr->zbuffer_size = PNG_ZBUF_SIZE; - - /* The 'zlib_strategy' setting is irrelevant because png_default_claim in - * pngwutil.c defaults it according to whether or not filters will be - * used, and ignores this setting. - */ - png_ptr->zlib_strategy = PNG_Z_DEFAULT_STRATEGY; - png_ptr->zlib_level = PNG_Z_DEFAULT_COMPRESSION; - png_ptr->zlib_mem_level = 8; - png_ptr->zlib_window_bits = 15; - png_ptr->zlib_method = 8; - -#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED - png_ptr->zlib_text_strategy = PNG_TEXT_Z_DEFAULT_STRATEGY; - png_ptr->zlib_text_level = PNG_TEXT_Z_DEFAULT_COMPRESSION; - png_ptr->zlib_text_mem_level = 8; - png_ptr->zlib_text_window_bits = 15; - png_ptr->zlib_text_method = 8; -#endif /* PNG_WRITE_COMPRESSED_TEXT_SUPPORTED */ - - /* This is a highly dubious configuration option; by default it is off, - * but it may be appropriate for private builds that are testing - * extensions not conformant to the current specification, or of - * applications that must not fail to write at all costs! - */ -#ifdef PNG_BENIGN_WRITE_ERRORS_SUPPORTED - png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN; - /* In stable builds only warn if an application error can be completely - * handled. - */ -#endif - - /* App warnings are warnings in release (or release candidate) builds but - * are errors during development. - */ -#if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC - png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN; -#endif - - /* TODO: delay this, it can be done in png_init_io() (if the app doesn't - * do it itself) avoiding setting the default function if it is not - * required. - */ - png_set_write_fn(png_ptr, NULL, NULL, NULL); +# ifdef PNG_BENIGN_ERRORS_SUPPORTED +# if !PNG_RELEASE_BUILD + /* Always quit on error prior to release */ + png_ptr->benign_error_action = PNG_ERROR; + png_ptr->app_warning_action = PNG_WARN; + png_ptr->app_error_action = PNG_ERROR; +# else /* RELEASE_BUILD */ + /* Allow benign errors on write, subject to app control. */ +# ifdef PNG_BENIGN_WRITE_ERRORS_SUPPORTED + png_ptr->benign_error_action = PNG_WARN; + png_ptr->app_error_action = PNG_WARN; + png_ptr->app_warning_action = PNG_WARN; +# else /* !BENIGN_WRITE_ERRORS */ + /* libpng build without benign error support; the application + * author has to be assumed to be correct, so: + */ + png_ptr->benign_error_action = PNG_ERROR; + png_ptr->app_warning_action = PNG_WARN; + png_ptr->app_error_action = PNG_ERROR; +# endif /* !BENIGN_WRITE_ERRORS */ +# endif /* RELEASE_BUILD */ +# endif /* BENIGN_ERRORS */ } return png_ptr; } +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) ||\ + defined(PNG_WRITE_TRANSFORMS_SUPPORTED) +static void +write_row_buffered(png_structrp png_ptr, + png_const_bytep row, unsigned int row_info_flags, + void (*copy_fn)(png_const_structrp png_ptr, png_bytep row_buffer, + png_const_bytep row, png_uint_32 x, unsigned int count, unsigned int p), + unsigned int copy_parameter) +{ + unsigned int max_pixels = png_max_pixel_block(png_ptr); + const unsigned int pass = png_ptr->pass; + const png_uint_32 width = png_ptr->interlaced == PNG_INTERLACE_NONE ? + png_ptr->width : PNG_PASS_COLS(png_ptr->width, pass); + png_uint_32 x; + png_byte prev_pixels[4*2*2]; /* 2 pixels up to 4 2-byte channels each */ + + memset(prev_pixels, 0U, sizeof prev_pixels); + + for (x = 0U; x < width; x += max_pixels) + { + union + { + PNG_ROW_BUFFER_ALIGN_TYPE force_buffer_alignment; + png_byte buffer[PNG_ROW_BUFFER_SIZE]; + } pixel_buffer; + + if (max_pixels > width - x) + max_pixels = (unsigned int)/*SAFE*/(width - x); + + debug((row_info_flags & png_row_end) == 0U); /* must be set here at end */ + if (x + max_pixels >= width) + row_info_flags |= png_row_end; + + /* Copy a block of input pixels into the buffer, effecting the interlace + * on the way if required. The argument is the number of pixels in the + * buffer, not the number handled from the input which will be larger in + * the interlaced case. + */ + copy_fn(png_ptr, pixel_buffer.buffer, row, x, max_pixels, copy_parameter); + + /* Now pixel_buffer[0..max_pixels-1] contains max_pixels pixels which may + * need to be transformed (the interlace has already been handled). + */ +# ifdef PNG_WRITE_TRANSFORMS_SUPPORTED + if (png_ptr->transform_list != NULL) + { + png_transform_control tc; + + /* The initial values are the memory format; this was worked out in + * png_init_row_info below. + */ + memset(&tc, 0, sizeof tc); + tc.png_ptr = png_ptr; + tc.sp = tc.dp = pixel_buffer.buffer; + + tc.width = max_pixels; /* width of block that we have */ + tc.format = png_ptr->row_format; + tc.range = png_ptr->row_range; + tc.bit_depth = png_ptr->row_bit_depth; + /* tc.init == 0 */ + /* tc.caching: not used */ + /* tc.palette: not used */ + debug(PNG_TC_PIXEL_DEPTH(tc) == png_ptr->row_input_pixel_depth); + + /* Run the list. */ + png_run_transform_list_backwards(png_ptr, &tc); + + /* Make sure the format that resulted is compatible with PNG: */ + affirm((tc.format & PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA + + PNG_FORMAT_FLAG_COLOR + PNG_FORMAT_FLAG_LINEAR + + PNG_FORMAT_FLAG_COLORMAP)) == 0); + + /* Now we must have the PNG format from the IHDR: */ + affirm(png_ptr->bit_depth == tc.bit_depth && + png_ptr->color_type == PNG_COLOR_TYPE_FROM_FORMAT(tc.format)); + } +# endif /* WRITE_TRANSFORMS */ + + /* Call png_write_png_data to write this block of data, the test on + * maxpixels says if this is the final block in the row. + */ + png_write_png_data(png_ptr, prev_pixels, pixel_buffer.buffer, x, + max_pixels, row_info_flags); + } +} +#endif /* WRITE { INTERLACING || TRANSFORMS } */ + +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED +static void +copy_row(png_const_structrp png_ptr, png_bytep row_buffer, + png_const_bytep row, png_uint_32 x, unsigned int count, + unsigned int pixel_depth) +{ + /* Copy row[x..x+count] pixels to row_buffer. */ + png_copy_row(png_ptr, row_buffer, row, x, count, pixel_depth, 1/*clear*/, + 0/* x_in_dest; row[x]->row_buffer */); +} +#endif /* WRITE_TRANSFORMS */ + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED +static void +interlace_row_lbd(png_const_structrp png_ptr, png_bytep dp, png_const_bytep sp, + png_uint_32 x, unsigned int count, const unsigned int B) +{ + /* Pick out the correct pixels for the interlace pass. The basic idea here + * is to go through the row with a source pointer and a destination pointer + * (sp and dp), and copy the correct pixels for the pass. As the row gets + * compacted, sp will always be >= dp, so we should never overwrite anything. + * See the default: case for the easiest code to understand. + */ + const unsigned int pass = png_ptr->pass; + png_uint_32 i = PNG_COL_FROM_PASS_COL(x, pass); + const unsigned int inc = PNG_PASS_COL_OFFSET(pass); + + /* For pixels less than one byte wide the correct pixels have to be + * extracted from the input bytes. Because we are reading data in + * the application memory format we cannot rely on the PNG big + * endian order. Notice that this was apparently broken before + * 1.7.0. + * + * In libpng 1.7.0 libpng uses a classic bit-pump to optimize the + * extraction. In all passes before the last (6/7) no two pixels + * are adjacent in the input, so we are always extracting 1 bit. + * At present the code uses an 8-bit buffer to avoid coding for + * different byte sexes, but this could easily be changed. + * + * 'i' is the bit-index of bit in the input (sp[]), so, + * considering the 1-bit per pixel case, sp[i>>3] is the byte + * and the bit is bit (i&7) (0 lowest) on swapped (little endian) + * data or 7-(i&7) on PNG default (big-endian) data. + * + * Define these macros, where: + * + * B: the log2 bit depth (0, 1, 2 for 1bpp, 2bpp or 4bpp) of + * the data; this should be a constant. + * sp: the source pointer (sp) (a png_const_bytep) + * i: the pixel index in the input (png_uint_32) + * j: the bit index in the output (unsigned int) + * + * Unlike 'i', 'j' is interpreted directly; for LSB bytes it counts + * up, for MSB it counts down. + * + * NOTE: this could all be expanded to eliminate the code below by + * the time honoured copy'n'paste into three separate functions. This + * might be worth doing in the future. + */ +# define PIXEL_MASK ((1U << (1<>(3-(B))]) /* byte to use */ +# define SP_OFFSET_LSB ((BIT_MASK & i) << (B)) +# define SP_OFFSET_MSB ((BIT_MASK & ~i) << (B)) +# define SP_PIXEL(sex) ((SP_BYTE >> SP_OFFSET_ ## sex) & PIXEL_MASK) + { + unsigned int j; + unsigned int d; + + /* The data is always in the PNG, big-endian, format: */ + for (j = 8U, d = 0U; count > 0U; --count, i += inc) + { /* big-endian */ + j -= 1U<pass; + const unsigned int inc = PNG_PASS_COL_OFFSET(pass); + + /* Loop through the input copying each pixel to the correct place + * in the output. Note that the loop may be executed 0 times if + * this is called on a narrow image that does not contain this + * pass. + */ + for (sp += PNG_COL_FROM_PASS_COL(x, pass) * cbytes; count > 0; + --count, sp += inc * cbytes, dp += cbytes) + memcpy(dp, sp, cbytes); +} +#endif /* WRITE_INTERLACING */ + +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED +static void +write_row_core(png_structrp png_ptr, png_const_bytep row, + unsigned int row_info_flags) +{ +# ifdef PNG_WRITE_TRANSFORMS_SUPPORTED + if (png_ptr->transform_list != NULL) + write_row_buffered(png_ptr, row, row_info_flags, + copy_row, png_ptr->row_input_pixel_depth); + + else +# endif /* WRITE_TRANSFORMS */ + + /* If control reaches this point the intermediate buffer is not required and + * the input data can be used unmodified. + */ + png_write_png_rows(png_ptr, &row, 1U); + PNG_UNUSED(row_info_flags) +} + +/* Write a single non-interlaced row. */ +static void +write_row_non_interlaced(png_structrp png_ptr, png_const_bytep row) +{ + const png_uint_32 row_number = png_ptr->row_number+1U; + /* There is only one pass, so this is the last pass: */ + const unsigned int row_info_flags = + (row_number == 1U ? png_pass_first_row : 0) | + (row_number >= png_ptr->height ? png_pass_last_row : 0) | + png_pass_last; + + debug(png_ptr->interlaced == PNG_INTERLACE_NONE); + + write_row_core(png_ptr, row, row_info_flags); +} + +/* Write a single interlaced row. */ +static void +write_row_interlaced(png_structrp png_ptr, png_const_bytep row) +{ + const png_uint_32 row_number = png_ptr->row_number+1U; + const png_uint_32 height = png_ptr->height; + const unsigned int pass = png_ptr->pass; + const unsigned int row_info_flags = + (row_number == 1U ? png_pass_first_row : 0) | + (row_number == PNG_PASS_ROWS(height, pass) ? png_pass_last_row : 0) | + (pass == PNG_LAST_PASS(png_ptr->width, height) ? png_pass_last : 0); + +# ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Check that libpng is not doing the interlace: */ + debug(png_ptr->interlaced != PNG_INTERLACE_NONE && + !png_ptr->do_interlace); +# endif /* WRITE_INTERLACING */ + + write_row_core(png_ptr, row, row_info_flags); +} +#endif /* WRITE_TRANSFORMS */ + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED +/* Interlace a row then write it out. */ +static void +interlace_row(png_structrp png_ptr, png_const_bytep row) +{ + /* The row may not exist in the image (for this pass). */ + const png_uint_32 row_number = png_ptr->row_number; /* in image */ + const unsigned int pass = png_ptr->pass; + + if (png_ptr->width > PNG_PASS_START_COL(pass) && + PNG_ROW_IN_INTERLACE_PASS(row_number, pass)) + { + const unsigned int row_info_flags = + (row_number == PNG_PASS_START_ROW(pass) ? png_pass_first_row : 0) | + (PNG_LAST_PASS_ROW(row_number, pass, png_ptr->height) ? + png_pass_last_row : 0) | + (pass == PNG_LAST_PASS(png_ptr->width, png_ptr->height) ? + png_pass_last : 0); + + if (pass < 6) + { + /* Libpng is doing the interlacing and pixels need to be selected + * from the input row for this pass. + */ + /* row interlacing uses either the log bit depth for low bit + * depth input or the byte count for 8bpp or bigger pixels. + */ + const unsigned int input_depth = png_ptr->row_input_pixel_depth; + unsigned int B = 0; /* log2(input_depth) */ + + switch (input_depth) + { + case 4U: /* B will be 2 */ + ++B; + /*FALL THROUGH*/ + case 2U: /* B will be 1 */ + ++B; + /*FALL THROUGH*/ + case 1U: /* B will be 0 */ + write_row_buffered(png_ptr, row, row_info_flags, + interlace_row_lbd, B); + break; + + default: /* Parameter is the pixel size in bytes */ + write_row_buffered(png_ptr, row, row_info_flags, + interlace_row_byte, input_depth >> 3); + break; + } + } /* pass < 6 */ + + else /* pass 6: no interlacing required */ + write_row_core(png_ptr, row, row_info_flags); + } + + else + { + /* This code must advance row_number/pass itself; the row has been + * skipped. + */ + if (row_number+1U < png_ptr->height) + png_ptr->row_number = row_number+1U; + + else + { + png_ptr->row_number = 0U; + png_ptr->pass = 0x7U & (pass+1U); + } + } +} +#endif /* WRITE_INTERLACING */ + +/* Bottleneck API to actually write a number of rows, only exists because the + * rows parameter to png_write_rows is wrong. + */ +static void +png_write_rows_internal(png_structrp png_ptr, png_const_bytep *rows, + png_uint_32 num_rows) +{ + if (png_ptr != NULL && num_rows > 0U && rows != NULL) + { + /* Unlike the read code initialization happens automatically: */ + if (png_ptr->row_number == 0U && png_ptr->pass == 0U) + { + png_init_row_info(png_ptr); + +# ifdef PNG_WRITE_TRANSFORMS_SUPPORTED + /* If the app takes a png_info from a read operation and if the app has + * performed transforms on the data the png_info can contain IHDR + * information that cannot be represented in PNG. The code that writes + * the IHDR takes the color type from the png_info::format. The app + * adds transforms, before or after writing the IHDR, then the IHDR + * color_type stored in png_struct::color_type is used in + * png_init_row_info above to work out the actual row format. + * + * Prior to 1.7.0 this was not verified (there was no easy way to do + * so). Now we can check it here, however this is an: + * + * API CHANGE: in 1.7.0 an error may be flagged against bogus + * info_struct formats even though the app had removed them itself. + * It's just a warning at present. + * + * The test is that either the row_format produced by the write + * transforms exactly matches that in the original info_struct::format + * or that the info_struct::format was a simple mapping of the + * color_type that ended up in the IHDR: + */ + if (png_ptr->row_format != png_ptr->info_format && + PNG_FORMAT_FROM_COLOR_TYPE(png_ptr->color_type) != + png_ptr->info_format) + png_app_warning(png_ptr, "info_struct format does not match IHDR"); +# endif /* WRITE_TRANSFORMS */ + + /* Perform initialization required before IDATs are written. */ + png_write_start_IDAT(png_ptr); + } + + else if (png_ptr->pass >= 7U) /* too many calls; write already ended */ + { + debug(png_ptr->row_number == 0U); + png_app_error(png_ptr, "Too many calls to png_write_row"); + return; + } + + /* The remainder of these tests detect internal errors in libpng */ + else if (png_ptr->interlaced == PNG_INTERLACE_NONE) + affirm(png_ptr->row_number < png_ptr->height && png_ptr->pass == 0U); + +# ifdef PNG_WRITE_INTERLACING_SUPPORTED + else if (png_ptr->do_interlace) + affirm(png_ptr->row_number < png_ptr->height); +# endif /* WRITE_INTERLACING */ + + else /* app does interlace */ + affirm( + PNG_PASS_IN_IMAGE(png_ptr->width, png_ptr->height, png_ptr->pass) && + png_ptr->row_number < PNG_PASS_ROWS(png_ptr->height, png_ptr->pass) + ); + + /* First handle rows that require buffering because of the need to + * interlace them or the need to perform write transforms. + */ +# ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* libpng is doing the interlacing, but this only makes a difference to + * the first six passes (numbered, in libpng, 0..5); the seventh pass + * (numbered 6 by libpng) consists of complete image rows. + */ + if (png_ptr->do_interlace) while (num_rows > 0U && png_ptr->pass < 6) + interlace_row(png_ptr, *rows++), --num_rows; +# endif /* WRITE_INTERLACING */ + +# ifdef PNG_WRITE_TRANSFORMS_SUPPORTED + /* Transforms required however the row interlacing has already been + * handled and we have a complete (PNG) row. + */ + if (png_ptr->transform_list != NULL) + { + if (png_ptr->interlaced == PNG_INTERLACE_NONE) + while (num_rows > 0U) + write_row_non_interlaced(png_ptr, *rows++), --num_rows; + +# ifdef PNG_WRITE_INTERLACING_SUPPORTED + else if (png_ptr->do_interlace) + while (num_rows > 0U) + interlace_row(png_ptr, *rows++), --num_rows; +# endif /* WRITE_INTERLACING */ + + else /* app does the interlacing */ + while (num_rows > 0U) + write_row_interlaced(png_ptr, *rows++), --num_rows; + } +# endif /* WRITE_TRANSFORMS */ + + /* Finally handle any remaining rows that require no (libpng) interlace + * and no transforms. + */ + if (num_rows > 0U) + png_write_png_rows(png_ptr, rows, num_rows); + + /* Repeat the checks above, but allow for end-of-image. */ + if (png_ptr->pass < 7U) + { + if (png_ptr->interlaced == PNG_INTERLACE_NONE) + affirm(png_ptr->row_number < png_ptr->height && + png_ptr->pass == 0U); + +# ifdef PNG_WRITE_INTERLACING_SUPPORTED + else if (png_ptr->do_interlace) + affirm(png_ptr->row_number < png_ptr->height); +# endif /* WRITE_INTERLACING */ + + else /* app does interlace */ + affirm(PNG_PASS_IN_IMAGE(png_ptr->width, png_ptr->height, + png_ptr->pass) && + png_ptr->row_number < + PNG_PASS_ROWS(png_ptr->height, png_ptr->pass)); + } + } /* png_ptr, rows, num_rows all valid */ + + else if (png_ptr != NULL) + png_app_warning(png_ptr, "Missing rows to row write API"); +} + +/* ROW WRITE APIs */ +/* Called by user to write a single row of image data */ +void PNGAPI +png_write_row(png_structrp png_ptr, png_const_bytep row) +{ + png_debug(1, "in png_write_row"); + png_write_rows_internal(png_ptr, &row, 1U); +} + /* Write a few rows of image data. If the image is interlaced, * either you will have to write the 7 sub images, or, if you * have called png_set_interlace_handling(), you will have to * "write" the image seven times. */ void PNGAPI -png_write_rows(png_structrp png_ptr, png_bytepp row, - png_uint_32 num_rows) +png_write_rows(png_structrp png_ptr, png_bytepp rows, png_uint_32 num_rows) { - png_uint_32 i; /* row counter */ - png_bytepp rp; /* row pointer */ - png_debug(1, "in png_write_rows"); - if (png_ptr == NULL) - return; - - /* Loop through the rows */ - for (i = 0, rp = row; i < num_rows; i++, rp++) - { - png_write_row(png_ptr, *rp); - } + if (png_ptr != NULL) + png_write_rows_internal(png_ptr, png_constcast(png_const_bytep*,rows), + num_rows); } /* Write the image. You only need to call this function once, even @@ -590,342 +1051,31 @@ png_write_rows(png_structrp png_ptr, png_bytepp row, void PNGAPI png_write_image(png_structrp png_ptr, png_bytepp image) { - png_uint_32 i; /* row index */ - int pass, num_pass; /* pass variables */ - png_bytepp rp; /* points to current row */ - - if (png_ptr == NULL) - return; - png_debug(1, "in png_write_image"); -#ifdef PNG_WRITE_INTERLACING_SUPPORTED - /* Initialize interlace handling. If image is not interlaced, - * this will set pass to 1 - */ - num_pass = png_set_interlace_handling(png_ptr); -#else - num_pass = 1; -#endif - /* Loop through passes */ - for (pass = 0; pass < num_pass; pass++) + if (png_ptr != NULL) { - /* Loop through image */ - for (i = 0, rp = image; i < png_ptr->height; i++, rp++) + int num_pass = 1; + + /* The image is always an non-interlaced image. To write it as interlaced + * interlace handling must be present: + */ + if (png_ptr->interlaced) { - png_write_row(png_ptr, *rp); - } - } -} - -#ifdef PNG_MNG_FEATURES_SUPPORTED -/* Performs intrapixel differencing */ -static void -png_do_write_intrapixel(png_row_infop row_info, png_bytep row) -{ - png_debug(1, "in png_do_write_intrapixel"); - - if ((row_info->color_type & PNG_COLOR_MASK_COLOR)) - { - int bytes_per_pixel; - png_uint_32 row_width = row_info->width; - if (row_info->bit_depth == 8) - { - png_bytep rp; - png_uint_32 i; - - if (row_info->color_type == PNG_COLOR_TYPE_RGB) - bytes_per_pixel = 3; - - else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - bytes_per_pixel = 4; - - else - return; - - for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) - { - *(rp) = (png_byte)((*rp - *(rp + 1)) & 0xff); - *(rp + 2) = (png_byte)((*(rp + 2) - *(rp + 1)) & 0xff); - } +# ifdef PNG_WRITE_INTERLACING_SUPPORTED + num_pass = png_set_interlace_handling(png_ptr); +# else /* !WRITE_INTERLACING */ + /* There is no recovery because the IHDR has already been written. + */ + png_error(png_ptr, "No interlace support"); +# endif /* !WRITE_INTERLACING */ } -#ifdef PNG_WRITE_16BIT_SUPPORTED - else if (row_info->bit_depth == 16) - { - png_bytep rp; - png_uint_32 i; - - if (row_info->color_type == PNG_COLOR_TYPE_RGB) - bytes_per_pixel = 6; - - else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - bytes_per_pixel = 8; - - else - return; - - for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) - { - png_uint_32 s0 = (*(rp ) << 8) | *(rp + 1); - png_uint_32 s1 = (*(rp + 2) << 8) | *(rp + 3); - png_uint_32 s2 = (*(rp + 4) << 8) | *(rp + 5); - png_uint_32 red = (png_uint_32)((s0 - s1) & 0xffffL); - png_uint_32 blue = (png_uint_32)((s2 - s1) & 0xffffL); - *(rp ) = (png_byte)((red >> 8) & 0xff); - *(rp + 1) = (png_byte)(red & 0xff); - *(rp + 4) = (png_byte)((blue >> 8) & 0xff); - *(rp + 5) = (png_byte)(blue & 0xff); - } - } -#endif /* PNG_WRITE_16BIT_SUPPORTED */ + /* And write the whole thing, 7 times if interlacing it: */ + for (; num_pass > 0; --num_pass) + png_write_rows(png_ptr, image, png_ptr->height); } } -#endif /* PNG_MNG_FEATURES_SUPPORTED */ - -/* Called by user to write a row of image data */ -void PNGAPI -png_write_row(png_structrp png_ptr, png_const_bytep row) -{ - /* 1.5.6: moved from png_struct to be a local structure: */ - png_row_info row_info; - - if (png_ptr == NULL) - return; - - png_debug2(1, "in png_write_row (row %u, pass %d)", - png_ptr->row_number, png_ptr->pass); - - /* Initialize transformations and other stuff if first time */ - if (png_ptr->row_number == 0 && png_ptr->pass == 0) - { - /* Make sure we wrote the header info */ - if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) - png_error(png_ptr, - "png_write_info was never called before png_write_row"); - - /* Check for transforms that have been set but were defined out */ -#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED) - if (png_ptr->transformations & PNG_INVERT_MONO) - png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined"); -#endif - -#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED) - if (png_ptr->transformations & PNG_FILLER) - png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined"); -#endif -#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \ - defined(PNG_READ_PACKSWAP_SUPPORTED) - if (png_ptr->transformations & PNG_PACKSWAP) - png_warning(png_ptr, - "PNG_WRITE_PACKSWAP_SUPPORTED is not defined"); -#endif - -#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED) - if (png_ptr->transformations & PNG_PACK) - png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined"); -#endif - -#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED) - if (png_ptr->transformations & PNG_SHIFT) - png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined"); -#endif - -#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED) - if (png_ptr->transformations & PNG_BGR) - png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined"); -#endif - -#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED) - if (png_ptr->transformations & PNG_SWAP_BYTES) - png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined"); -#endif - - png_write_start_row(png_ptr); - } - -#ifdef PNG_WRITE_INTERLACING_SUPPORTED - /* If interlaced and not interested in row, return */ - if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) - { - switch (png_ptr->pass) - { - case 0: - if (png_ptr->row_number & 0x07) - { - png_write_finish_row(png_ptr); - return; - } - break; - - case 1: - if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) - { - png_write_finish_row(png_ptr); - return; - } - break; - - case 2: - if ((png_ptr->row_number & 0x07) != 4) - { - png_write_finish_row(png_ptr); - return; - } - break; - - case 3: - if ((png_ptr->row_number & 0x03) || png_ptr->width < 3) - { - png_write_finish_row(png_ptr); - return; - } - break; - - case 4: - if ((png_ptr->row_number & 0x03) != 2) - { - png_write_finish_row(png_ptr); - return; - } - break; - - case 5: - if ((png_ptr->row_number & 0x01) || png_ptr->width < 2) - { - png_write_finish_row(png_ptr); - return; - } - break; - - case 6: - if (!(png_ptr->row_number & 0x01)) - { - png_write_finish_row(png_ptr); - return; - } - break; - - default: /* error: ignore it */ - break; - } - } -#endif - - /* Set up row info for transformations */ - row_info.color_type = png_ptr->color_type; - row_info.width = png_ptr->usr_width; - row_info.channels = png_ptr->usr_channels; - row_info.bit_depth = png_ptr->usr_bit_depth; - row_info.pixel_depth = (png_byte)(row_info.bit_depth * row_info.channels); - row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width); - - png_debug1(3, "row_info->color_type = %d", row_info.color_type); - png_debug1(3, "row_info->width = %u", row_info.width); - png_debug1(3, "row_info->channels = %d", row_info.channels); - png_debug1(3, "row_info->bit_depth = %d", row_info.bit_depth); - png_debug1(3, "row_info->pixel_depth = %d", row_info.pixel_depth); - png_debug1(3, "row_info->rowbytes = %lu", (unsigned long)row_info.rowbytes); - - /* Copy user's row into buffer, leaving room for filter byte. */ - memcpy(png_ptr->row_buf + 1, row, row_info.rowbytes); - -#ifdef PNG_WRITE_INTERLACING_SUPPORTED - /* Handle interlacing */ - if (png_ptr->interlaced && png_ptr->pass < 6 && - (png_ptr->transformations & PNG_INTERLACE)) - { - png_do_write_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass); - /* This should always get caught above, but still ... */ - if (row_info.width == 0) - { - png_write_finish_row(png_ptr); - return; - } - } -#endif - -#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED - /* Handle other transformations */ - if (png_ptr->transformations) - png_do_write_transformations(png_ptr, &row_info); -#endif - - /* At this point the row_info pixel depth must match the 'transformed' depth, - * which is also the output depth. - */ - if (row_info.pixel_depth != png_ptr->pixel_depth || - row_info.pixel_depth != png_ptr->transformed_pixel_depth) - png_error(png_ptr, "internal write transform logic error"); - -#ifdef PNG_MNG_FEATURES_SUPPORTED - /* Write filter_method 64 (intrapixel differencing) only if - * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and - * 2. Libpng did not write a PNG signature (this filter_method is only - * used in PNG datastreams that are embedded in MNG datastreams) and - * 3. The application called png_permit_mng_features with a mask that - * included PNG_FLAG_MNG_FILTER_64 and - * 4. The filter_method is 64 and - * 5. The color_type is RGB or RGBA - */ - if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && - (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) - { - /* Intrapixel differencing */ - png_do_write_intrapixel(&row_info, png_ptr->row_buf + 1); - } -#endif - -/* Added at libpng-1.5.10 */ -#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED - /* Check for out-of-range palette index */ - if (row_info.color_type == PNG_COLOR_TYPE_PALETTE && - png_ptr->num_palette_max >= 0) - png_do_check_palette_indexes(png_ptr, &row_info); -#endif - - /* Find a filter if necessary, filter the row and write it out. */ - png_write_find_filter(png_ptr, &row_info); - - if (png_ptr->write_row_fn != NULL) - (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); -} - -#ifdef PNG_WRITE_FLUSH_SUPPORTED -/* Set the automatic flush interval or 0 to turn flushing off */ -void PNGAPI -png_set_flush(png_structrp png_ptr, int nrows) -{ - png_debug(1, "in png_set_flush"); - - if (png_ptr == NULL) - return; - - png_ptr->flush_dist = (nrows < 0 ? 0 : nrows); -} - -/* Flush the current output buffers now */ -void PNGAPI -png_write_flush(png_structrp png_ptr) -{ - png_debug(1, "in png_write_flush"); - - if (png_ptr == NULL) - return; - - /* We have already written out all of the data */ - if (png_ptr->row_number >= png_ptr->num_rows) - return; - - png_compress_IDAT(png_ptr, NULL, 0, Z_SYNC_FLUSH); - png_ptr->flush_rows = 0; - png_flush(png_ptr); -} -#endif /* PNG_WRITE_FLUSH_SUPPORTED */ - -#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED -static void png_reset_filter_heuristics(png_structrp png_ptr);/* forward decl */ -#endif /* Free any memory used in png_ptr struct without freeing the struct itself. */ static void @@ -933,30 +1083,15 @@ png_write_destroy(png_structrp png_ptr) { png_debug(1, "in png_write_destroy"); - /* Free any memory zlib uses */ - if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) - deflateEnd(&png_ptr->zstream); + png_deflate_destroy(png_ptr); - /* Free our memory. png_free checks NULL for us. */ - png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list); - png_free(png_ptr, png_ptr->row_buf); -#ifdef PNG_WRITE_FILTER_SUPPORTED - png_free(png_ptr, png_ptr->prev_row); - png_free(png_ptr, png_ptr->sub_row); - png_free(png_ptr, png_ptr->up_row); - png_free(png_ptr, png_ptr->avg_row); - png_free(png_ptr, png_ptr->paeth_row); -#endif - -#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED - /* Use this to save a little code space, it doesn't free the filter_costs */ - png_reset_filter_heuristics(png_ptr); - png_free(png_ptr, png_ptr->filter_costs); - png_free(png_ptr, png_ptr->inv_filter_costs); +#ifdef PNG_TRANSFORM_MECH_SUPPORTED + png_transform_free(png_ptr, &png_ptr->transform_list); #endif #ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list = NULL; #endif /* The error handling and memory handling information is left intact at this @@ -992,518 +1127,6 @@ png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr) } } -/* Allow the application to select one or more row filters to use. */ -void PNGAPI -png_set_filter(png_structrp png_ptr, int method, int filters) -{ - png_debug(1, "in png_set_filter"); - - if (png_ptr == NULL) - return; - -#ifdef PNG_MNG_FEATURES_SUPPORTED - if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && - (method == PNG_INTRAPIXEL_DIFFERENCING)) - method = PNG_FILTER_TYPE_BASE; -#endif - - /* The only supported method, except for the check above, is - * PNG_FILTER_TYPE_BASE. The code below does not use 'method' other than - * for the check, so just keep going if png_app_error returns. - */ - if (method != PNG_FILTER_TYPE_BASE) - png_app_error(png_ptr, "Unknown custom filter method"); - - /* If filter writing is not supported the 'filters' value must be zero, - * otherwise the value must be a single, valid, filter value or a set of the - * mask values. The defines in png.h are such that the filter masks used in - * this API and internally are 1<<(3+value), value is in the range 0..4, so - * this fits in a byte. - */ -# ifdef PNG_WRITE_FILTER_SUPPORTED - /* Notice that PNG_NO_FILTERS is 0 and passes this test; this is OK - * because filters then gets set to PNG_FILTER_NONE, as is required. - */ - if (filters < PNG_FILTER_VALUE_LAST) - filters = 0x08 << filters; - - else if ((filters & ~PNG_ALL_FILTERS) != 0) - { - png_app_error(png_ptr, "png_set_filter: invalid filters mask/value"); - - /* For compatibility with the previous behavior assume a mask value was - * passed and ignore the non-mask bits. - */ - filters &= PNG_ALL_FILTERS; - - /* For a possibly foolish consistency (it shouldn't matter) set - * PNG_FILTER_NONE rather than 0. - */ - if (filters == 0) - filters = PNG_FILTER_NONE; - } -# else - /* PNG_FILTER_VALUE_NONE and PNG_NO_FILTERS are both 0. */ - if (filters != 0 && filters != PNG_FILTER_NONE) - png_app_error(png_ptr, "png_set_filter: no filters supported"); - - filters = PNG_FILTER_NONE; -# endif - -# ifdef PNG_WRITE_FILTER_SUPPORTED - /* If we have allocated the row_buf, this means we have already started - * with the image and we should have allocated all of the filter buffers - * that have been selected. If prev_row isn't already allocated, then - * it is too late to start using the filters that need it, since we - * will be missing the data in the previous row. If an application - * wants to start and stop using particular filters during compression, - * it should start out with all of the filters, and then add and - * remove them after the start of compression. - * - * NOTE: this is a nasty constraint on the code, because it means that the - * prev_row buffer must be maintained even if there are currently no - * 'prev_row' requiring filters active. - */ - if (png_ptr->row_buf != NULL) - { - /* Repeat the checks in png_write_start_row; 1 pixel high or wide - * images cannot benefit from certain filters. If this isn't done here - * the check below will fire on 1 pixel high images. - */ - if (png_ptr->height == 1) - filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH); - - if (png_ptr->width == 1) - filters &= ~(PNG_FILTER_SUB|PNG_FILTER_AVG|PNG_FILTER_PAETH); - - if ((filters & (PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH)) != 0 - && png_ptr->prev_row == NULL) - { - /* This is the error case, however it is benign - the previous row - * is not available so the filter can't be used. Just warn here. - */ - png_app_warning(png_ptr, - "png_set_filter: UP/AVG/PAETH cannot be added after start"); - filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH); - } - - /* Allocate any required buffers that have not already been allocated. - */ - png_write_alloc_filter_row_buffers(png_ptr, filters); - } -# endif /* PNG_WRITE_FILTER_SUPPORTED */ - - /* Finally store the value. - * TODO: this field could probably be removed if neither READ nor - * WRITE_FILTER are supported. - */ - png_ptr->do_filter = (png_byte)filters; /* SAFE: checked above */ -} - -/* This allows us to influence the way in which libpng chooses the "best" - * filter for the current scanline. While the "minimum-sum-of-absolute- - * differences metric is relatively fast and effective, there is some - * question as to whether it can be improved upon by trying to keep the - * filtered data going to zlib more consistent, hopefully resulting in - * better compression. - */ -#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* GRR 970116 */ -/* Convenience reset API. */ -static void -png_reset_filter_heuristics(png_structrp png_ptr) -{ - /* Clear out any old values in the 'weights' - this must be done because if - * the app calls set_filter_heuristics multiple times with different - * 'num_weights' values we would otherwise potentially have wrong sized - * arrays. - */ - png_ptr->num_prev_filters = 0; - png_ptr->heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED; - if (png_ptr->prev_filters != NULL) - { - png_bytep old = png_ptr->prev_filters; - png_ptr->prev_filters = NULL; - png_free(png_ptr, old); - } - if (png_ptr->filter_weights != NULL) - { - png_uint_16p old = png_ptr->filter_weights; - png_ptr->filter_weights = NULL; - png_free(png_ptr, old); - } - - if (png_ptr->inv_filter_weights != NULL) - { - png_uint_16p old = png_ptr->inv_filter_weights; - png_ptr->inv_filter_weights = NULL; - png_free(png_ptr, old); - } - - /* Leave the filter_costs - this array is fixed size. */ -} - -static int -png_init_filter_heuristics(png_structrp png_ptr, int heuristic_method, - int num_weights) -{ - if (png_ptr == NULL) - return 0; - - /* Clear out the arrays */ - png_reset_filter_heuristics(png_ptr); - - /* Check arguments; the 'reset' function makes the correct settings for the - * unweighted case, but we must handle the weight case by initializing the - * arrays for the caller. - */ - if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) - { - int i; - - if (num_weights > 0) - { - png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr, - (png_uint_32)((sizeof (png_byte)) * num_weights)); - - /* To make sure that the weighting starts out fairly */ - for (i = 0; i < num_weights; i++) - { - png_ptr->prev_filters[i] = 255; - } - - png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr, - (png_uint_32)((sizeof (png_uint_16)) * num_weights)); - - png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr, - (png_uint_32)((sizeof (png_uint_16)) * num_weights)); - - for (i = 0; i < num_weights; i++) - { - png_ptr->inv_filter_weights[i] = - png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; - } - - /* Safe to set this now */ - png_ptr->num_prev_filters = (png_byte)num_weights; - } - - /* If, in the future, there are other filter methods, this would - * need to be based on png_ptr->filter. - */ - if (png_ptr->filter_costs == NULL) - { - png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr, - (png_uint_32)((sizeof (png_uint_16)) * PNG_FILTER_VALUE_LAST)); - - png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr, - (png_uint_32)((sizeof (png_uint_16)) * PNG_FILTER_VALUE_LAST)); - } - - for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) - { - png_ptr->inv_filter_costs[i] = - png_ptr->filter_costs[i] = PNG_COST_FACTOR; - } - - /* All the arrays are inited, safe to set this: */ - png_ptr->heuristic_method = PNG_FILTER_HEURISTIC_WEIGHTED; - - /* Return the 'ok' code. */ - return 1; - } - else if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT || - heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED) - { - return 1; - } - else - { - png_warning(png_ptr, "Unknown filter heuristic method"); - return 0; - } -} - -/* Provide floating and fixed point APIs */ -#ifdef PNG_FLOATING_POINT_SUPPORTED -void PNGAPI -png_set_filter_heuristics(png_structrp png_ptr, int heuristic_method, - int num_weights, png_const_doublep filter_weights, - png_const_doublep filter_costs) -{ - png_debug(1, "in png_set_filter_heuristics"); - - /* The internal API allocates all the arrays and ensures that the elements of - * those arrays are set to the default value. - */ - if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights)) - return; - - /* If using the weighted method copy in the weights. */ - if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) - { - int i; - for (i = 0; i < num_weights; i++) - { - if (filter_weights[i] <= 0.0) - { - png_ptr->inv_filter_weights[i] = - png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; - } - - else - { - png_ptr->inv_filter_weights[i] = - (png_uint_16)(PNG_WEIGHT_FACTOR*filter_weights[i]+.5); - - png_ptr->filter_weights[i] = - (png_uint_16)(PNG_WEIGHT_FACTOR/filter_weights[i]+.5); - } - } - - /* Here is where we set the relative costs of the different filters. We - * should take the desired compression level into account when setting - * the costs, so that Paeth, for instance, has a high relative cost at low - * compression levels, while it has a lower relative cost at higher - * compression settings. The filter types are in order of increasing - * relative cost, so it would be possible to do this with an algorithm. - */ - for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) if (filter_costs[i] >= 1.0) - { - png_ptr->inv_filter_costs[i] = - (png_uint_16)(PNG_COST_FACTOR / filter_costs[i] + .5); - - png_ptr->filter_costs[i] = - (png_uint_16)(PNG_COST_FACTOR * filter_costs[i] + .5); - } - } -} -#endif /* FLOATING_POINT */ - -#ifdef PNG_FIXED_POINT_SUPPORTED -void PNGAPI -png_set_filter_heuristics_fixed(png_structrp png_ptr, int heuristic_method, - int num_weights, png_const_fixed_point_p filter_weights, - png_const_fixed_point_p filter_costs) -{ - png_debug(1, "in png_set_filter_heuristics_fixed"); - - /* The internal API allocates all the arrays and ensures that the elements of - * those arrays are set to the default value. - */ - if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights)) - return; - - /* If using the weighted method copy in the weights. */ - if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) - { - int i; - for (i = 0; i < num_weights; i++) - { - if (filter_weights[i] <= 0) - { - png_ptr->inv_filter_weights[i] = - png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; - } - - else - { - png_ptr->inv_filter_weights[i] = (png_uint_16) - ((PNG_WEIGHT_FACTOR*filter_weights[i]+PNG_FP_HALF)/PNG_FP_1); - - png_ptr->filter_weights[i] = (png_uint_16)((PNG_WEIGHT_FACTOR* - PNG_FP_1+(filter_weights[i]/2))/filter_weights[i]); - } - } - - /* Here is where we set the relative costs of the different filters. We - * should take the desired compression level into account when setting - * the costs, so that Paeth, for instance, has a high relative cost at low - * compression levels, while it has a lower relative cost at higher - * compression settings. The filter types are in order of increasing - * relative cost, so it would be possible to do this with an algorithm. - */ - for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) - if (filter_costs[i] >= PNG_FP_1) - { - png_uint_32 tmp; - - /* Use a 32 bit unsigned temporary here because otherwise the - * intermediate value will be a 32 bit *signed* integer (ANSI rules) - * and this will get the wrong answer on division. - */ - tmp = PNG_COST_FACTOR*PNG_FP_1 + (filter_costs[i]/2); - tmp /= filter_costs[i]; - - png_ptr->inv_filter_costs[i] = (png_uint_16)tmp; - - tmp = PNG_COST_FACTOR * filter_costs[i] + PNG_FP_HALF; - tmp /= PNG_FP_1; - - png_ptr->filter_costs[i] = (png_uint_16)tmp; - } - } -} -#endif /* FIXED_POINT */ -#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ - -void PNGAPI -png_set_compression_level(png_structrp png_ptr, int level) -{ - png_debug(1, "in png_set_compression_level"); - - if (png_ptr == NULL) - return; - - png_ptr->zlib_level = level; -} - -void PNGAPI -png_set_compression_mem_level(png_structrp png_ptr, int mem_level) -{ - png_debug(1, "in png_set_compression_mem_level"); - - if (png_ptr == NULL) - return; - - png_ptr->zlib_mem_level = mem_level; -} - -void PNGAPI -png_set_compression_strategy(png_structrp png_ptr, int strategy) -{ - png_debug(1, "in png_set_compression_strategy"); - - if (png_ptr == NULL) - return; - - /* The flag setting here prevents the libpng dynamic selection of strategy. - */ - png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY; - png_ptr->zlib_strategy = strategy; -} - -/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a - * smaller value of window_bits if it can do so safely. - */ -void PNGAPI -png_set_compression_window_bits(png_structrp png_ptr, int window_bits) -{ - if (png_ptr == NULL) - return; - - /* Prior to 1.6.0 this would warn but then set the window_bits value, this - * meant that negative window bits values could be selected which would cause - * libpng to write a non-standard PNG file with raw deflate or gzip - * compressed IDAT or ancillary chunks. Such files can be read and there is - * no warning on read, so this seems like a very bad idea. - */ - if (window_bits > 15) - { - png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); - window_bits = 15; - } - - else if (window_bits < 8) - { - png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); - window_bits = 8; - } - - png_ptr->zlib_window_bits = window_bits; -} - -void PNGAPI -png_set_compression_method(png_structrp png_ptr, int method) -{ - png_debug(1, "in png_set_compression_method"); - - if (png_ptr == NULL) - return; - - /* This would produce an invalid PNG file if it worked, but it doesn't and - * deflate will fault it, so it is harmless to just warn here. - */ - if (method != 8) - png_warning(png_ptr, "Only compression method 8 is supported by PNG"); - - png_ptr->zlib_method = method; -} - -/* The following were added to libpng-1.5.4 */ -#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED -void PNGAPI -png_set_text_compression_level(png_structrp png_ptr, int level) -{ - png_debug(1, "in png_set_text_compression_level"); - - if (png_ptr == NULL) - return; - - png_ptr->zlib_text_level = level; -} - -void PNGAPI -png_set_text_compression_mem_level(png_structrp png_ptr, int mem_level) -{ - png_debug(1, "in png_set_text_compression_mem_level"); - - if (png_ptr == NULL) - return; - - png_ptr->zlib_text_mem_level = mem_level; -} - -void PNGAPI -png_set_text_compression_strategy(png_structrp png_ptr, int strategy) -{ - png_debug(1, "in png_set_text_compression_strategy"); - - if (png_ptr == NULL) - return; - - png_ptr->zlib_text_strategy = strategy; -} - -/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a - * smaller value of window_bits if it can do so safely. - */ -void PNGAPI -png_set_text_compression_window_bits(png_structrp png_ptr, int window_bits) -{ - if (png_ptr == NULL) - return; - - if (window_bits > 15) - { - png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); - window_bits = 15; - } - - else if (window_bits < 8) - { - png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); - window_bits = 8; - } - - png_ptr->zlib_text_window_bits = window_bits; -} - -void PNGAPI -png_set_text_compression_method(png_structrp png_ptr, int method) -{ - png_debug(1, "in png_set_text_compression_method"); - - if (png_ptr == NULL) - return; - - if (method != 8) - png_warning(png_ptr, "Only compression method 8 is supported by PNG"); - - png_ptr->zlib_text_method = method; -} -#endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */ -/* end of API added to libpng-1.5.4 */ - void PNGAPI png_set_write_status_fn(png_structrp png_ptr, png_write_status_ptr write_row_fn) { @@ -1513,23 +1136,7 @@ png_set_write_status_fn(png_structrp png_ptr, png_write_status_ptr write_row_fn) png_ptr->write_row_fn = write_row_fn; } -#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED -void PNGAPI -png_set_write_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr - write_user_transform_fn) -{ - png_debug(1, "in png_set_write_user_transform_fn"); - - if (png_ptr == NULL) - return; - - png_ptr->transformations |= PNG_USER_TRANSFORM; - png_ptr->write_user_transform_fn = write_user_transform_fn; -} -#endif - - -#ifdef PNG_INFO_IMAGE_SUPPORTED +#ifdef PNG_WRITE_PNG_SUPPORTED void PNGAPI png_write_png(png_structrp png_ptr, png_inforp info_ptr, int transforms, voidp params) @@ -1549,7 +1156,7 @@ png_write_png(png_structrp png_ptr, png_inforp info_ptr, /* ------ these transformations don't touch the info structure ------- */ /* Invert monochrome pixels */ - if (transforms & PNG_TRANSFORM_INVERT_MONO) + if ((transforms & PNG_TRANSFORM_INVERT_MONO) != 0) #ifdef PNG_WRITE_INVERT_SUPPORTED png_set_invert_mono(png_ptr); #else @@ -1559,16 +1166,16 @@ png_write_png(png_structrp png_ptr, png_inforp info_ptr, /* Shift the pixels up to a legal bit depth and fill in * as appropriate to correctly scale the image. */ - if (transforms & PNG_TRANSFORM_SHIFT) + if ((transforms & PNG_TRANSFORM_SHIFT) != 0) #ifdef PNG_WRITE_SHIFT_SUPPORTED - if (info_ptr->valid & PNG_INFO_sBIT) + if ((info_ptr->valid & PNG_INFO_sBIT) != 0) png_set_shift(png_ptr, &info_ptr->sig_bit); #else png_app_error(png_ptr, "PNG_TRANSFORM_SHIFT not supported"); #endif /* Pack pixels into bytes */ - if (transforms & PNG_TRANSFORM_PACKING) + if ((transforms & PNG_TRANSFORM_PACKING) != 0) #ifdef PNG_WRITE_PACK_SUPPORTED png_set_packing(png_ptr); #else @@ -1576,7 +1183,7 @@ png_write_png(png_structrp png_ptr, png_inforp info_ptr, #endif /* Swap location of alpha bytes from ARGB to RGBA */ - if (transforms & PNG_TRANSFORM_SWAP_ALPHA) + if ((transforms & PNG_TRANSFORM_SWAP_ALPHA) != 0) #ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED png_set_swap_alpha(png_ptr); #else @@ -1587,21 +1194,21 @@ png_write_png(png_structrp png_ptr, png_inforp info_ptr, * RGB, note that the code expects the input color type to be G or RGB; no * alpha channel. */ - if (transforms & - (PNG_TRANSFORM_STRIP_FILLER_AFTER|PNG_TRANSFORM_STRIP_FILLER_BEFORE)) + if ((transforms & (PNG_TRANSFORM_STRIP_FILLER_AFTER| + PNG_TRANSFORM_STRIP_FILLER_BEFORE)) != 0) { #ifdef PNG_WRITE_FILLER_SUPPORTED - if (transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER) + if ((transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER) != 0) { - if (transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE) + if ((transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE) != 0) png_app_error(png_ptr, - "PNG_TRANSFORM_STRIP_FILLER: BEFORE+AFTER not supported"); + "PNG_TRANSFORM_STRIP_FILLER: BEFORE+AFTER not supported"); /* Continue if ignored - this is the pre-1.6.10 behavior */ png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); } - else if (transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE) + else if ((transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE) != 0) png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); #else png_app_error(png_ptr, "PNG_TRANSFORM_STRIP_FILLER not supported"); @@ -1609,7 +1216,7 @@ png_write_png(png_structrp png_ptr, png_inforp info_ptr, } /* Flip BGR pixels to RGB */ - if (transforms & PNG_TRANSFORM_BGR) + if ((transforms & PNG_TRANSFORM_BGR) != 0) #ifdef PNG_WRITE_BGR_SUPPORTED png_set_bgr(png_ptr); #else @@ -1617,7 +1224,7 @@ png_write_png(png_structrp png_ptr, png_inforp info_ptr, #endif /* Swap bytes of 16-bit files to most significant byte first */ - if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) + if ((transforms & PNG_TRANSFORM_SWAP_ENDIAN) != 0) #ifdef PNG_WRITE_SWAP_SUPPORTED png_set_swap(png_ptr); #else @@ -1625,7 +1232,7 @@ png_write_png(png_structrp png_ptr, png_inforp info_ptr, #endif /* Swap bits of 1, 2, 4 bit packed pixel formats */ - if (transforms & PNG_TRANSFORM_PACKSWAP) + if ((transforms & PNG_TRANSFORM_PACKSWAP) != 0) #ifdef PNG_WRITE_PACKSWAP_SUPPORTED png_set_packswap(png_ptr); #else @@ -1633,7 +1240,7 @@ png_write_png(png_structrp png_ptr, png_inforp info_ptr, #endif /* Invert the alpha channel from opacity to transparency */ - if (transforms & PNG_TRANSFORM_INVERT_ALPHA) + if ((transforms & PNG_TRANSFORM_INVERT_ALPHA) != 0) #ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED png_set_invert_alpha(png_ptr); #else @@ -1650,17 +1257,16 @@ png_write_png(png_structrp png_ptr, png_inforp info_ptr, PNG_UNUSED(params) } -#endif +#endif /* WRITE_PNG */ #ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED -#ifdef PNG_STDIO_SUPPORTED /* currently required for png_image_write_* */ /* Initialize the write structure - general purpose utility. */ static int png_image_write_init(png_imagep image) { png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, image, - png_safe_error, png_safe_warning); + png_safe_error, png_safe_warning); if (png_ptr != NULL) { @@ -1669,7 +1275,7 @@ png_image_write_init(png_imagep image) if (info_ptr != NULL) { png_controlp control = png_voidcast(png_controlp, - png_malloc_warn(png_ptr, (sizeof *control))); + png_malloc_warn(png_ptr, (sizeof *control))); if (control != NULL) { @@ -1699,13 +1305,17 @@ typedef struct /* Arguments: */ png_imagep image; png_const_voidp buffer; - png_int_32 row_stride; + ptrdiff_t row_stride; png_const_voidp colormap; int convert_to_8bit; /* Local variables: */ png_const_voidp first_row; ptrdiff_t row_bytes; png_voidp local_row; + /* Byte count for memory writing */ + png_bytep memory; + png_alloc_size_t memory_bytes; /* not used for STDIO */ + png_alloc_size_t output_bytes; /* running total */ } png_image_write_control; /* Write png_uint_16 input to a 16-bit PNG; the png_ptr has already been set to @@ -1716,22 +1326,22 @@ static int png_write_image_16bit(png_voidp argument) { png_image_write_control *display = png_voidcast(png_image_write_control*, - argument); + argument); png_imagep image = display->image; png_structrp png_ptr = image->opaque->png_ptr; png_const_uint_16p input_row = png_voidcast(png_const_uint_16p, - display->first_row); + display->first_row); png_uint_16p output_row = png_voidcast(png_uint_16p, display->local_row); png_uint_16p row_end; - const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1; + const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ? 3 : 1; int aindex = 0; png_uint_32 y = image->height; - if (image->format & PNG_FORMAT_FLAG_ALPHA) + if ((image->format & PNG_FORMAT_FLAG_ALPHA) != 0) { # ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED - if (image->format & PNG_FORMAT_FLAG_AFIRST) + if ((image->format & PNG_FORMAT_FLAG_AFIRST) != 0) { aindex = -1; ++input_row; /* To point to the first component */ @@ -1795,7 +1405,7 @@ png_write_image_16bit(png_voidp argument) { png_uint_32 calc = component * reciprocal; calc += 16384; /* round to nearest */ - component = (png_uint_16)(calc >> 15); + component = png_check_u16(png_ptr, calc >> 15); } *out_ptr++ = component; @@ -1825,8 +1435,8 @@ png_write_image_16bit(png_voidp argument) #define UNP_RECIPROCAL(alpha) ((((0xffff*0xff)<<7)+(alpha>>1))/alpha) static png_byte -png_unpremultiply(png_uint_32 component, png_uint_32 alpha, - png_uint_32 reciprocal/*from the above macro*/) +png_unpremultiply(png_const_structrp png_ptr, png_uint_32 component, + png_uint_32 alpha, png_uint_32 reciprocal/*from the above macro*/) { /* The following gives 1.0 for an alpha of 0, which is fine, otherwise if 0/0 * is represented as some other value there is more likely to be a @@ -1862,34 +1472,36 @@ png_unpremultiply(png_uint_32 component, png_uint_32 alpha, component *= 255; /* Convert the component to sRGB. */ - return (png_byte)PNG_sRGB_FROM_LINEAR(component); + return PNG_sRGB_FROM_LINEAR(png_ptr, component); } else return 0; + + PNG_UNUSEDRC(png_ptr) } static int png_write_image_8bit(png_voidp argument) { png_image_write_control *display = png_voidcast(png_image_write_control*, - argument); + argument); png_imagep image = display->image; png_structrp png_ptr = image->opaque->png_ptr; png_const_uint_16p input_row = png_voidcast(png_const_uint_16p, - display->first_row); + display->first_row); png_bytep output_row = png_voidcast(png_bytep, display->local_row); png_uint_32 y = image->height; - const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1; + const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ? 3 : 1; - if (image->format & PNG_FORMAT_FLAG_ALPHA) + if ((image->format & PNG_FORMAT_FLAG_ALPHA) != 0) { png_bytep row_end; int aindex; # ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED - if (image->format & PNG_FORMAT_FLAG_AFIRST) + if ((image->format & PNG_FORMAT_FLAG_AFIRST) != 0) { aindex = -1; ++input_row; /* To point to the first component */ @@ -1911,7 +1523,7 @@ png_write_image_8bit(png_voidp argument) while (out_ptr < row_end) { png_uint_16 alpha = in_ptr[aindex]; - png_byte alphabyte = (png_byte)PNG_DIV257(alpha); + png_byte alphabyte = png_check_byte(png_ptr, PNG_DIV257(alpha)); png_uint_32 reciprocal = 0; int c; @@ -1923,7 +1535,8 @@ png_write_image_8bit(png_voidp argument) c = channels; do /* always at least one channel */ - *out_ptr++ = png_unpremultiply(*in_ptr++, alpha, reciprocal); + *out_ptr++ = png_unpremultiply(png_ptr, *in_ptr++, alpha, + reciprocal); while (--c > 0); /* Skip to next component (skip the intervening alpha channel) */ @@ -1932,7 +1545,7 @@ png_write_image_8bit(png_voidp argument) } /* while out_ptr < row_end */ png_write_row(png_ptr, png_voidcast(png_const_bytep, - display->local_row)); + display->local_row)); input_row += display->row_bytes/(sizeof (png_uint_16)); } /* while y */ } @@ -1954,7 +1567,7 @@ png_write_image_8bit(png_voidp argument) png_uint_32 component = *in_ptr++; component *= 255; - *out_ptr++ = (png_byte)PNG_sRGB_FROM_LINEAR(component); + *out_ptr++ = PNG_sRGB_FROM_LINEAR(png_ptr, component); } png_write_row(png_ptr, output_row); @@ -1986,7 +1599,7 @@ png_image_set_PLTE(png_image_write_control *display) # endif # ifdef PNG_FORMAT_BGR_SUPPORTED - const int bgr = (format & PNG_FORMAT_FLAG_BGR) ? 2 : 0; + const int bgr = (format & PNG_FORMAT_FLAG_BGR) != 0 ? 2 : 0; # else # define bgr 0 # endif @@ -2003,33 +1616,35 @@ png_image_set_PLTE(png_image_write_control *display) /* This gets automatically converted to sRGB with reversal of the * pre-multiplication if the color-map has an alpha channel. */ - if (format & PNG_FORMAT_FLAG_LINEAR) + if ((format & PNG_FORMAT_FLAG_LINEAR) != 0) { png_const_uint_16p entry = png_voidcast(png_const_uint_16p, cmap); entry += i * channels; - if (channels & 1) /* no alpha */ + if ((channels & 1) != 0) /* no alpha */ { if (channels >= 3) /* RGB */ { - palette[i].blue = (png_byte)PNG_sRGB_FROM_LINEAR(255 * - entry[(2 ^ bgr)]); - palette[i].green = (png_byte)PNG_sRGB_FROM_LINEAR(255 * - entry[1]); - palette[i].red = (png_byte)PNG_sRGB_FROM_LINEAR(255 * - entry[bgr]); + palette[i].blue = PNG_sRGB_FROM_LINEAR( + display->image->opaque->png_ptr, 255 * entry[(2 ^ bgr)]); + palette[i].green = PNG_sRGB_FROM_LINEAR( + display->image->opaque->png_ptr, 255 * entry[1]); + palette[i].red = PNG_sRGB_FROM_LINEAR( + display->image->opaque->png_ptr, 255 * entry[bgr]); } else /* Gray */ palette[i].blue = palette[i].red = palette[i].green = - (png_byte)PNG_sRGB_FROM_LINEAR(255 * *entry); + PNG_sRGB_FROM_LINEAR(display->image->opaque->png_ptr, + 255 * *entry); } else /* alpha */ { png_uint_16 alpha = entry[afirst ? 0 : channels-1]; - png_byte alphabyte = (png_byte)PNG_DIV257(alpha); + png_byte alphabyte = png_check_byte( + display->image->opaque->png_ptr, PNG_DIV257(alpha)); png_uint_32 reciprocal = 0; /* Calculate a reciprocal, as in the png_write_image_8bit code above @@ -2045,17 +1660,21 @@ png_image_set_PLTE(png_image_write_control *display) if (channels >= 3) /* RGB */ { - palette[i].blue = png_unpremultiply(entry[afirst + (2 ^ bgr)], - alpha, reciprocal); - palette[i].green = png_unpremultiply(entry[afirst + 1], alpha, - reciprocal); - palette[i].red = png_unpremultiply(entry[afirst + bgr], alpha, - reciprocal); + palette[i].blue = png_unpremultiply( + display->image->opaque->png_ptr, entry[afirst + (2 ^ bgr)], + alpha, reciprocal); + palette[i].green = png_unpremultiply( + display->image->opaque->png_ptr, entry[afirst + 1], alpha, + reciprocal); + palette[i].red = png_unpremultiply( + display->image->opaque->png_ptr, entry[afirst + bgr], alpha, + reciprocal); } else /* gray */ palette[i].blue = palette[i].red = palette[i].green = - png_unpremultiply(entry[afirst], alpha, reciprocal); + png_unpremultiply(display->image->opaque->png_ptr, + entry[afirst], alpha, reciprocal); } } @@ -2102,11 +1721,11 @@ png_image_set_PLTE(png_image_write_control *display) # endif png_set_PLTE(image->opaque->png_ptr, image->opaque->info_ptr, palette, - entries); + entries); if (num_trans > 0) png_set_tRNS(image->opaque->png_ptr, image->opaque->info_ptr, tRNS, - num_trans, NULL); + num_trans, NULL); image->colormap_entries = entries; } @@ -2115,52 +1734,92 @@ static int png_image_write_main(png_voidp argument) { png_image_write_control *display = png_voidcast(png_image_write_control*, - argument); + argument); png_imagep image = display->image; png_structrp png_ptr = image->opaque->png_ptr; png_inforp info_ptr = image->opaque->info_ptr; png_uint_32 format = image->format; - int colormap = (format & PNG_FORMAT_FLAG_COLORMAP) != 0; - int linear = !colormap && (format & PNG_FORMAT_FLAG_LINEAR) != 0; /* input */ - int alpha = !colormap && (format & PNG_FORMAT_FLAG_ALPHA) != 0; - int write_16bit = linear && !colormap && !display->convert_to_8bit; + /* The following four ints are actually booleans */ + int colormap = (format & PNG_FORMAT_FLAG_COLORMAP); + int linear = !colormap && (format & PNG_FORMAT_FLAG_LINEAR); /* input */ + int alpha = !colormap && (format & PNG_FORMAT_FLAG_ALPHA); + int write_16bit = linear && !colormap && (display->convert_to_8bit == 0); # ifdef PNG_BENIGN_ERRORS_SUPPORTED /* Make sure we error out on any bad situation */ png_set_benign_errors(png_ptr, 0/*error*/); # endif - /* Default the 'row_stride' parameter if required. */ - if (display->row_stride == 0) - display->row_stride = PNG_IMAGE_ROW_STRIDE(*image); + /* Default the 'row_stride' parameter if required, also check the row stride + * and total image size to ensure that they are within the system limits. + */ + { + const unsigned int channels = PNG_IMAGE_PIXEL_CHANNELS(image->format); + + /* The test is slightly evil: it assumes that a signed pointer difference + * (ptrdiff_t) can hold a maximum value of half, rounded down, of the + * maximum of a (size_t). This is almost certain to be true. + */ + if (image->width <= (PNG_SIZE_MAX >> 1)/channels) /* no overflow */ + { + png_alloc_size_t check; + const png_alloc_size_t png_row_stride = + (png_alloc_size_t)/*SAFE*/image->width * channels; + + if (display->row_stride == 0) + display->row_stride = (ptrdiff_t)png_row_stride; + + if (display->row_stride < 0) + check = -display->row_stride; + + else + check = display->row_stride; + + if (check >= png_row_stride) + { + /* Now check for overflow of the image buffer calculation; check for + * (size_t) overflow here. This detects issues with the + * PNG_IMAGE_BUFFER_SIZE macro. + */ + if (image->height > PNG_SIZE_MAX/png_row_stride) + png_error(image->opaque->png_ptr, "memory image too large"); + } + + else + png_error(image->opaque->png_ptr, "supplied row stride too small"); + } + + else + png_error(image->opaque->png_ptr, "image row stride too large"); + } /* Set the required transforms then write the rows in the correct order. */ - if (format & PNG_FORMAT_FLAG_COLORMAP) + if ((format & PNG_FORMAT_FLAG_COLORMAP) != 0) { if (display->colormap != NULL && image->colormap_entries > 0) { png_uint_32 entries = image->colormap_entries; png_set_IHDR(png_ptr, info_ptr, image->width, image->height, - entries > 16 ? 8 : (entries > 4 ? 4 : (entries > 2 ? 2 : 1)), - PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + entries > 16 ? 8 : (entries > 4 ? 4 : (entries > 2 ? 2 : 1)), + PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_image_set_PLTE(display); } else png_error(image->opaque->png_ptr, - "no color-map for color-mapped image"); + "no color-map for color-mapped image"); } else png_set_IHDR(png_ptr, info_ptr, image->width, image->height, - write_16bit ? 16 : 8, - ((format & PNG_FORMAT_FLAG_COLOR) ? PNG_COLOR_MASK_COLOR : 0) + - ((format & PNG_FORMAT_FLAG_ALPHA) ? PNG_COLOR_MASK_ALPHA : 0), - PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + write_16bit ? 16 : 8, + ((format & PNG_FORMAT_FLAG_COLOR) ? PNG_COLOR_MASK_COLOR : 0) + + ((format & PNG_FORMAT_FLAG_ALPHA) ? PNG_COLOR_MASK_ALPHA : 0), + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); /* Counter-intuitively the data transformations must be called *after* * png_write_info, not before as in the read code, but the 'set' functions @@ -2173,17 +1832,17 @@ png_image_write_main(png_voidp argument) /* The gamma here is 1.0 (linear) and the cHRM chunk matches sRGB. */ png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_LINEAR); - if (!(image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB)) + if ((image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB) == 0) png_set_cHRM_fixed(png_ptr, info_ptr, - /* color x y */ - /* white */ 31270, 32900, - /* red */ 64000, 33000, - /* green */ 30000, 60000, - /* blue */ 15000, 6000 + /* color x y */ + /* white */ 31270, 32900, + /* red */ 64000, 33000, + /* green */ 30000, 60000, + /* blue */ 15000, 6000 ); } - else if (!(image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB)) + else if ((image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB) == 0) png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL); /* Else writing an 8-bit file and the *colors* aren't sRGB, but the 8-bit @@ -2204,36 +1863,36 @@ png_image_write_main(png_voidp argument) { PNG_CONST png_uint_16 le = 0x0001; - if (*(png_const_bytep)&le) + if ((*(png_const_bytep) & le) != 0) png_set_swap(png_ptr); } # ifdef PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED - if (format & PNG_FORMAT_FLAG_BGR) + if ((format & PNG_FORMAT_FLAG_BGR) != 0) { - if (!colormap && (format & PNG_FORMAT_FLAG_COLOR) != 0) + if (colormap == 0 && (format & PNG_FORMAT_FLAG_COLOR) != 0) png_set_bgr(png_ptr); - format &= ~PNG_FORMAT_FLAG_BGR; + format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_BGR); } # endif # ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED - if (format & PNG_FORMAT_FLAG_AFIRST) + if ((format & PNG_FORMAT_FLAG_AFIRST) != 0) { - if (!colormap && (format & PNG_FORMAT_FLAG_ALPHA) != 0) + if (colormap == 0 && (format & PNG_FORMAT_FLAG_ALPHA) != 0) png_set_swap_alpha(png_ptr); - format &= ~PNG_FORMAT_FLAG_AFIRST; + format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_AFIRST); } # endif /* If there are 16 or fewer color-map entries we wrote a lower bit depth * above, but the application data is still byte packed. */ - if (colormap && image->colormap_entries <= 16) + if (colormap != 0 && image->colormap_entries <= 16) png_set_packing(png_ptr); /* That should have handled all (both) the transforms. */ - if ((format & ~(png_uint_32)(PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_LINEAR | + if ((format & PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_LINEAR | PNG_FORMAT_FLAG_ALPHA | PNG_FORMAT_FLAG_COLORMAP)) != 0) png_error(png_ptr, "png_write_image: unsupported transformation"); @@ -2251,26 +1910,22 @@ png_image_write_main(png_voidp argument) display->row_bytes = row_bytes; } - /* Apply 'fast' options if the flag is set. */ - if ((image->flags & PNG_IMAGE_FLAG_FAST) != 0) - { - png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_NO_FILTERS); - /* NOTE: determined by experiment using pngstest, this reflects some - * balance between the time to write the image once and the time to read - * it about 50 times. The speed-up in pngstest was about 10-20% of the - * total (user) time on a heavily loaded system. - */ - png_set_compression_level(png_ptr, 3); - } + /* Select the right compression mode based on the presence or absence of the + * 'fast' flag. This will use whatever options are available in the libpng + * build. It is always supported. + */ + png_set_compression(png_ptr, (image->flags & PNG_IMAGE_FLAG_FAST) != 0 ? + PNG_COMPRESSION_HIGH_SPEED : PNG_COMPRESSION_HIGH); /* Check for the cases that currently require a pre-transform on the row * before it is written. This only applies when the input is 16-bit and * either there is an alpha channel or it is converted to 8-bit. */ - if ((linear && alpha) || (!colormap && display->convert_to_8bit)) + if ((linear != 0 && alpha != 0 ) || + (colormap == 0 && display->convert_to_8bit != 0)) { png_bytep row = png_voidcast(png_bytep, png_malloc(png_ptr, - png_get_rowbytes(png_ptr, info_ptr))); + png_get_rowbytes(png_ptr, info_ptr))); int result; display->local_row = row; @@ -2307,25 +1962,136 @@ png_image_write_main(png_voidp argument) return 1; } -int PNGAPI -png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit, - const void *buffer, png_int_32 row_stride, const void *colormap) + +static void (PNGCBAPI +image_memory_write)(png_structp png_ptr, png_bytep/*const*/ data, + png_size_t size) { - /* Write the image to the given (FILE*). */ + png_image_write_control *display = png_voidcast(png_image_write_control*, + png_ptr->io_ptr/*backdoor: png_get_io_ptr(png_ptr)*/); + const png_alloc_size_t ob = display->output_bytes; + + /* Check for overflow; this should never happen: */ + if (size <= ((png_alloc_size_t)-1) - ob) + { + /* I don't think libpng ever does this, but just in case: */ + if (size > 0) + { + if (display->memory_bytes >= ob+size) /* writing */ + memcpy(display->memory+ob, data, size); + + /* Always update the size: */ + display->output_bytes = ob+size; + } + } + + else + png_error(png_ptr, "png_image_write_to_memory: PNG too big"); +} + +static void (PNGCBAPI +image_memory_flush)(png_structp png_ptr) +{ + PNG_UNUSED(png_ptr) +} + +static int +png_image_write_memory(png_voidp argument) +{ + png_image_write_control *display = png_voidcast(png_image_write_control*, + argument); + + /* The rest of the memory-specific init and write_main in an error protected + * environment. This case needs to use callbacks for the write operations + * since libpng has no built in support for writing to memory. + */ + png_set_write_fn(display->image->opaque->png_ptr, display/*io_ptr*/, + image_memory_write, image_memory_flush); + + return png_image_write_main(display); +} + +int PNGAPI +png_image_write_to_memory(png_imagep image, void *memory, + png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8bit, + const void *buffer, ptrdiff_t row_stride, const void *colormap) +{ + /* Write the image to the given buffer, or count the bytes if it is NULL */ if (image != NULL && image->version == PNG_IMAGE_VERSION) { - if (file != NULL) + if (memory_bytes != NULL && buffer != NULL) { - if (png_image_write_init(image)) + /* This is to give the caller an easier error detection in the NULL + * case and guard against uninitialized variable problems: + */ + if (memory == NULL) + *memory_bytes = 0; + + if (png_image_write_init(image) != 0) { png_image_write_control display; int result; - /* This is slightly evil, but png_init_io doesn't do anything other - * than this and we haven't changed the standard IO functions so - * this saves a 'safe' function. - */ - image->opaque->png_ptr->io_ptr = file; + memset(&display, 0, (sizeof display)); + display.image = image; + display.buffer = buffer; + display.row_stride = row_stride; + display.colormap = colormap; + display.convert_to_8bit = convert_to_8bit; + display.memory = png_voidcast(png_bytep, memory); + display.memory_bytes = *memory_bytes; + display.output_bytes = 0; + + result = png_safe_execute(image, png_image_write_memory, &display); + png_image_free(image); + + /* write_memory returns true even if we ran out of buffer. */ + if (result) + { + /* On out-of-buffer this function returns '0' but still updates + * memory_bytes: + */ + if (memory != NULL && display.output_bytes > *memory_bytes) + result = 0; + + *memory_bytes = display.output_bytes; + } + + return result; + } + + else + return 0; + } + + else + return png_image_error(image, + "png_image_write_to_memory: invalid argument"); + } + + else if (image != NULL) + return png_image_error(image, + "png_image_write_to_memory: incorrect PNG_IMAGE_VERSION"); + + else + return 0; +} + +#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +int PNGAPI +png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit, + const void *buffer, ptrdiff_t row_stride, const void *colormap) +{ + /* Write the image to the given (FILE*). */ + if (image != NULL && image->version == PNG_IMAGE_VERSION) + { + if (file != NULL && buffer != NULL) + { + if (png_image_write_init(image) != 0 && + png_image_init_io(image, file) != 0) + { + png_image_write_control display; + int result; memset(&display, 0, (sizeof display)); display.image = image; @@ -2345,12 +2111,12 @@ png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit, else return png_image_error(image, - "png_image_write_to_stdio: invalid argument"); + "png_image_write_to_stdio: invalid argument"); } else if (image != NULL) return png_image_error(image, - "png_image_write_to_stdio: incorrect PNG_IMAGE_VERSION"); + "png_image_write_to_stdio: incorrect PNG_IMAGE_VERSION"); else return 0; @@ -2358,20 +2124,20 @@ png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit, int PNGAPI png_image_write_to_file(png_imagep image, const char *file_name, - int convert_to_8bit, const void *buffer, png_int_32 row_stride, - const void *colormap) + int convert_to_8bit, const void *buffer, ptrdiff_t row_stride, + const void *colormap) { /* Write the image to the named file. */ if (image != NULL && image->version == PNG_IMAGE_VERSION) { - if (file_name != NULL) + if (file_name != NULL && buffer != NULL) { FILE *fp = fopen(file_name, "wb"); if (fp != NULL) { if (png_image_write_to_stdio(image, fp, convert_to_8bit, buffer, - row_stride, colormap)) + row_stride, colormap) != 0) { int error; /* from fflush/fclose */ @@ -2412,16 +2178,16 @@ png_image_write_to_file(png_imagep image, const char *file_name, else return png_image_error(image, - "png_image_write_to_file: invalid argument"); + "png_image_write_to_file: invalid argument"); } else if (image != NULL) return png_image_error(image, - "png_image_write_to_file: incorrect PNG_IMAGE_VERSION"); + "png_image_write_to_file: incorrect PNG_IMAGE_VERSION"); else return 0; } -#endif /* PNG_STDIO_SUPPORTED */ +#endif /* SIMPLIFIED_WRITE_STDIO */ #endif /* SIMPLIFIED_WRITE */ -#endif /* PNG_WRITE_SUPPORTED */ +#endif /* WRITE */ diff --git a/ext/libpng17/pngwtran.c b/ext/libpng17/pngwtran.c index 215bd68a9d..3aaf2205e0 100644 --- a/ext/libpng17/pngwtran.c +++ b/ext/libpng17/pngwtran.c @@ -1,8 +1,8 @@ /* pngwtran.c - transforms the data in a row for PNG writers * - * Last changed in libpng 1.6.9 [February 6, 2014] - * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * Last changed in libpng 1.7.0 [(PENDING RELEASE)] + * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -12,561 +12,137 @@ */ #include "pngpriv.h" - -#ifdef PNG_WRITE_SUPPORTED -#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED +#define PNG_SRC_FILE PNG_SRC_FILE_pngwtran #ifdef PNG_WRITE_PACK_SUPPORTED -/* Pack pixels into bytes. Pass the true bit depth in bit_depth. The - * row_info bit depth should be 8 (one pixel per byte). The channels - * should be 1 (this only happens on grayscale and paletted images). - */ +/* Pack pixels into bytes. */ static void -png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) +png_do_write_pack(png_transformp *transform, png_transform_controlp tc) { + png_alloc_size_t rowbytes = PNG_TC_ROWBYTES(*tc); + png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); + png_const_bytep ep = png_upcast(png_const_bytep, tc->sp) + rowbytes; + png_bytep dp = png_voidcast(png_bytep, tc->dp); + png_debug(1, "in png_do_pack"); - if (row_info->bit_depth == 8 && - row_info->channels == 1) +# define png_ptr tc->png_ptr + + switch ((*transform)->args) { - switch ((int)bit_depth) + case 1: { - case 1: + unsigned int mask = 0x80, v = 0; + + while (sp < ep) { - png_bytep sp, dp; - int mask, v; - png_uint_32 i; - png_uint_32 row_width = row_info->width; + if (*sp++ != 0) + v |= mask; - sp = row; - dp = row; - mask = 0x80; - v = 0; + mask >>= 1; - for (i = 0; i < row_width; i++) + if (mask == 0) { - if (*sp != 0) - v |= mask; - - sp++; - - if (mask > 1) - mask >>= 1; - - else - { - mask = 0x80; - *dp = (png_byte)v; - dp++; - v = 0; - } + mask = 0x80; + *dp++ = PNG_BYTE(v); + v = 0; } - - if (mask != 0x80) - *dp = (png_byte)v; - - break; } - case 2: - { - png_bytep sp, dp; - int shift, v; - png_uint_32 i; - png_uint_32 row_width = row_info->width; - - sp = row; - dp = row; - shift = 6; - v = 0; - - for (i = 0; i < row_width; i++) - { - png_byte value; - - value = (png_byte)(*sp & 0x03); - v |= (value << shift); - - if (shift == 0) - { - shift = 6; - *dp = (png_byte)v; - dp++; - v = 0; - } - - else - shift -= 2; - - sp++; - } - - if (shift != 6) - *dp = (png_byte)v; - - break; - } - - case 4: - { - png_bytep sp, dp; - int shift, v; - png_uint_32 i; - png_uint_32 row_width = row_info->width; - - sp = row; - dp = row; - shift = 4; - v = 0; - - for (i = 0; i < row_width; i++) - { - png_byte value; - - value = (png_byte)(*sp & 0x0f); - v |= (value << shift); - - if (shift == 0) - { - shift = 4; - *dp = (png_byte)v; - dp++; - v = 0; - } - - else - shift -= 4; - - sp++; - } - - if (shift != 4) - *dp = (png_byte)v; - - break; - } - - default: - break; + if (mask != 0x80) + *dp++ = PNG_BYTE(v); + break; } - row_info->bit_depth = (png_byte)bit_depth; - row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels); - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, - row_info->width); + case 2: + { + unsigned int shift = 8, v = 0; + + while (sp < ep) + { + shift -= 2; + v |= (*sp++ & 0x3) << shift; + + if (shift == 0) + { + shift = 8; + *dp++ = PNG_BYTE(v); + v = 0; + } + } + + if (shift != 8) + *dp++ = PNG_BYTE(v); + break; + } + + case 4: + { + unsigned int shift = 8, v = 0; + + while (sp < ep) + { + shift -= 4; + v |= ((*sp++ & 0xf) << shift); + + if (shift == 0) + { + shift = 8; + *dp++ = PNG_BYTE(v); + v = 0; + } + } + + if (shift != 8) + *dp++ = PNG_BYTE(v); + break; + } + + default: + impossible("bit depth"); } + + if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0 && + --(tc->range) == 0) + tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_RANGE); + + tc->bit_depth = (*transform)->args; + tc->sp = tc->dp; +# undef png_ptr } -#endif -#ifdef PNG_WRITE_SHIFT_SUPPORTED -/* Shift pixel values to take advantage of whole range. Pass the - * true number of bits in bit_depth. The row should be packed - * according to row_info->bit_depth. Thus, if you had a row of - * bit depth 4, but the pixels only had values from 0 to 7, you - * would pass 3 as bit_depth, and this routine would translate the - * data to 0 to 15. - */ -static void -png_do_shift(png_row_infop row_info, png_bytep row, - png_const_color_8p bit_depth) -{ - png_debug(1, "in png_do_shift"); - - if (row_info->color_type != PNG_COLOR_TYPE_PALETTE) - { - int shift_start[4], shift_dec[4]; - int channels = 0; - - if (row_info->color_type & PNG_COLOR_MASK_COLOR) - { - shift_start[channels] = row_info->bit_depth - bit_depth->red; - shift_dec[channels] = bit_depth->red; - channels++; - - shift_start[channels] = row_info->bit_depth - bit_depth->green; - shift_dec[channels] = bit_depth->green; - channels++; - - shift_start[channels] = row_info->bit_depth - bit_depth->blue; - shift_dec[channels] = bit_depth->blue; - channels++; - } - - else - { - shift_start[channels] = row_info->bit_depth - bit_depth->gray; - shift_dec[channels] = bit_depth->gray; - channels++; - } - - if (row_info->color_type & PNG_COLOR_MASK_ALPHA) - { - shift_start[channels] = row_info->bit_depth - bit_depth->alpha; - shift_dec[channels] = bit_depth->alpha; - channels++; - } - - /* With low row depths, could only be grayscale, so one channel */ - if (row_info->bit_depth < 8) - { - png_bytep bp = row; - png_size_t i; - unsigned int mask; - png_size_t row_bytes = row_info->rowbytes; - - if (bit_depth->gray == 1 && row_info->bit_depth == 2) - mask = 0x55; - - else if (row_info->bit_depth == 4 && bit_depth->gray == 3) - mask = 0x11; - - else - mask = 0xff; - - for (i = 0; i < row_bytes; i++, bp++) - { - int j; - unsigned int v, out; - - v = *bp; - out = 0; - - for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0]) - { - if (j > 0) - out |= v << j; - - else - out |= (v >> (-j)) & mask; - } - - *bp = (png_byte)(out & 0xff); - } - } - - else if (row_info->bit_depth == 8) - { - png_bytep bp = row; - png_uint_32 i; - png_uint_32 istop = channels * row_info->width; - - for (i = 0; i < istop; i++, bp++) - { - - const unsigned int c = i%channels; - int j; - unsigned int v, out; - - v = *bp; - out = 0; - - for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) - { - if (j > 0) - out |= v << j; - - else - out |= v >> (-j); - } - - *bp = (png_byte)(out & 0xff); - } - } - - else - { - png_bytep bp; - png_uint_32 i; - png_uint_32 istop = channels * row_info->width; - - for (bp = row, i = 0; i < istop; i++) - { - const unsigned int c = i%channels; - int j; - unsigned int value, v; - - v = png_get_uint_16(bp); - value = 0; - - for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) - { - if (j > 0) - value |= v << j; - - else - value |= v >> (-j); - } - *bp++ = (png_byte)((value >> 8) & 0xff); - *bp++ = (png_byte)(value & 0xff); - } - } - } -} -#endif - -#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED -static void -png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) -{ - png_debug(1, "in png_do_write_swap_alpha"); - - { - if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - { - if (row_info->bit_depth == 8) - { - /* This converts from ARGB to RGBA */ - png_bytep sp, dp; - png_uint_32 i; - png_uint_32 row_width = row_info->width; - - for (i = 0, sp = dp = row; i < row_width; i++) - { - png_byte save = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = save; - } - } - -#ifdef PNG_WRITE_16BIT_SUPPORTED - else - { - /* This converts from AARRGGBB to RRGGBBAA */ - png_bytep sp, dp; - png_uint_32 i; - png_uint_32 row_width = row_info->width; - - for (i = 0, sp = dp = row; i < row_width; i++) - { - png_byte save[2]; - save[0] = *(sp++); - save[1] = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = save[0]; - *(dp++) = save[1]; - } - } -#endif /* PNG_WRITE_16BIT_SUPPORTED */ - } - - else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - { - if (row_info->bit_depth == 8) - { - /* This converts from AG to GA */ - png_bytep sp, dp; - png_uint_32 i; - png_uint_32 row_width = row_info->width; - - for (i = 0, sp = dp = row; i < row_width; i++) - { - png_byte save = *(sp++); - *(dp++) = *(sp++); - *(dp++) = save; - } - } - -#ifdef PNG_WRITE_16BIT_SUPPORTED - else - { - /* This converts from AAGG to GGAA */ - png_bytep sp, dp; - png_uint_32 i; - png_uint_32 row_width = row_info->width; - - for (i = 0, sp = dp = row; i < row_width; i++) - { - png_byte save[2]; - save[0] = *(sp++); - save[1] = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = save[0]; - *(dp++) = save[1]; - } - } -#endif /* PNG_WRITE_16BIT_SUPPORTED */ - } - } -} -#endif - -#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED -static void -png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) -{ - png_debug(1, "in png_do_write_invert_alpha"); - - { - if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) - { - if (row_info->bit_depth == 8) - { - /* This inverts the alpha channel in RGBA */ - png_bytep sp, dp; - png_uint_32 i; - png_uint_32 row_width = row_info->width; - - for (i = 0, sp = dp = row; i < row_width; i++) - { - /* Does nothing - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - */ - sp+=3; dp = sp; - *(dp++) = (png_byte)(255 - *(sp++)); - } - } - -#ifdef PNG_WRITE_16BIT_SUPPORTED - else - { - /* This inverts the alpha channel in RRGGBBAA */ - png_bytep sp, dp; - png_uint_32 i; - png_uint_32 row_width = row_info->width; - - for (i = 0, sp = dp = row; i < row_width; i++) - { - /* Does nothing - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - *(dp++) = *(sp++); - */ - sp+=6; dp = sp; - *(dp++) = (png_byte)(255 - *(sp++)); - *(dp++) = (png_byte)(255 - *(sp++)); - } - } -#endif /* PNG_WRITE_16BIT_SUPPORTED */ - } - - else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - { - if (row_info->bit_depth == 8) - { - /* This inverts the alpha channel in GA */ - png_bytep sp, dp; - png_uint_32 i; - png_uint_32 row_width = row_info->width; - - for (i = 0, sp = dp = row; i < row_width; i++) - { - *(dp++) = *(sp++); - *(dp++) = (png_byte)(255 - *(sp++)); - } - } - -#ifdef PNG_WRITE_16BIT_SUPPORTED - else - { - /* This inverts the alpha channel in GGAA */ - png_bytep sp, dp; - png_uint_32 i; - png_uint_32 row_width = row_info->width; - - for (i = 0, sp = dp = row; i < row_width; i++) - { - /* Does nothing - *(dp++) = *(sp++); - *(dp++) = *(sp++); - */ - sp+=2; dp = sp; - *(dp++) = (png_byte)(255 - *(sp++)); - *(dp++) = (png_byte)(255 - *(sp++)); - } - } -#endif /* PNG_WRITE_16BIT_SUPPORTED */ - } - } -} -#endif - -/* Transform the data according to the user's wishes. The order of - * transformations is significant. - */ void /* PRIVATE */ -png_do_write_transformations(png_structrp png_ptr, png_row_infop row_info) +png_init_write_pack(png_transformp *transform, png_transform_controlp tc) { - png_debug(1, "in png_do_write_transformations"); +# define png_ptr tc->png_ptr + debug(tc->init); +# undef png_ptr - if (png_ptr == NULL) - return; + /* The init routine is called *forward* so the transform control we get has + * the required bit depth and the transform routine will increase it to 8 + * bits per channel. The code doesn't really care how many channels there + * are, but the only way to get a channel depth of less than 8 is to have + * just one channel. + */ + if (tc->bit_depth < 8) /* else no packing/unpacking */ + { + if (tc->init == PNG_TC_INIT_FINAL) + { + (*transform)->fn = png_do_write_pack; + /* Record this for the backwards run: */ + (*transform)->args = tc->bit_depth & 0xf; + } -#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED - if (png_ptr->transformations & PNG_USER_TRANSFORM) - if (png_ptr->write_user_transform_fn != NULL) - (*(png_ptr->write_user_transform_fn)) /* User write transform - function */ - (png_ptr, /* png_ptr */ - row_info, /* row_info: */ - /* png_uint_32 width; width of row */ - /* png_size_t rowbytes; number of bytes in row */ - /* png_byte color_type; color type of pixels */ - /* png_byte bit_depth; bit depth of samples */ - /* png_byte channels; number of channels (1-4) */ - /* png_byte pixel_depth; bits per pixel (depth*channels) */ - png_ptr->row_buf + 1); /* start of pixel data for row */ -#endif + if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0) + { + tc->range++; + tc->format |= PNG_FORMAT_FLAG_RANGE; /* forwards: backwards cancels */ + } -#ifdef PNG_WRITE_FILLER_SUPPORTED - if (png_ptr->transformations & PNG_FILLER) - png_do_strip_channel(row_info, png_ptr->row_buf + 1, - !(png_ptr->flags & PNG_FLAG_FILLER_AFTER)); -#endif + tc->bit_depth = 8; + } -#ifdef PNG_WRITE_PACKSWAP_SUPPORTED - if (png_ptr->transformations & PNG_PACKSWAP) - png_do_packswap(row_info, png_ptr->row_buf + 1); -#endif - -#ifdef PNG_WRITE_PACK_SUPPORTED - if (png_ptr->transformations & PNG_PACK) - png_do_pack(row_info, png_ptr->row_buf + 1, - (png_uint_32)png_ptr->bit_depth); -#endif - -#ifdef PNG_WRITE_SWAP_SUPPORTED - if (png_ptr->transformations & PNG_SWAP_BYTES) - png_do_swap(row_info, png_ptr->row_buf + 1); -#endif - -#ifdef PNG_WRITE_SHIFT_SUPPORTED - if (png_ptr->transformations & PNG_SHIFT) - png_do_shift(row_info, png_ptr->row_buf + 1, - &(png_ptr->shift)); -#endif - -#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED - if (png_ptr->transformations & PNG_SWAP_ALPHA) - png_do_write_swap_alpha(row_info, png_ptr->row_buf + 1); -#endif - -#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED - if (png_ptr->transformations & PNG_INVERT_ALPHA) - png_do_write_invert_alpha(row_info, png_ptr->row_buf + 1); -#endif - -#ifdef PNG_WRITE_BGR_SUPPORTED - if (png_ptr->transformations & PNG_BGR) - png_do_bgr(row_info, png_ptr->row_buf + 1); -#endif - -#ifdef PNG_WRITE_INVERT_SUPPORTED - if (png_ptr->transformations & PNG_INVERT_MONO) - png_do_invert(row_info, png_ptr->row_buf + 1); -#endif + else /* the transform is not applicable */ + (*transform)->fn = NULL; } -#endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */ -#endif /* PNG_WRITE_SUPPORTED */ +#endif /* WRITE_PACK */ diff --git a/ext/libpng17/pngwutil.c b/ext/libpng17/pngwutil.c index df2ffdb9df..19052bfbaa 100644 --- a/ext/libpng17/pngwutil.c +++ b/ext/libpng17/pngwutil.c @@ -1,8 +1,14 @@ +#ifdef _MSC_VER +#pragma warning (disable:4018) +#pragma warning (disable:4028) +#pragma warning (disable:4146) +#pragma warning (disable:4334) +#endif /* pngwutil.c - utilities to write a PNG file * * Last changed in libpng 1.7.0 [(PENDING RELEASE)] - * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -12,6 +18,7 @@ */ #include "pngpriv.h" +#define PNG_SRC_FILE PNG_SRC_FILE_pngwutil #ifdef PNG_WRITE_SUPPORTED @@ -23,10 +30,10 @@ void PNGAPI png_save_uint_32(png_bytep buf, png_uint_32 i) { - buf[0] = (png_byte)((i >> 24) & 0xff); - buf[1] = (png_byte)((i >> 16) & 0xff); - buf[2] = (png_byte)((i >> 8) & 0xff); - buf[3] = (png_byte)(i & 0xff); + buf[0] = PNG_BYTE(i >> 24); + buf[1] = PNG_BYTE(i >> 16); + buf[2] = PNG_BYTE(i >> 8); + buf[3] = PNG_BYTE(i); } /* Place a 16-bit number into a buffer in PNG byte order. @@ -36,10 +43,10 @@ png_save_uint_32(png_bytep buf, png_uint_32 i) void PNGAPI png_save_uint_16(png_bytep buf, unsigned int i) { - buf[0] = (png_byte)((i >> 8) & 0xff); - buf[1] = (png_byte)(i & 0xff); + buf[0] = PNG_BYTE(i >> 8); + buf[1] = PNG_BYTE(i); } -#endif +#endif /* WRITE_INT_FUNCTIONS */ /* Simple function to write the signature. If we have already written * the magic bytes of the signature, or more likely, the PNG stream is @@ -59,7 +66,7 @@ png_write_sig(png_structrp png_ptr) /* Write the rest of the 8 byte signature */ png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes], - (png_size_t)(8 - png_ptr->sig_bytes)); + (png_size_t)(8 - png_ptr->sig_bytes)); if (png_ptr->sig_bytes < 3) png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; @@ -99,9 +106,7 @@ png_write_chunk_header(png_structrp png_ptr, png_uint_32 chunk_name, png_ptr->chunk_name = chunk_name; /* Reset the crc and run it over the chunk name */ - png_reset_crc(png_ptr); - - png_calculate_crc(png_ptr, buf + 4, 4); + png_reset_crc(png_ptr, buf+4); #ifdef PNG_IO_STATE_SUPPORTED /* Inform the I/O callback that chunk data will (possibly) be written. @@ -124,7 +129,7 @@ png_write_chunk_start(png_structrp png_ptr, png_const_bytep chunk_string, * given to png_write_chunk_header(). */ void PNGAPI -png_write_chunk_data(png_structrp png_ptr, png_const_bytep data, +png_write_chunk_data(png_structrp png_ptr, png_const_voidp data, png_size_t length) { /* Write the data, and run the CRC over it */ @@ -136,7 +141,7 @@ png_write_chunk_data(png_structrp png_ptr, png_const_bytep data, png_write_data(png_ptr, data, length); /* Update the CRC after writing the data, - * in case that the user I/O routine alters it. + * in case the user I/O routine alters it. */ png_calculate_crc(png_ptr, data, length); } @@ -174,16 +179,16 @@ png_write_chunk_end(png_structrp png_ptr) */ static void png_write_complete_chunk(png_structrp png_ptr, png_uint_32 chunk_name, - png_const_bytep data, png_size_t length) + png_const_voidp data, png_size_t length) { if (png_ptr == NULL) return; /* On 64 bit architectures 'length' may not fit in a png_uint_32. */ if (length > PNG_UINT_31_MAX) - png_error(png_ptr, "length exceeds PNG maxima"); + png_error(png_ptr, "length exceeds PNG maximum"); - png_write_chunk_header(png_ptr, chunk_name, (png_uint_32)length); + png_write_chunk_header(png_ptr, chunk_name, (png_uint_32)/*SAFE*/length); png_write_chunk_data(png_ptr, data, length); png_write_chunk_end(png_ptr); } @@ -191,253 +196,132 @@ png_write_complete_chunk(png_structrp png_ptr, png_uint_32 chunk_name, /* This is the API that calls the internal function above. */ void PNGAPI png_write_chunk(png_structrp png_ptr, png_const_bytep chunk_string, - png_const_bytep data, png_size_t length) + png_const_voidp data, png_size_t length) { png_write_complete_chunk(png_ptr, PNG_CHUNK_FROM_STRING(chunk_string), data, - length); + length); } -/* This is used below to find the size of an image to pass to png_deflate_claim, - * so it only needs to be accurate if the size is less than 16384 bytes (the - * point at which a lower LZ window size can be used.) - */ static png_alloc_size_t -png_image_size(png_structrp png_ptr) -{ - /* Only return sizes up to the maximum of a png_uint_32, do this by limiting - * the width and height used to 15 bits. +png_write_row_buffer_size(png_const_structrp png_ptr) + /* Returns the width of the widest pass in the first row of an interlaced + * image. Passes in the first row are: 0.5.3.5.1.5.3.5, so the widest row is + * normally the one from pass 5. The only exception is if the image is only + * one pixel wide, so: */ - png_uint_32 h = png_ptr->height; +#define PNG_FIRST_ROW_MAX_WIDTH(w) (w > 1U ? PNG_PASS_COLS(w, 5U) : 1U) - if (png_ptr->rowbytes < 32768 && h < 32768) + /* For interlaced images the count of pixels is rounded up to a the number of + * pixels in the first pass (numbered 0). This ensures that passes before + * the last can be packed in the buffer without overflow. + */ +{ + png_alloc_size_t w; + + /* If the image is interlaced adjust 'w' for the interlacing: */ + if (png_ptr->interlaced != PNG_INTERLACE_NONE) { - if (png_ptr->interlaced) - { - /* Interlacing makes the image larger because of the replication of - * both the filter byte and the padding to a byte boundary. - */ - png_uint_32 w = png_ptr->width; - unsigned int pd = png_ptr->pixel_depth; - png_alloc_size_t cb_base; - int pass; - - for (cb_base=0, pass=0; pass<=6; ++pass) - { - png_uint_32 pw = PNG_PASS_COLS(w, pass); - - if (pw > 0) - cb_base += (PNG_ROWBYTES(pd, pw)+1) * PNG_PASS_ROWS(h, pass); - } - - return cb_base; - } + /* Take advantage of the fact that 1-row interlaced PNGs require half the + * normal row width: + */ + if (png_ptr->height == 1U) /* no pass 6 */ + w = PNG_FIRST_ROW_MAX_WIDTH(png_ptr->width); + /* Otherwise round up to a multiple of 8. This may waste a few (less + * than 8) bytes for PNGs with a height less than 57 but this hardly + * matters. + */ else - return (png_ptr->rowbytes+1) * h; + w = (png_ptr->width + 7U) & ~7U; } else - return 0xffffffffU; + w = png_ptr->width; + + /* The rounding above may leave 'w' exactly 2^31 */ + debug(w <= 0x80000000U); + + switch (png_ptr->row_output_pixel_depth) + { + /* This would happen if the function is called before png_write_IHDR. */ + default: NOT_REACHED; return 0; + + case 1: w = (w+7) >> 3; break; + case 2: w = (w+3) >> 2; break; + case 4: w = (w+1) >> 1; break; + case 8: break; + case 16: w <<= 1; break; /* overflow: w is set to 0, which is OK */ + + /* For the remaining cases the answer is w*bytes; where bytes is 3,4,6 + * or 8. This may overflow 32 bits. There is no way to compute the + * result on an arbitrary platform, so test the maximum of a (size_t) + * against w for each possible byte depth: + */ +# define CASE(b)\ + case b*8:\ + if (w <= (PNG_SIZE_MAX/b)/*compile-time constant*/)\ + return w * b;\ + return 0; + + CASE(3) + CASE(4) + CASE(6) + CASE(8) + +# undef CASE + } + + /* This is the low bit depth case. The following can never be false on + * systems with a 32-bit or greater size_t: + */ + if (w <= PNG_SIZE_MAX) + return w; + + return 0U; } -#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED - /* This is the code to hack the first two bytes of the deflate stream (the - * deflate header) to correct the windowBits value to match the actual data - * size. Note that the second argument is the *uncompressed* size but the - * first argument is the *compressed* data (and it must be deflate - * compressed.) - */ +/* Release memory used by the deflate mechanism */ static void -optimize_cmf(png_bytep data, png_alloc_size_t data_size) +png_deflateEnd(png_const_structrp png_ptr, z_stream *zs, int check) { - /* Optimize the CMF field in the zlib stream. The resultant zlib stream is - * still compliant to the stream specification. - */ - if (data_size <= 16384) /* else windowBits must be 15 */ + if (zs->state != NULL) { - unsigned int z_cmf = data[0]; /* zlib compression method and flags */ + int ret = deflateEnd(zs); - if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70) + /* Z_DATA_ERROR means there was pending output. */ + if ((ret != Z_OK && (check || ret != Z_DATA_ERROR)) || zs->state != NULL) { - unsigned int z_cinfo; - unsigned int half_z_window_size; + png_zstream_error(zs, ret); - z_cinfo = z_cmf >> 4; - half_z_window_size = 1U << (z_cinfo + 7); - - if (data_size <= half_z_window_size) /* else no change */ - { - unsigned int tmp; - - do - { - half_z_window_size >>= 1; - --z_cinfo; - } - while (z_cinfo > 0 && data_size <= half_z_window_size); - - z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4); - - data[0] = (png_byte)z_cmf; - tmp = data[1] & 0xe0; - tmp += 0x1f - ((z_cmf << 8) + tmp) % 0x1f; - data[1] = (png_byte)tmp; - } - } - } -} -#else -# define optimize_cmf(dp,dl) ((void)0) -#endif /* PNG_WRITE_OPTIMIZE_CMF_SUPPORTED */ - -/* Initialize the compressor for the appropriate type of compression. */ -static int -png_deflate_claim(png_structrp png_ptr, png_uint_32 owner, - png_alloc_size_t data_size) -{ - if (png_ptr->zowner != 0) - { - char msg[64]; - - PNG_STRING_FROM_CHUNK(msg, owner); - msg[4] = ':'; - msg[5] = ' '; - PNG_STRING_FROM_CHUNK(msg+6, png_ptr->zowner); - /* So the message that results is " using zstream"; this is an - * internal error, but is very useful for debugging. i18n requirements - * are minimal. - */ - (void)png_safecat(msg, (sizeof msg), 10, " using zstream"); -# if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC - png_warning(png_ptr, msg); - - /* Attempt sane error recovery */ - if (png_ptr->zowner == png_IDAT) /* don't steal from IDAT */ - { - png_ptr->zstream.msg = PNGZ_MSG_CAST("in use by IDAT"); - return Z_STREAM_ERROR; - } - - png_ptr->zowner = 0; -# else - png_error(png_ptr, msg); -# endif - } - - { - int level = png_ptr->zlib_level; - int method = png_ptr->zlib_method; - int windowBits = png_ptr->zlib_window_bits; - int memLevel = png_ptr->zlib_mem_level; - int strategy; /* set below */ - int ret; /* zlib return code */ - - if (owner == png_IDAT) - { - if (png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY) - strategy = png_ptr->zlib_strategy; - - else if (png_ptr->do_filter != PNG_FILTER_NONE) - strategy = PNG_Z_DEFAULT_STRATEGY; + if (check) + png_error(png_ptr, zs->msg); else - strategy = PNG_Z_DEFAULT_NOFILTER_STRATEGY; + png_warning(png_ptr, zs->msg); + + zs->state = NULL; } - - else - { -# ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED - level = png_ptr->zlib_text_level; - method = png_ptr->zlib_text_method; - windowBits = png_ptr->zlib_text_window_bits; - memLevel = png_ptr->zlib_text_mem_level; - strategy = png_ptr->zlib_text_strategy; -# else - /* If customization is not supported the values all come from the - * IDAT values except for the strategy, which is fixed to the - * default. (This is the pre-1.6.0 behavior too, although it was - * implemented in a very different way.) - */ - strategy = Z_DEFAULT_STRATEGY; -# endif - } - - /* Adjust 'windowBits' down if larger than 'data_size'; to stop this - * happening just pass 32768 as the data_size parameter. Notice that zlib - * requires an extra 262 bytes in the window in addition to the data to be - * able to see the whole of the data, so if data_size+262 takes us to the - * next windowBits size we need to fix up the value later. (Because even - * though deflate needs the extra window, inflate does not!) - */ - if (data_size <= 16384) - { - /* IMPLEMENTATION NOTE: this 'half_window_size' stuff is only here to - * work round a Microsoft Visual C misbehavior which, contrary to C-90, - * widens the result of the following shift to 64-bits if (and, - * apparently, only if) it is used in a test. - */ - unsigned int half_window_size = 1U << (windowBits-1); - - while (data_size + 262 <= half_window_size) - { - half_window_size >>= 1; - --windowBits; - } - } - - /* Check against the previous initialized values, if any. */ - if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) && - (png_ptr->zlib_set_level != level || - png_ptr->zlib_set_method != method || - png_ptr->zlib_set_window_bits != windowBits || - png_ptr->zlib_set_mem_level != memLevel || - png_ptr->zlib_set_strategy != strategy)) - { - if (deflateEnd(&png_ptr->zstream) != Z_OK) - png_warning(png_ptr, "deflateEnd failed (ignored)"); - - png_ptr->flags &= ~PNG_FLAG_ZSTREAM_INITIALIZED; - } - - /* For safety clear out the input and output pointers (currently zlib - * doesn't use them on Init, but it might in the future). - */ - png_ptr->zstream.next_in = NULL; - png_ptr->zstream.avail_in = 0; - png_ptr->zstream.next_out = NULL; - png_ptr->zstream.avail_out = 0; - - /* Now initialize if required, setting the new parameters, otherwise just - * to a simple reset to the previous parameters. - */ - if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) - ret = deflateReset(&png_ptr->zstream); - - else - { - ret = deflateInit2(&png_ptr->zstream, level, method, windowBits, - memLevel, strategy); - - if (ret == Z_OK) - png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED; - } - - /* The return code is from either deflateReset or deflateInit2; they have - * pretty much the same set of error codes. - */ - if (ret == Z_OK) - png_ptr->zowner = owner; - - else - png_zstream_error(png_ptr, ret); - - return ret; } } -/* Clean up (or trim) a linked list of compression buffers. */ -void /* PRIVATE */ -png_free_buffer_list(png_structrp png_ptr, png_compression_bufferp *listp) +/* compression_buffer (new in 1.6.0) is just a linked list of temporary buffers. * From 1.6.0 it is retained in png_struct so that it will be correctly freed in + * the event of a write error (previous implementations just leaked memory.) + * + * From 1.7.0 the size is fixed to the same as the (uncompressed) row buffer + * size. This avoids allocating a large chunk of memory when compressing small + * images. This type is also opaque outside this file. + */ +typedef struct png_compression_buffer +{ + struct png_compression_buffer *next; + png_byte output[PNG_ROW_BUFFER_SIZE]; +} png_compression_buffer, *png_compression_bufferp; + +/* png_compression_buffer methods */ +/* Deleting a compression buffer deletes the whole list: */ +static void +png_free_compression_buffer(png_const_structrp png_ptr, + png_compression_bufferp *listp) { png_compression_bufferp list = *listp; @@ -456,43 +340,1501 @@ png_free_buffer_list(png_structrp png_ptr, png_compression_bufferp *listp) } } -#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED -/* This pair of functions encapsulates the operation of (a) compressing a - * text string, and (b) issuing it later as a series of chunk data writes. - * The compression_state structure is shared context for these functions - * set up by the caller to allow access to the relevant local variables. +/* Return the next compression buffer in the list, allocating it if necessary. + * The caller must update 'end' if required; this just moves down the list. + */ +static png_compression_bufferp +png_get_compression_buffer(png_const_structrp png_ptr, + png_compression_bufferp *end) +{ + png_compression_bufferp next = *end; + + if (next == NULL) + { + next = png_voidcast(png_compression_bufferp, png_malloc_base(png_ptr, + sizeof *next)); + + /* Check for OOM: this is a recoverable error for non-critical chunks, let + * the caller decide what to do rather than issuing a png_error here. + */ + if (next != NULL) + { + next->next = NULL; /* initialize the buffer */ + *end = next; + } + } + + return next; /* may still be NULL on OOM */ +} + +/* This structure is used to hold all the data for zlib compression of a single + * stream of data. It may be re-used, it stores the compressed data internally + * and can handle arbitrary input and output. * - * compression_buffer (new in 1.6.0) is just a linked list of zbuffer_size - * temporary buffers. From 1.6.0 it is retained in png_struct so that it will - * be correctly freed in the event of a write error (previous implementations - * just leaked memory.) + * 'list' is the output data contained in compression buffers, 'end' points to + * list at the start and is advanced down the compression buffer list (extending + * it as required) as the data is written. If 'end' points into a compression + * buffer (does not point to 'list') that is the buffer in use in + * z_stream::{next,avail}_out. + * + * Compression may be performed in multiple steps, '*end' always points to the + * compression buffer *after* the one that is in use, so 'end' is pointing + * *into* the one in use. + * + * end(on entry) .... end ....... end(on exit) + * | | | + * | | | + * V +----V-----+ +-----V----+ +----------+ + * list ---> | next --+--> | next --+--> | next | + * | output[] | | output[] | | output[] | + * +----------+ +----------+ +----------+ + * [in use] [unused] + * + * These invariants should always hold: + * + * 1) If zs.state is NULL decompression is not in progress, list may be non-NULL + * but end could be anything; + * + * 2) Otherwise if zs.next_out is NULL list will be NULL and end will point at + * list, len, overflow and start will be 0; + * + * 3) Otherwise list is non-NULL and end points at the 'next' element of an + * in-use compression buffer. zs.next_out points into the 'output' element + * of the same buffer. {overflow, len} is the amount of compressed data, len + * being the low 31 bits, overflow being the higher bits. start is used for + * writing and is the index of the first byte in list->output to write, + * {overflow, len} does not include start. */ typedef struct { - png_const_bytep input; /* The uncompressed input data */ - png_alloc_size_t input_len; /* Its length */ - png_uint_32 output_len; /* Final compressed length */ - png_byte output[1024]; /* First block of output */ -} compression_state; + z_stream zs; /* zlib compression data */ + png_compression_bufferp list; /* Head of the buffer list */ + png_compression_bufferp *end; /* Pointer to last 'next' pointer */ + png_uint_32 len; /* Bottom 31 bits of data length */ + unsigned int overflow; /* Top bits of data length */ + unsigned int start; /* Start of data in first block */ +} png_zlib_compress, *png_zlib_compressp; +/* png_zlib_compress methods */ +/* Initialize the compress structure. The z_stream itself is not initialized, + * however the the 'user' fields are set, including {next,avail}_{in,out}. The + * initialization does not change 'list', however it does set 'end' to point to + * it, effectively truncating the list. + */ static void -png_text_compress_init(compression_state *comp, png_const_bytep input, - png_alloc_size_t input_len) +png_zlib_compress_init(png_structrp png_ptr, png_zlib_compressp pz) { - comp->input = input; - comp->input_len = input_len; - comp->output_len = 0; + /* png_zlib_compress z_stream: */ + pz->zs.zalloc = png_zalloc; + pz->zs.zfree = png_zfree; + /* NOTE: this does not destroy 'restrict' because in all the functions herein + * *png_ptr is only ever accessed via *either* pz->zs.opaque *or* a passed in + * png_ptr. + */ + pz->zs.opaque = png_ptr; + + pz->zs.next_in = NULL; + pz->zs.avail_in = 0U; + pz->zs.total_in = 0U; + + pz->zs.next_out = NULL; + pz->zs.avail_out = 0U; + pz->zs.total_out = 0U; + + pz->zs.msg = PNGZ_MSG_CAST("zlib success"); /* safety */ + + /* pz->list preserved */ + pz->end = &pz->list; + pz->len = 0U; + pz->overflow = 0U; + pz->start = 0U; } -/* Compress the data in the compression state input */ -static int -png_text_compress(png_structrp png_ptr, png_uint_32 chunk_name, - compression_state *comp, png_uint_32 prefix_len) -{ - int ret; +/* Return the png_ptr: this is defined here for all the remaining + * png_zlib_compress methods because they are only ever called with zs + * initialized. + */ +#define png_ptr png_voidcast(png_const_structrp, pz->zs.opaque) +#if PNG_RELEASE_BUILD +# define png_zlib_compress_validate(pz, in_use) ((void)0) +#else /* !RELEASE_BUILD */ +static void +png_zlib_compress_validate(png_zlib_compressp pz, int in_use) +{ + const uInt o_size = sizeof pz->list->output; + + affirm(pz->end != NULL && (in_use || (pz->zs.next_in == NULL && + pz->zs.avail_in == 0U && *pz->end == NULL))); + + if (pz->overflow == 0U && pz->len == 0U && pz->start == 0U) /* empty */ + { + affirm((pz->end == &pz->list && pz->zs.next_out == NULL + && pz->zs.avail_out == 0U) || + (pz->list != NULL && pz->end == &pz->list->next && + pz->zs.next_out == pz->list->output && + pz->zs.avail_out == o_size)); + } + + else /* not empty */ + { + png_compression_bufferp *ep = &pz->list, list; + png_uint_32 o, l; + + affirm(*ep != NULL && pz->zs.next_out != NULL); + + /* Check the list length: */ + o = pz->overflow; + l = pz->len; + affirm((l & 0x80000000U) == 0U && (o & 0x80000000U) == 0U); + + do + { + list = *ep; + l -= o_size; + if (l & 0x80000000U) --o, l &= 0x7FFFFFFFU; + ep = &list->next; + } + while (ep != pz->end); + + l += pz->start; + l += pz->zs.avail_out; + if (l & 0x80000000U) ++o, l &= 0x7FFFFFFFU; + + affirm(o == 0U && l == 0U && pz->zs.next_out >= list->output && + pz->zs.next_out + pz->zs.avail_out == list->output + o_size); + } +} +#endif /* !RELEASE_BUILD */ + +/* Destroy one zlib compress structure. */ +static void +png_zlib_compress_destroy(png_zlib_compressp pz, int check) +{ + /* If the 'opaque' pointer is NULL this png_zlib_compress was never + * initialized, so do nothing. + */ + if (png_ptr != NULL) + { + if (pz->zs.state != NULL) + { + if (check) + png_zlib_compress_validate(pz, 0/*in_use*/); + + png_deflateEnd(png_ptr, &pz->zs, check); + } + + pz->end = &pz->list; /* safety */ + png_free_compression_buffer(png_ptr, &pz->list); + } +} + +/* Ensure that space is available for output, returns the amount of space + * available, 0 on OOM. This updates pz->zs.avail_out (etc) as required. + */ +static uInt +png_zlib_compress_avail_out(png_zlib_compressp pz) +{ + uInt avail_out = pz->zs.avail_out; + + png_zlib_compress_validate(pz, 1/*in_use*/); + + if (avail_out == 0U) + { + png_compression_bufferp next; + + affirm(pz->end == &pz->list || (pz->end != NULL && pz->list != NULL)); + next = png_get_compression_buffer(png_ptr, pz->end); + + if (next != NULL) + { + pz->zs.next_out = next->output; + pz->zs.avail_out = avail_out = sizeof next->output; + pz->end = &next->next; + } + + /* else return 0: OOM */ + } + + else + affirm(pz->end != NULL && pz->list != NULL); + + return avail_out; +} + +/* Compress the given data given an initialized png_zlib_compress structure. + * This may be called multiple times, interleaved with writes as required. + * + * The input data is passed in in pz->zs.next_in, however the length of the data + * is in 'input_len' (to avoid the zlib uInt limit) and pz->zs.avail_in is + * overwritten (and left at 0). + * + * The output information is used and the amount of compressed data is added on + * to pz->{overflow,len}. + * + * If 'limit' is a limit on the amount of data to add to the output (not the + * total amount). The function will retun Z_BUF_ERROR if the limit is reached + * and the function will never produce more (additional) compressed data than + * the limit. + * + * All of zstream::next_in[input] is consumed if a success code is returned + * (Z_OK or Z_STREAM_END if flush is Z_FINISH), otherwise next_in may be used to + * determine how much was compressed. + * + * pz->overflow is not checked for overflow, so if 'limit' is not set overflow + * is possible. The caller must guard against this when supplying a limit of 0. + */ +static int +png_compress( + png_zlib_compressp pz, + png_alloc_size_t input_len, /* Length of data to be compressed */ + png_uint_32 limit, /* Limit on amount of compressed data made */ + int flush) /* Flush parameter at end of input */ +{ + const int unlimited = (limit == 0U); + + /* Sanity checking: */ + affirm(pz->zs.state != NULL && + (pz->zs.next_out == NULL + ? pz->end == &pz->list && pz->len == 0U && pz->overflow == 0U + : pz->list != NULL && pz->end != NULL)); + implies(pz->zs.next_out == NULL, pz->zs.avail_out == 0); + + for (;;) + { + uInt extra; + + /* OUTPUT: make sure some space is available: */ + if (png_zlib_compress_avail_out(pz) == 0U) + return Z_MEM_ERROR; + + /* INPUT: limit the deflate call input to ZLIB_IO_MAX: */ + /* Adjust the input counters: */ + { + uInt avail_in = ZLIB_IO_MAX; + + if (avail_in > input_len) + avail_in = (uInt)/*SAFE*/input_len; + + input_len -= avail_in; + pz->zs.avail_in = avail_in; + } + + if (!unlimited && pz->zs.avail_out > limit) + { + extra = (uInt)/*SAFE*/(pz->zs.avail_out - limit); /* unused bytes */ + pz->zs.avail_out = (uInt)/*SAFE*/limit; + limit = 0U; + } + + else + { + extra = 0U; + limit -= pz->zs.avail_out; /* limit >= 0U */ + } + + pz->len += pz->zs.avail_out; /* maximum that can be produced */ + + /* Compress the data */ + { + int ret = deflate(&pz->zs, input_len > 0U ? Z_NO_FLUSH : flush); + + /* Claw back input data that was not consumed (because avail_in is + * reset above every time round the loop) and correct the output + * length. + */ + input_len += pz->zs.avail_in; + pz->zs.avail_in = 0; /* safety */ + pz->len -= pz->zs.avail_out; + + if (pz->len & 0x80000000U) + ++pz->overflow, pz->len &= 0x7FFFFFFFU; + + limit += pz->zs.avail_out; + pz->zs.avail_out += extra; + + /* Check the error code: */ + switch (ret) + { + case Z_OK: + if (pz->zs.avail_out > extra) + { + /* zlib had output space, so all the input should have been + * consumed: + */ + affirm(input_len == 0U /* else unexpected stop */ && + flush != Z_FINISH/* ret != Z_STREAM_END */); + return Z_OK; + } + + else + { + /* zlib ran out of output space, produce some more. If the + * limit is 0 at this point, however, no more space is + * available. + */ + if (unlimited || limit > 0U) + break; /* Allocate more output */ + + /* No more output space available, but the input may have all + * been consumed. + */ + if (input_len == 0U && flush != Z_FINISH) + return Z_OK; + + /* Input all consumed, but insufficient space to flush the + * output; this is the Z_BUF_ERROR case. + */ + return Z_BUF_ERROR; + } + + case Z_STREAM_END: + affirm(input_len == 0U && flush == Z_FINISH); + return Z_STREAM_END; + + case Z_BUF_ERROR: + /* This means that we are flushing all the output; expect + * avail_out and input_len to be 0. + * + * NOTE: if png_compress is called with input_len 0 and flush set + * to Z_NO_FLUSH this affirm will fire because zlib will have no + * work to do. + */ + affirm(input_len == 0U && pz->zs.avail_out == extra); + /* Allocate another buffer */ + break; + + default: + /* An error */ + return ret; + } + } + } +} + +#undef png_ptr /* remove definition using a png_zlib_compressp */ + +/* All the compression state is held here, it is allocated when required. This + * ensures that the read code doesn't carry the overhead of the much less + * frequently used write stuff. + * + * TODO: make png_create_write_struct allocate this stuff after the main + * png_struct. + */ +struct filter_selector; /* Used only for filter selection */ + +typedef struct png_zlib_state +{ + png_zlib_compress s; /* Primary compression state */ + png_compression_bufferp stash; /* Unused compression buffers */ + +# define ps_png_ptr(ps) png_upcast(png_const_structrp, (ps)->s.zs.opaque) + /* A png_ptr, used below in functions that only have a png_zlib_state. + * NOTE: the png_zlib_compress must have been initialized! + */ + + png_uint_32 zlib_max_pixels; + /* Maximum number of pixels that zlib can handle at once; the lesser of + * the PNG maximum and the maximum that will fit in (uInt)-1 bytes. This + * number of pixels may not be byte aligned. + */ + png_uint_32 zlib_max_aligned_pixels; + /* The maximum number of pixels that zlib can handle while maintaining a + * buffer byte alignment of PNG_ROW_BUFFER_BYTE_ALIGN; <= the previous + * value. + */ + + png_alloc_size_t write_row_size; + /* Size of the PNG row (without the filter byte) in bytes or 0 if it is + * too large to be cached. + */ + +# ifdef PNG_WRITE_FILTER_SUPPORTED + /* During write libpng needs the previous row when writing a new row with + * up, avg or paeth and one or more image rows when performing filter + * selection. So if performing filter selection typically two or more + * rows are required while if no filter selection is to be done only the + * previous row pointer is required. + */ + png_bytep previous_write_row; /* Last row written, if any */ +# ifdef PNG_SELECT_FILTER_SUPPORTED + png_bytep current_write_row; /* Row being written */ + struct filter_selector *selector; /* Data for filter selection */ + png_uint_32 filter_select_window; + /* The number of bytes of uncompressed PNG data which are assumed to + * be relevant when doing filter selection. Limited to 8453377 + * (about 2^23); the maximum number of bytes that can be encoded in + * the largest deflate window. + */ +# define PNG_FILTER_SELECT_WINDOW_MAX 8453377U + png_byte filter_select_threshold; + /* If the number of distinct codes seen in the PNG data are below + * this threshold the PNG data will not be filtered (if the 'none' + * filter is allowed). If this is still true and a particular + * filter does not add new codes that filter will be used. + */ + png_byte filter_select_threshold2; + /* If the number of distinct codes that result by using a particular + * filter is below this second threshold that filter will be used. + * (When multiple filters pass this criterion the lowest numbered + * one producing the lowest number of new codes will be + * chosen.) + */ +# endif /* SELECT_FILTER */ + + unsigned int row_buffer_max_pixels; + /* The maximum number of pixels that can fit in PNG_ROW_BUFFER_SIZE + * bytes; not necessary a whole number of bytes. + */ + unsigned int row_buffer_max_aligned_pixels; + /* The maximum number of pixels that can fit in PNG_ROW_BUFFER_SIZE + * bytes while maintaining PNG_ROW_BUFFER_BYTE_ALIGN alignment. + */ + + unsigned int filter_mask :8; /* mask of filters to consider on NEXT row */ +# define PREVIOUS_ROW_FILTERS\ + (PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH) + unsigned int filters :8; /* Filters for current row */ + unsigned int save_row :2; /* As below: */ +# define SAVE_ROW_UNSET 0U +# define SAVE_ROW_OFF 1U /* Previous-row filters will be ignored */ +# define SAVE_ROW_DEFAULT 2U /* Default to save rows set by libpng */ +# define SAVE_ROW_ON 3U /* Force rows to be saved */ +# define SAVE_ROW(ps) ((ps)->save_row >= SAVE_ROW_DEFAULT) +# endif /* WRITE_FILTER */ + + /* Compression settings: see below for how these are encoded. */ + png_uint_32 pz_IDAT; /* Settings for the image */ + png_uint_32 pz_iCCP; /* Settings for iCCP chunks */ + png_uint_32 pz_text; /* Settings for text chunks */ + png_uint_32 pz_current; /* Last set settings */ + +# ifdef PNG_WRITE_FLUSH_SUPPORTED + png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */ + png_uint_32 flush_rows; /* number of rows written since last flush */ +# endif /* WRITE_FLUSH */ +} png_zlib_state; + +/* Create the zlib state: */ +static void +png_create_zlib_state(png_structrp png_ptr) +{ + png_zlib_statep ps = png_voidcast(png_zlib_state*, + png_malloc(png_ptr, sizeof *ps)); + + /* Clear to NULL/0: */ + memset(ps, 0, sizeof *ps); + + debug(png_ptr->zlib_state == NULL); + png_ptr->zlib_state = ps; + png_zlib_compress_init(png_ptr, &ps->s); + +# ifdef PNG_WRITE_FILTER_SUPPORTED + ps->previous_write_row = NULL; +# ifdef PNG_SELECT_FILTER_SUPPORTED + ps->current_write_row = NULL; + ps->selector = NULL; +# endif /* SELECT_FILTER */ +# endif /* WRITE_FILTER */ +# ifdef PNG_WRITE_FLUSH_SUPPORTED + /* Set this to prevent flushing by making it larger than the number + * of rows in the largest interlaced PNG; PNG_UINT_31_MAX times + * (1/8+1/8+1/8+1/4+1/4+1/2+1/2); 1.875, or 15/8 + */ + ps->flush_dist = 0xEFFFFFFFU; +# endif /* WRITE_FLUSH */ +} + +static void +png_zlib_state_set_buffer_limits(png_const_structrp png_ptr, png_zlib_statep ps) + /* Delayed initialization of the zlib state maxima; this is not done above in + * case the zlib_state is created before the IHDR has been written, which + * would lead to the various png_struct fields used below being + * uninitialized. + */ +{ + /* Initialization of the buffer size constants. */ + const unsigned int bpp = PNG_PIXEL_DEPTH(*png_ptr); + const unsigned int byte_pp = bpp >> 3; /* May be 0 */ + const unsigned int pixel_block = + /* Number of pixels required to maintain PNG_ROW_BUFFER_BYTE_ALIGN + * alignment. For multi-byte pixels use the first set bit to determine + * if the pixels have a greater alignment already. + */ + bpp < 8U ? + PNG_ROW_BUFFER_BYTE_ALIGN * (8U/bpp) : + PNG_ROW_BUFFER_BYTE_ALIGN <= (byte_pp & -byte_pp) ? + 1U : + PNG_ROW_BUFFER_BYTE_ALIGN / (byte_pp & -byte_pp); + + /* pixel_block must always be a power of two: */ + debug(bpp > 0 && pixel_block > 0 && + (pixel_block & -pixel_block) == pixel_block && + ((8U*PNG_ROW_BUFFER_BYTE_ALIGN-1U) & (pixel_block*bpp)) == 0U); + + /* Zlib maxima */ + { + png_uint_32 max = (uInt)-1; /* max bytes */ + + if (bpp <= 8U) + { + /* Maximum number of bytes PNG can generate in the lower bit depth + * cases: + */ + png_uint_32 png_max = + (0x7FFFFFFF + PNG_ADDOF(bpp)) >> PNG_SHIFTOF(bpp); + + if (png_max < max) + max = 0x7FFFFFFF; + } + + else /* bpp > 8U */ + { + max /= byte_pp; + if (max > 0x7FFFFFFF) + max = 0x7FFFFFFF; + } + + /* So this is the maximum number of pixels regardless of alignment: */ + ps->zlib_max_pixels = max; + + /* For byte alignment the value has to be a multiple of pixel_block and + * that is a power of 2, so: + */ + ps->zlib_max_aligned_pixels = max & ~(pixel_block-1U); + } + +# ifdef PNG_WRITE_FILTER_SUPPORTED + /* PNG_ROW_BUFFER maxima; this is easier because PNG_ROW_BUFFER_SIZE is + * limited so that the number of bits fits in any ANSI-C (unsigned int). + */ + { + const unsigned int max = (8U * PNG_ROW_BUFFER_SIZE) / bpp; + + ps->row_buffer_max_pixels = max; + ps->row_buffer_max_aligned_pixels = max & ~(pixel_block-1U); + } +# endif /* WRITE_FILTER */ + + /* NOTE: this will be 0 for very long rows on 32-bit or less systems */ + ps->write_row_size = png_write_row_buffer_size(png_ptr); +} + +static png_zlib_statep +get_zlib_state(png_structrp png_ptr) +{ + if (png_ptr->zlib_state == NULL) + png_create_zlib_state(png_ptr); + + return png_ptr->zlib_state; +} + +/* Internal API to clean up all the deflate related stuff, including the buffer + * lists. + */ +static void /* PRIVATE */ +png_deflate_release(png_structrp png_ptr, png_zlib_statep ps, int check) +{ +# ifdef PNG_WRITE_FILTER_SUPPORTED + /* Free any mode-specific data that is owned here: */ + if (ps->previous_write_row != NULL) + { + png_bytep p = ps->previous_write_row; + ps->previous_write_row = NULL; + png_free(png_ptr, p); + } + +# ifdef PNG_SELECT_FILTER_SUPPORTED + if (ps->current_write_row != NULL) + { + png_bytep p = ps->current_write_row; + ps->current_write_row = NULL; + png_free(png_ptr, p); + } + + if (ps->selector != NULL) + { + struct filter_selector *s = ps->selector; + ps->selector = NULL; + png_free(png_ptr, s); + } +# endif /* SELECT_FILTER */ +# endif /* WRITE_FILTER */ + + /* The main z_stream opaque pointer needs to remain set to png_ptr; it is + * only set once. + */ + png_zlib_compress_destroy(&ps->s, check); + png_free_compression_buffer(png_ptr, &ps->stash); +} + +void /* PRIVATE */ +png_deflate_destroy(png_structrp png_ptr) +{ + png_zlib_statep ps = png_ptr->zlib_state; + + if (ps != NULL) + { + png_deflate_release(png_ptr, ps, 0/*check*/); + png_ptr->zlib_state = NULL; + png_free(png_ptr, ps); + } +} + +/* Compression settings. + * + * These are stored packed into a png_uint_32 to make comparison with the + * current setting quick. The packing method uses four bits for each setting + * and reserves '0' for unset. + * + * ps__base: The lowest valid value (encoded as 1). + * ps__max: The highest valid value. + * ps__pos: The position in the range 0..3 (shift of 0..12). + * + * The low 16 bits are the zlib compression parameters: + */ +#define pz_level_base (-1) +#define pz_level_max 9 +#define pz_level_pos 0 +#define pz_windowBits_base 8 +#define pz_windowBits_max 15 +#define pz_windowBits_pos 1 +#define pz_memLevel_base 1 +#define pz_memLevel_max 9 +#define pz_memLevel_pos 2 +#define pz_strategy_base 0 +#define pz_strategy_max 4 +#define pz_strategy_pos 3 +#define pz_zlib_bits 0xFFFFU +/* Anything below this is not used directly by zlib: */ +#define pz_png_level_base 0 +#define pz_png_level_max 6 +#define pz_png_level_pos 4 + +#define pz_offset(name) (pz_ ## name ## _base - 1) + /* setting_value == pz_offset(setting)+encoded_value */ +#define pz_min(name) pz_ ## name ## _base +#define pz_max(name) pz_ ## name ## _max +#define pz_shift(name) (4 * pz_ ## name ## _pos) + +#define pz_bits(name,x) ((int)(((x)>>pz_shift(name))&0xF)) + /* the encoded value, or 0 if unset */ + +/* Enquiries: */ +#define pz_isset(name,x) (pz_bits(name,x) != 0) +#define pz_value(name,x) (pz_bits(name,x)+pz_offset(name)) + +/* Assignments: */ +#define pz_clear(name,x) ((x)&~((png_uint_32)0xFU<pz_ ## type) +#define pz_get(ps, type, name, def)\ + (pz_isset(name, pz_var(ps, type)) ? pz_value(name, pz_var(ps, type)) : (def)) +/* pz_assign checks for out-of-range values and clears the setting if these are + * given. No warning or error is generated. + */ +#define pz_assign(ps, type, name, value)\ + (pz_var(ps, type) = pz_clear(name, pz_var(ps, type)) |\ + ((value) >= pz_min(name) && (value) <= pz_max(name) ?\ + pz_encode(name, value) : 0)) + +static png_int_32 +pz_compression_setting(png_structrp png_ptr, png_uint_32 owner, + int min, int max, int shift, png_int_32 value, int only_get, int unset) + /* This is a support function for png_write_setting below. */ +{ + png_zlib_statep ps; + png_uint_32p psettings; + + /* The value is only required for a 'set', eliminate out-of-range values + * first: + */ + if (!only_get && (value < min || value > max)) + return PNG_EDOM; + + /* If setting a value make sure the state exists: */ + if (!only_get) + ps = get_zlib_state(png_ptr); + + else if (owner != 0U) /* ps may be NULL */ + ps = png_ptr->zlib_state; + + else /* get and owner is 0U */ + return 0; /* supported */ + + psettings = NULL; + switch (owner) + { + png_int_32 res; + + case png_IDAT: + if (ps != NULL) psettings = &ps->pz_IDAT; + break; + + case png_iCCP: + if (ps != NULL) psettings = &ps->pz_iCCP; + break; + + case 0U: + /* All the settings. At this point the 'get' case has returned 0 + * above, the value has been checked and the paramter is 0, therefore + * valid. Each of the following calls should succeed and it would be + * reasonable to eliminate the PNG_FAILED tests in a world where + * software engineers never made mistakes. + */ + res = pz_compression_setting(png_ptr, png_IDAT, min, max, shift, + value, 0/*set*/, 1/*iff unset*/); + + if (PNG_FAILED(res)) + return res; + + res = pz_compression_setting(png_ptr, png_iCCP, min, max, shift, + value, 0/*set*/, 1/*iff unset*/); + + if (PNG_FAILED(res)) + return res; + + /* The text settings are changed regardless of the customize support + * because if WRITE_CUSTOMIZE_ZTXT_COMPRESSION is not supported the old + * behavior was to use the WRITE_CUSTOMIZE_COMPRESSION setting. + * + * However, when we get png_zTXt directly (from png_write_setting) and + * the support is not compiled in return PNG_ENOSYS. + */ + unset = 1; /* i.e. only if not already set */ + +# ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED + case png_zTXt: + case png_iTXt: +# endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ + if (ps != NULL) psettings = &ps->pz_text; + break; + + default: + /* Return PNG_ENOSYS, not PNG_EINVAL, to support future addition of new + * compressed chunks and the fact that zTXt and iTXt customization can + * be disabled. + */ + return PNG_ENOSYS; + } + + if (psettings == NULL) + return PNG_UNSET; /* valid setting that is not set */ + + { + png_uint_32 settings = *psettings; + png_uint_32 mask = 0xFU << shift; + + /* Do not set it if 'only_get' was passed in or if 'unset' is true and the + * setting is not currently set: + */ + if (!only_get && ((settings & mask) == 0U || !unset)) + *psettings = (settings & ~mask) + + ((png_uint_32)/*SAFE*/(value-min+1) << shift); + + settings &= mask; + + if (settings == 0U) + return PNG_UNSET; + + else + return (int)/*SAFE*/((settings >> shift)-1U) + min; + } +} + +#define compression_setting(pp, owner, setting, value, get)\ + pz_compression_setting(pp, owner, pz_min(setting), pz_max(setting),\ + pz_shift(setting), value, get, 0/*always*/) + +/* There is (as of zlib 1.2.8) a bug in the implementation of compression with a + * window size of 256 which zlib works round by resetting windowBits from 8 to 9 + * whenever deflateInit2 is called with that value. Fix this up here. + */ +static void +fix_cinfo(png_zlib_statep ps, png_bytep data, png_alloc_size_t data_size) +{ + /* Do this if the CINFO field is '1', meaning windowBits of 9. The first + * byte of the stream is the CMF value, CINFO is in the upper four bits. + * + * If zlib didn't futz with the value then it should match the value in + * pz_current; check this is debug. (See below for why this works in the + * pz_default_settings call.) + */ +# define png_ptr png_voidcast(png_const_structrp, ps->s.zs.opaque) + if (data[0] == 0x18U && + pz_get(ps, current, windowBits, 0) == 8 /* i.e. it was requested */) + { + /* Double check this here; the fixup only works if the data was 256 bytes + * or shorter *or* the window is never used. For safety repeat the checks + * done in pz_default_settings; technically we should be able to just skip + * this test. + * + * TODO: set a 'fixup' flag in zlib_state to make this quicker? + */ + if (data_size <= 256U || + pz_get(ps, current, strategy, Z_RLE) == Z_HUFFMAN_ONLY || + pz_get(ps, current, level, 1) == Z_NO_COMPRESSION) + { + unsigned int d1; + + data[0] = 0x08U; + /* The header checksum must be fixed too. The FCHECK (low 5 bits) make + * CMF.FLG a multiple of 31: + */ + d1 = data[1] & 0xE0U; /* top three bits */ + d1 += 31U - (0x0800U + d1) % 31U; + data[1] = PNG_BYTE(d1); + } + + else /* pz_default_settings is expected to guarantee the above */ + NOT_REACHED; + } + + else if (data_size > 0U) + { + /* Prior to 1.7.0 libpng would shrink the windowBits even if the + * application requested a particular value, so: + */ + unsigned int z_cinfo = data[0] >> 4; + unsigned int half_z_window_size = 1U << (z_cinfo + 7); + + if (data_size <= half_z_window_size && z_cinfo > 0) + { + unsigned int tmp; + + do + { + half_z_window_size >>= 1; + --z_cinfo; + } + while (z_cinfo > 0 && data_size <= half_z_window_size); + + data[0] = PNG_BYTE((z_cinfo << 4) + 0x8U); + tmp = data[1] & 0xE0U; /* top three bits */ + tmp += 31U - ((data[0] << 8) + tmp) % 31U; + data[1] = PNG_BYTE(tmp); + } + } + + else + NOT_REACHED; /* invalid data size (0) */ +# undef png_ptr +} + +static png_uint_32 +pz_default_settings(png_uint_32 settings, const png_uint_32 owner, + const png_alloc_size_t data_size, const unsigned int filters/*for IDAT*/) +{ + int png_level, strategy, zlib_level, windowBits; + + /* The png 'level' parameter controls the defaults below. It uses the same + * numbering scheme as the Zlib compression level except that -1 invokes the + * set of options and, in some cases, libpng behavior of libpng 1.6 and + * earlier. + * + * In the comments below reference is made to the differences beteen the + * legacy compression sizes from libpng 1.6 and earlier and the result of + * using the various options. These are quoted as an overall size change in + * the compression of 147323 PNG test files. The set of test files is + * slightly restricted because pre-1.7 versions of png_read_png leave random + * bits into the final byte of a row which ends with a partial byte. This + * affects the compression unpredictably so such files were omitted from the + * measurements. + */ + if (!pz_isset(png_level, settings)) + { + png_level = PNG_DEFAULT_COMPRESSION_LEVEL; + settings |= pz_encode(png_level, png_level); + } + + else + png_level = pz_value(png_level, settings); + + /* First default the strategy. At lower data sizes other strategies do as + * well as the zlib default compression strategy but they never seem to + * improve on it with the 1.7 filtering. + */ + if (!pz_isset(strategy, settings)) + { + switch (png_level) + { + case PNG_COMPRESSION_COMPAT: /* Legacy setting */ + /* The pre-1.7 code used Z_FILTERED normally but uses + * Z_DEFAULT_STRATEGY for palette or low-bit-depth images. + * + * In fact Z_DEFAULT_STRATEGY works best for filtered images as + * well, however the change in results is small: + * + * Z_DEFAULT_STRATEGY: -0.1% + * Z_FILTERED: +0.1% + * + * NOTE: this happened even if WRITE_FILTER was *not* supported. + */ + if (owner != png_IDAT || filters == PNG_FILTER_NONE) + strategy = Z_DEFAULT_STRATEGY; + + else + strategy = Z_FILTERED; + break; + + case PNG_COMPRESSION_HIGH_SPEED: + /* RLE is as fast as HUFFMAN_ONLY and can reduce size a lot in a few + * cases. + */ + strategy = Z_RLE; + break; + + default: /* For GCC */ + case PNG_COMPRESSION_LOW: + case PNG_COMPRESSION_MEDIUM: + /* Z_FILTERED is almost as good as the default and can be + * significantly faster. It biases the algorithm towards smaller + * byte values. + * + * Using Z_DEFAULT_STRATEGY here, rather than Z_FILTERED, benefits + * smaller 8 and 16-bit gray and larger 8 and 16-bit RGB images, + * however the overall gain is only 0.1% because it is offset by + * losses in larger 8-bit gray and alpha images. It is extremely + * difficult to deduce a pattern other than biases in the test set + * of images. + * + * Looking at the pattern of behavior with the 1.6 filter selection + * algorithm (none of palette or low-bit-depth, else all) produces + * results as follows: + */ + if (owner == png_IDAT) + { + if (filters == PNG_FILTER_NONE) + strategy = Z_DEFAULT_STRATEGY; + + else + strategy = Z_FILTERED; + } + + else if (owner == png_iCCP) + strategy = Z_DEFAULT_STRATEGY; + + /* TODO: investigate this, the observed behavior is suspicious: */ + else /* text chunk */ + strategy = Z_FILTERED; /* Always better for some reason */ + break; + + case PNG_COMPRESSION_LOW_MEMORY: + /* Reduce memory at all costs, speed doesn't matter. */ + case PNG_COMPRESSION_HIGH_READ_SPEED: + case PNG_COMPRESSION_HIGH: + if (owner == png_IDAT || owner == png_iCCP) + strategy = Z_DEFAULT_STRATEGY; + + else + strategy = Z_FILTERED; + break; + } + + settings |= pz_encode(strategy, strategy); + } + + else + strategy = pz_value(strategy, settings); + + /* Next the zlib level; this just defaults to the png level, except that for + * Huffman or RLE encoding the level setting for Zlib doesn't matter. + */ + if (!pz_isset(level, settings)) + { + switch (strategy) + { + case Z_HUFFMAN_ONLY: + case Z_RLE: + /* The 'level' doesn't make any significant difference to the + * compression with these strategies; in a test set of about 3GByte + * of PNG files the total compressed size changed under 20 bytes + * with libpng 1.6! + */ + zlib_level = 1; + break; + + default: /* Z_FIXED, Z_FILTERED, Z_DEFAULT_STRATEGY */ + /* Everything that uses the window seems to show rapidly diminishing + * returns above level 6 (at least with libpng 1.6). + * Z_DEFAULT_COMPRESSION is, in fact, level 6 so Mark seems to + * concur. With libpng 1.6 the following results were obtained + * using the full test set of files (including those with a partial + * byte at the end of the row) and just varying the zlib level: + * + * LEVEL SIZE(bytes) CHANGE TIME(s) CHANGE METRIC + * 9 2550246600 -1.19% 1972 +227% -77% + * 8 2556675866 -0.94% 1215 +101% -59% + * 7 2572685552 -0.32% 679 +12% -15% + * 6 2581196708 0% 604 0% 0% + * 5 2602831249 +0.84% 414 -30% +87% + * 4 2625206800 +1.71% 358 -40% +153% + * 3 2674752349 +3.62% 298 -50% +303% + * 2 2716261483 +5.23% 262 -56% +537% + * 1 2749875805 +6.53% 251 -57% +662% + * 0 7174488347 202 -66% + * + * The CHANGE columns express the change in compressed size + * (positive is an increase; a decrease in compression) and time + * (positive is an increase; an increase in time) relative to level + * 6. The METRIC column is a measure of the compression-per-second + * relative to level 6; positive is an increase in + * compression-per-second. + * + * The metric is derived by assuming the difference in time between + * level 0 (which does no compression) and the level being + * considered is spent doing the compression. (Reasonable, since + * only the level changed). Just the inverse of the product of the + * size and the time difference is a measure of compression per + * second. It can be seen that time dominates the metric; + * compression only varies slightly (under 8%) across the level + * range. + */ + switch (png_level) + { + case PNG_COMPRESSION_COMPAT: + zlib_level = Z_DEFAULT_COMPRESSION; /* NOTE: -1 */ + break; + + case PNG_COMPRESSION_HIGH_SPEED: + zlib_level = 1; + break; + + default: /* For GCC */ + case PNG_COMPRESSION_LOW: + zlib_level = 3; + break; + + case PNG_COMPRESSION_MEDIUM: + zlib_level = 6; /* Old default! */ + break; + + case PNG_COMPRESSION_LOW_MEMORY: + case PNG_COMPRESSION_HIGH_READ_SPEED: + case PNG_COMPRESSION_HIGH: + zlib_level = 9; + break; + } + break; + } + + settings |= pz_encode(level, zlib_level); + } + + else + zlib_level = pz_value(level, settings); + + /* Now default windowBits. This is probably the most important of the + * settings because it is pretty much the only one that affects decode + * performance. The smaller the better: + */ + if (!pz_isset(windowBits, settings)) + { + if (png_level == PNG_COMPRESSION_COMPAT/* Legacy */) + { + /* This is the libpng16 calculation (it is wrong; a misunderstanding of + * what zlib actually requires!) + * + * Using the code below with the legacy choice of Z_FILTERED or + * Z_DEFAULT_STRATEGY increases the size of the test files by only + * 0.04%, however the settings below considerably reduce the windowBits + * used potentially benefitting read code a lot. + * + * NOTE: the algorithm below was determined by experiment and + * observation with the same set of test files; there is some + * considerable possibility that a different set might show different + * results. Obtaining large, representative, test sets is both a + * considerable amount of work and very error prone. [JB 20160518] + */ + windowBits = 15; + + if (data_size <= 16384U) + { + unsigned int half_window_size = 1U << (windowBits-1); + + while (data_size + 262U <= half_window_size) + { + half_window_size >>= 1; + --windowBits; + } + } + } + + /* The window size affects the memory used on both read and write but also + * the time on write (but not normally read). Handle the low memory + * requirement first: + */ + else if (zlib_level == Z_NO_COMPRESSION || + png_level == PNG_COMPRESSION_LOW_MEMORY) + windowBits = 8; + + /* If the strategy has been set to something that doesn't benefit from + * higher windowBits values take advantage of this. Note that pz_value + * returns an invalid value if pz_isset is false. + * + * The only png_level that affects this decision is HIGH_SPEED, because + * a smaller windowBits should speed up the search, however the code above + * chose zlib_level based on this so ignore that consideration and just + * use zlib_level below. + */ + else switch (strategy) + { + png_alloc_size_t test_size; + + case Z_HUFFMAN_ONLY: + /* Use the minimum; the window doesn't get used */ + windowBits = 8; + break; + + case Z_RLE: + /* The longest length code is 258 bytes, the shortest string that + * can achieve this is 259 bytes long; 259 copies of the same byte + * which can be encoded as a code for the byte value then a string + * of length 258 starting at the first byte. So if the data is + * longer than 256 bytes use '9' for the windowBits, otherwise use + * 8: + */ + if (data_size <= 256U) + windowBits = 8; + + else + windowBits = 9; + break; + + /* By experiment using about 150,000 files the optimal windowBits + * value across a range of files is somewhat less than implied by + * the data size and depends on the zlib level and the strategy + * used, the following values were determined by experiment using + * those files: + */ + case Z_FILTERED: + /* The Z_FILTERED case changes suddenly at (zlib) level 4 to + * benefit from looking at all the data: + */ + if (zlib_level < 4 && zlib_level != Z_DEFAULT_COMPRESSION/*-1: 6*/) + test_size = data_size / 8U; + + else + test_size = data_size; + + goto check_test_size; + + case Z_FIXED: + /* With the fixed Huffman tables better compression only ever comes + * from looking for matches, so, logically: + */ + test_size = data_size; + goto check_test_size; + + default: + /* The default algorithm always does better with a window smaller + * than all the data and shows jumps at level 4 and level 8. The + * net effect with the test set of images is a very minor overall + * improvement compared to the pre-1.7 calculation (data size + + * 262). The benefit is less than 0.01%, however smaller window + * sizes reduce the memory zlib has to allocate in the decoder. + */ + switch (zlib_level) + { + case 1: case 2: case 3: + test_size = data_size / 8U; + break; + + default: /* -1(Z_DEFAULT_COMPRESSION) == 6, 4..7 */ + /* This includes, implicitly, ZLIB_NO_COMPRESSION, but that + * was eliminated in the 'if' above. + */ + test_size = data_size / 4U; + break; + + case 8: case 9: + test_size = data_size / 3U; + break; + } + + goto check_test_size; + + check_test_size: + /* Find the smallest window that covers 'test_size' bytes, subject + * to the constraint that if the actual data size is more than 256 + * bytes the minimum windowBits that can be supported is 9: + */ + if (data_size <= 256U) + windowBits = 8; + + else + windowBits = 9; + + while (windowBits < 15 && (1U << windowBits) < test_size) + ++windowBits; + + break; + } + + settings |= pz_encode(windowBits, windowBits); + } + + else + windowBits = pz_value(windowBits, settings); + + /* zlib has a problem with 256 byte windows; 512 is used instead. + * We can't work round this if the data size is more than 256 bytes and + * the strategy actually uses the window (everything except huffman-only) + * so fix the problem here. + */ + if (windowBits == 8 && data_size > 256U && strategy != Z_HUFFMAN_ONLY && + zlib_level != Z_NO_COMPRESSION) + settings = pz_change(windowBits, settings, 9); + + /* For memLevel this just increases the memory used but can help with the + * Huffman code generation even to level 9 (the maximum), so just set the + * max. This affects memory used, not (apparently) compression speed so + * the only relevant png_level is LOW_MEMORY. + * + * The legacy setting is '8'; this is the level that Zlib defaults to because + * 16-bit iAPX86 systems could not handle '9'. Because MAX_MEM_LEVEL is used + * below this does not matter; zconf.h selects 8 or 9 as appropriate. + * + * In fact using '9' with the legacy settings increases the size of the test + * set minutely; +0.007%. This is hardly significant; 0.007% of the test + * images equals 10 images. (Nevertheless it is interesting, just as the + * observation that decreasing windowBits can result in smaller compressed + * sizes is interesting.) + */ + if (!pz_isset(memLevel, settings)) + { + int memLevel; + + switch (png_level) + { + case PNG_COMPRESSION_COMPAT: + memLevel = 8; + break; + + case PNG_COMPRESSION_LOW_MEMORY: + memLevel = 1; + break; + + default: + memLevel = MAX_MEM_LEVEL/*from zconf.h*/; + break; + } + + settings |= pz_encode(memLevel, memLevel); + } + + return settings; +} + +/* This is used below to find the size of an image to pass to png_deflate_claim. + * It returns 0 for images whose size would overflow a 32-bit integer or have + * rows which cannot be allocated. + */ +static png_alloc_size_t +png_image_size(png_const_structrp png_ptr) +{ + /* The size returned here is limited to PNG_SIZE_MAX, if the size would + * exceed that (or is close to exceeding that) 0 is returned. See below for + * a variant that limits the size of 0xFFFFFFFFU. + */ + const png_alloc_size_t rowbytes = png_ptr->zlib_state->write_row_size; + + /* NON-INTERLACED: (1+rowbytes) * h + * INTERLACED: Each pixel is transmitted exactly once, so the size is + * (rowbytes * h) + the count of filter bytes. Each complete + * block of 8 image rows generates at most 15 output rows + * (less for narrow images), so the filter byte count is + * at most (15*h/8)+14. Because the original rows are split + * extra byte passing may be introduced. Account for this by + * allowing an extra 1 byte per output row; that's two bytes + * including the filer byte. + * + * So: + * NON-INTERLACED: (rowbytes * h) + h + * INTERLACED: < (rowbytes * h) + 2*(15 * h/8) + 2*15 + * + * Hence: + */ + if (rowbytes != 0) + { + const png_uint_32 h = png_ptr->height; + + if (png_ptr->interlaced == PNG_INTERLACE_NONE) + { + const png_alloc_size_t limit = PNG_SIZE_MAX / h; + + /* On 16-bit systems the above might be 0, so: */ + if (rowbytes width; + + /* Interlacing makes the image larger because of the replication of + * both the filter byte and the padding to a byte boundary. + */ + png_alloc_size_t cb_base; + int pass; + + for (cb_base=0, pass=0; pass 0) + { + const png_uint_32 pass_h = PNG_PASS_ROWS(h, pass); + + if (pass_h > 0) + { + /* This is the number of bytes available for each row of this + * pass: + */ + const png_alloc_size_t limit = (PNG_SIZE_MAX - cb_base)/pass_h; + /* This cannot overflow because if it did rowbytes would + * have been 0 above. + */ + const png_alloc_size_t pass_bytes = + PNG_ROWBYTES(png_ptr->row_output_pixel_depth, pass_w); + + if (pass_bytes zowner == 0); + + { + int ret; /* zlib return code */ + unsigned int filters = 0U; + png_uint_32 settings; + + switch (owner) + { + case png_IDAT: + debug(data_size == 0U); + data_size = png_image_size(png_ptr); + + if (data_size == 0U) + data_size = PNG_SIZE_MAX; + + settings = ps->pz_IDAT; +# ifdef PNG_WRITE_FILTER_SUPPORTED + filters = ps->filter_mask; + debug(filters != 0U); +# else /* !WRITE_FILTER */ + filters = PNG_FILTER_NONE; +# endif /* !WRITE_FILTER */ + break; + + case png_iCCP: + settings = ps->pz_iCCP; + break; + + default: /* text chunk */ + settings = ps->pz_text; + break; + } + + settings = pz_default_settings(settings, owner, data_size, filters); + + /* Check against the previous initialized values, if any. The relevant + * settings are in the low 16 bits. + */ + if (ps->s.zs.state != NULL && + ((settings ^ ps->pz_current) & pz_zlib_bits) != 0U) + png_deflateEnd(png_ptr, &ps->s.zs, 0/*check*/); + + /* For safety clear out the input and output pointers (currently zlib + * doesn't use them on Init, but it might in the future). + */ + ps->s.zs.next_in = NULL; + ps->s.zs.avail_in = 0; + ps->s.zs.next_out = NULL; + ps->s.zs.avail_out = 0; + + /* The length fields must be cleared too and the lists reset: */ + ps->s.overflow = ps->s.len = ps->s.start = 0U; + + if (ps->s.list != NULL) /* error in prior chunk writing */ + { + debug(ps->stash == NULL); + ps->stash = ps->s.list; + ps->s.list = NULL; + } + + ps->s.end = &ps->s.list; + + /* Now initialize if required, setting the new parameters, otherwise just + * do a simple reset to the previous parameters. + */ + if (ps->s.zs.state != NULL) + ret = deflateReset(&ps->s.zs); + + else + ret = deflateInit2(&ps->s.zs, pz_value(level, settings), Z_DEFLATED, + pz_value(windowBits, settings), pz_value(memLevel, settings), + pz_value(strategy, settings)); + + ps->pz_current = settings; + + /* The return code is from either deflateReset or deflateInit2; they have + * pretty much the same set of error codes. + */ + if (ret == Z_OK && ps->s.zs.state != NULL) + png_ptr->zowner = owner; + + else + { + png_zstream_error(&ps->s.zs, ret); + png_error(png_ptr, ps->s.zs.msg); + } + } + + return ps; +} + +#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED /* includes iCCP */ +/* Compress the block of data at the end of a chunk. This claims and releases + * png_struct::z_stream. It returns the amount of data in the chunk list or + * zero on error (a zlib stream always contains some bytes!) + * + * prefix_len is the amount of (uncompressed) data before the start of the + * compressed data. The routine will return 0 if the total of the compressed + * data and the prefix exceeds PNG_UINT_MAX_31. + * + * NOTE: this function may not return; it only returns 0 if + * png_chunk_report(PNG_CHUNK_WRITE_ERROR) returns (not the default). + */ +static int /* success */ +png_compress_chunk_data(png_structrp png_ptr, png_uint_32 chunk_name, + png_uint_32 prefix_len, png_const_voidp input, png_alloc_size_t input_len) +{ /* To find the length of the output it is necessary to first compress the - * input, the result is buffered rather than using the two-pass algorithm + * input. The result is buffered rather than using the two-pass algorithm * that is used on the inflate side; deflate is assumed to be slower and a * PNG writer is assumed to have more memory available than a PNG reader. * @@ -501,168 +1843,115 @@ png_text_compress(png_structrp png_ptr, png_uint_32 chunk_name, * size so it is likely to be more efficient to use this linked-list * approach. */ - ret = png_deflate_claim(png_ptr, chunk_name, comp->input_len); + png_zlib_statep ps = png_deflate_claim(png_ptr, chunk_name, input_len); - if (ret != Z_OK) - return ret; + affirm(ps != NULL); + + /* The data compression function always returns so that we can clean up. */ + ps->s.zs.next_in = PNGZ_INPUT_CAST(png_voidcast(const Bytef*, input)); + + /* Use the stash, if available: */ + debug(ps->s.list == NULL); + ps->s.list = ps->stash; + ps->stash = NULL; - /* Set up the compression buffers, we need a loop here to avoid overflowing a - * uInt. Use ZLIB_IO_MAX to limit the input. The output is always limited - * by the output buffer size, so there is no need to check that. Since this - * is ANSI-C we know that an 'int', hence a uInt, is always at least 16 bits - * in size. - */ { - png_compression_bufferp *end = &png_ptr->zbuffer_list; - png_alloc_size_t input_len = comp->input_len; /* may be zero! */ - png_uint_32 output_len; + int ret = png_compress(&ps->s, input_len, PNG_UINT_31_MAX-prefix_len, + Z_FINISH); - /* zlib updates these for us: */ - png_ptr->zstream.next_in = PNGZ_INPUT_CAST(comp->input); - png_ptr->zstream.avail_in = 0; /* Set below */ - png_ptr->zstream.next_out = comp->output; - png_ptr->zstream.avail_out = (sizeof comp->output); + ps->s.zs.next_out = NULL; /* safety */ + ps->s.zs.avail_out = 0; + ps->s.zs.next_in = NULL; + ps->s.zs.avail_in = 0; + png_ptr->zowner = 0; /* release png_ptr::zstream */ - output_len = png_ptr->zstream.avail_out; - - do + /* Since Z_FINISH was passed as the flush parameter any result other than + * Z_STREAM_END is an error. In any case in the event of an error free + * the whole compression state; the only expected error is Z_MEM_ERROR. + */ + if (ret != Z_STREAM_END) { - uInt avail_in = ZLIB_IO_MAX; + png_zlib_compress_destroy(&ps->s, 0/*check*/); - if (avail_in > input_len) - avail_in = (uInt)input_len; - - input_len -= avail_in; - - png_ptr->zstream.avail_in = avail_in; - - if (png_ptr->zstream.avail_out == 0) - { - png_compression_buffer *next; - - /* Chunk data is limited to 2^31 bytes in length, so the prefix - * length must be counted here. - */ - if (output_len + prefix_len > PNG_UINT_31_MAX) - { - ret = Z_MEM_ERROR; - break; - } - - /* Need a new (malloc'ed) buffer, but there may be one present - * already. - */ - next = *end; - if (next == NULL) - { - next = png_voidcast(png_compression_bufferp, png_malloc_base - (png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr))); - - if (next == NULL) - { - ret = Z_MEM_ERROR; - break; - } - - /* Link in this buffer (so that it will be freed later) */ - next->next = NULL; - *end = next; - } - - png_ptr->zstream.next_out = next->output; - png_ptr->zstream.avail_out = png_ptr->zbuffer_size; - output_len += png_ptr->zstream.avail_out; - - /* Move 'end' to the next buffer pointer. */ - end = &next->next; - } - - /* Compress the data */ - ret = deflate(&png_ptr->zstream, - input_len > 0 ? Z_NO_FLUSH : Z_FINISH); - - /* Claw back input data that was not consumed (because avail_in is - * reset above every time round the loop). + /* This is not very likely given the PNG_UINT_31_MAX limit above, but + * if code is added to limit the size of the chunks produced it can + * start to happen. */ - input_len += png_ptr->zstream.avail_in; - png_ptr->zstream.avail_in = 0; /* safety */ + if (ret == Z_BUF_ERROR) + ps->s.zs.msg = PNGZ_MSG_CAST("compressed chunk too long"); + + else + png_zstream_error(&ps->s.zs, ret); + + png_chunk_report(png_ptr, ps->s.zs.msg, PNG_CHUNK_WRITE_ERROR); + return 0; } - while (ret == Z_OK); - - /* There may be some space left in the last output buffer, this needs to - * be subtracted from output_len. - */ - output_len -= png_ptr->zstream.avail_out; - png_ptr->zstream.avail_out = 0; /* safety */ - comp->output_len = output_len; - - /* Now double check the output length, put in a custom message if it is - * too long. Otherwise ensure the z_stream::msg pointer is set to - * something. - */ - if (output_len + prefix_len >= PNG_UINT_31_MAX) - { - png_ptr->zstream.msg = PNGZ_MSG_CAST("compressed data too long"); - ret = Z_MEM_ERROR; - } - - else - png_zstream_error(png_ptr, ret); - - /* Reset zlib for another zTXt/iTXt or image data */ - png_ptr->zowner = 0; - - /* The only success case is Z_STREAM_END, input_len must be 0, if not this - * is an internal error. - */ - if (ret == Z_STREAM_END && input_len == 0) - { - /* Fix up the deflate header, if required */ - optimize_cmf(comp->output, comp->input_len); - - /* But Z_OK is returned, not Z_STREAM_END; this allows the claim - * function above to return Z_STREAM_END on an error (though it never - * does in the current versions of zlib.) - */ - return Z_OK; - } - - else - return ret; } + + /* png_compress is meant to guarantee this on a successful return: */ + affirm(ps->s.overflow == 0U && ps->s.len <= PNG_UINT_31_MAX - prefix_len); + + /* Correct the zlib CINFO field: */ + if (ps->s.len >= 2U) + fix_cinfo(ps, ps->s.list->output, input_len); + + return 1; } -/* Ship the compressed text out via chunk writes */ -static void -png_write_compressed_data_out(png_structrp png_ptr, compression_state *comp) +/* Return the length of the compressed data; this is effectively a debug + * function to catch inconsistencies caused by internal errors. It will + * disappear in a release build. + */ +#if PNG_RELEASE_BUILD +# define png_length_compressed_chunk_data(pp, p) ((pp)->zlib_state->s.len) +#else /* !RELEASE_BUILD */ +static png_uint_32 +png_length_compressed_chunk_data(png_structrp png_ptr, png_uint_32 p) { - png_uint_32 output_len = comp->output_len; - png_const_bytep output = comp->output; - png_uint_32 avail = (sizeof comp->output); - png_compression_buffer *next = png_ptr->zbuffer_list; + png_zlib_statep ps = png_ptr->zlib_state; - for (;;) + debug(ps != NULL && ps->s.overflow == 0U && ps->s.len <= PNG_UINT_31_MAX-p); + return ps->s.len; +} +#endif /* !RELEASE_BUILD */ + +/* Write all the data produced by the above function; the caller must write the + * prefix and chunk header. + */ +static void +png_write_compressed_chunk_data(png_structrp png_ptr) +{ + png_zlib_statep ps = png_ptr->zlib_state; + png_compression_bufferp next; + png_uint_32 output_len; + + affirm(ps != NULL && ps->s.overflow == 0U); + next = ps->s.list; + + for (output_len = ps->s.len; output_len > 0U; next = next->next) { - if (avail > output_len) - avail = output_len; + png_uint_32 size = PNG_ROW_BUFFER_SIZE; - png_write_chunk_data(png_ptr, output, avail); + /* If this affirm fails there is a bug in the calculation of + * output_length above, or in the buffer_limit code in png_compress. + */ + affirm(next != NULL && output_len > 0U); - output_len -= avail; + if (size > output_len) + size = output_len; - if (output_len == 0 || next == NULL) - break; + png_write_chunk_data(png_ptr, next->output, size); - avail = png_ptr->zbuffer_size; - output = next->output; - next = next->next; + output_len -= size; } - /* This is an internal error; 'next' must have been NULL! */ - if (output_len > 0) - png_error(png_ptr, "error writing ancillary chunked compressed data"); + /* Release the list back to the stash. */ + debug(ps->stash == NULL); + ps->stash = ps->s.list; + ps->s.list = NULL; + ps->s.end = &ps->s.list; } -#endif /* PNG_WRITE_COMPRESSED_TEXT_SUPPORTED */ +#endif /* WRITE_COMPRESSED_TEXT */ #if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) @@ -672,15 +1961,15 @@ png_write_compressed_data_out(png_structrp png_ptr, compression_state *comp) * length, forbids leading or trailing whitespace, multiple internal spaces, * and the non-break space (0x80) from ISO 8859-1. Returns keyword length. * - * The 'new_key' buffer must be 80 characters in size (for the keyword plus a - * trailing '\0'). If this routine returns 0 then there was no keyword, or a - * valid one could not be generated, and the caller must png_error. + * The 'new_key' buffer must be at least 80 characters in size (for the keyword + * plus a trailing '\0'). If this routine returns 0 then there was no keyword, + * or a valid one could not be generated, and the caller must CHUNK_WRITE_ERROR. */ -static png_uint_32 +static unsigned int png_check_keyword(png_structrp png_ptr, png_const_charp key, png_bytep new_key) { png_const_charp orig_key = key; - png_uint_32 key_len = 0; + unsigned int key_len = 0; int bad_character = 0; int space = 1; @@ -715,7 +2004,7 @@ png_check_keyword(png_structrp png_ptr, png_const_charp key, png_bytep new_key) bad_character = ch; /* just skip it, record the first error */ } - if (key_len > 0 && space) /* trailing space */ + if (key_len > 0 && space != 0) /* trailing space */ { --key_len, --new_key; if (bad_character == 0) @@ -728,9 +2017,10 @@ png_check_keyword(png_structrp png_ptr, png_const_charp key, png_bytep new_key) if (key_len == 0) return 0; +#ifdef PNG_WARNINGS_SUPPORTED /* Try to only output one warning per keyword: */ - if (*key) /* keyword too long */ - png_warning(png_ptr, "keyword truncated"); + if (*key != 0) /* keyword too long */ + png_app_warning(png_ptr, "keyword truncated"); else if (bad_character != 0) { @@ -741,10 +2031,11 @@ png_check_keyword(png_structrp png_ptr, png_const_charp key, png_bytep new_key) png_formatted_warning(png_ptr, p, "keyword \"@1\": bad character '0x@2'"); } +#endif /* WARNINGS */ return key_len; } -#endif +#endif /* WRITE_TEXT || WRITE_pCAL || WRITE_iCCP || WRITE_sPLT */ /* Write the IHDR chunk, and update the png_struct with the necessary * information. Note that the rest of this code depends upon this @@ -752,7 +2043,7 @@ png_check_keyword(png_structrp png_ptr, png_const_charp key, png_bytep new_key) */ void /* PRIVATE */ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, - int bit_depth, int color_type, int compression_type, int filter_type, + int bit_depth, int color_type, int compression_type, int filter_method, int interlace_type) { png_byte buf[13]; /* Buffer to store the IHDR info */ @@ -772,11 +2063,10 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, #ifdef PNG_WRITE_16BIT_SUPPORTED case 16: #endif - png_ptr->channels = 1; break; + break; default: - png_error(png_ptr, - "Invalid bit depth for grayscale image"); + png_error(png_ptr, "Invalid bit depth for grayscale image"); } break; @@ -788,7 +2078,6 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, #endif png_error(png_ptr, "Invalid bit depth for RGB image"); - png_ptr->channels = 3; break; case PNG_COLOR_TYPE_PALETTE: @@ -798,7 +2087,6 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, case 2: case 4: case 8: - png_ptr->channels = 1; break; default: @@ -810,7 +2098,6 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, if (bit_depth != 8 && bit_depth != 16) png_error(png_ptr, "Invalid bit depth for grayscale+alpha image"); - png_ptr->channels = 2; break; case PNG_COLOR_TYPE_RGB_ALPHA: @@ -821,7 +2108,6 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, #endif png_error(png_ptr, "Invalid bit depth for RGBA image"); - png_ptr->channels = 4; break; default: @@ -830,7 +2116,7 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, if (compression_type != PNG_COMPRESSION_TYPE_BASE) { - png_warning(png_ptr, "Invalid compression type specified"); + png_app_error(png_ptr, "Invalid compression type specified"); compression_type = PNG_COMPRESSION_TYPE_BASE; } @@ -844,71 +2130,46 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, * 5. The color_type is RGB or RGBA */ if ( -#ifdef PNG_MNG_FEATURES_SUPPORTED - !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && - ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) && - (color_type == PNG_COLOR_TYPE_RGB || - color_type == PNG_COLOR_TYPE_RGB_ALPHA) && - (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) && -#endif - filter_type != PNG_FILTER_TYPE_BASE) +# ifdef PNG_MNG_FEATURES_SUPPORTED + !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 && + ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && + (filter_method == PNG_INTRAPIXEL_DIFFERENCING)) && +# endif /* MNG_FEATURES */ + filter_method != PNG_FILTER_TYPE_BASE) { - png_warning(png_ptr, "Invalid filter type specified"); - filter_type = PNG_FILTER_TYPE_BASE; + png_app_error(png_ptr, "Invalid filter type specified"); + filter_method = PNG_FILTER_TYPE_BASE; } -#ifdef PNG_WRITE_INTERLACING_SUPPORTED if (interlace_type != PNG_INTERLACE_NONE && interlace_type != PNG_INTERLACE_ADAM7) { - png_warning(png_ptr, "Invalid interlace type specified"); + png_app_error(png_ptr, "Invalid interlace type specified"); interlace_type = PNG_INTERLACE_ADAM7; } -#else - interlace_type=PNG_INTERLACE_NONE; -#endif - /* Save the relevent information */ - png_ptr->bit_depth = (png_byte)bit_depth; - png_ptr->color_type = (png_byte)color_type; - png_ptr->interlaced = (png_byte)interlace_type; -#ifdef PNG_MNG_FEATURES_SUPPORTED - png_ptr->filter_type = (png_byte)filter_type; -#endif - png_ptr->compression_type = (png_byte)compression_type; + /* Save the relevant information */ + png_ptr->bit_depth = png_check_byte(png_ptr, bit_depth); + png_ptr->color_type = png_check_byte(png_ptr, color_type); + png_ptr->interlaced = png_check_byte(png_ptr, interlace_type); + png_ptr->filter_method = png_check_byte(png_ptr, filter_method); png_ptr->width = width; png_ptr->height = height; - png_ptr->pixel_depth = (png_byte)(bit_depth * png_ptr->channels); - png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width); - /* Set the usr info, so any transformations can modify it */ - png_ptr->usr_width = png_ptr->width; - png_ptr->usr_bit_depth = png_ptr->bit_depth; - png_ptr->usr_channels = png_ptr->channels; - /* Pack the header information into the buffer */ png_save_uint_32(buf, width); png_save_uint_32(buf + 4, height); - buf[8] = (png_byte)bit_depth; - buf[9] = (png_byte)color_type; - buf[10] = (png_byte)compression_type; - buf[11] = (png_byte)filter_type; - buf[12] = (png_byte)interlace_type; + buf[8] = png_check_byte(png_ptr, bit_depth); + buf[9] = png_check_byte(png_ptr, color_type); + buf[10] = png_check_byte(png_ptr, compression_type); + buf[11] = png_check_byte(png_ptr, filter_method); + buf[12] = png_check_byte(png_ptr, interlace_type); /* Write the chunk */ png_write_complete_chunk(png_ptr, png_IHDR, buf, (png_size_t)13); - - if ((png_ptr->do_filter) == PNG_NO_FILTERS) - { - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE || - png_ptr->bit_depth < 8) - png_ptr->do_filter = PNG_FILTER_NONE; - - else - png_ptr->do_filter = PNG_ALL_FILTERS; - } - - png_ptr->mode = PNG_HAVE_IHDR; /* not READY_FOR_ZTXT */ + png_ptr->mode |= PNG_HAVE_IHDR; } /* Write the palette. We are careful not to trust png_color to be in the @@ -917,19 +2178,22 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, */ void /* PRIVATE */ png_write_PLTE(png_structrp png_ptr, png_const_colorp palette, - png_uint_32 num_pal) + unsigned int num_pal) { - png_uint_32 i; + png_uint_32 max_palette_length, i; png_const_colorp pal_ptr; png_byte buf[3]; png_debug(1, "in png_write_PLTE"); + max_palette_length = (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) ? + (1 << png_ptr->bit_depth) : PNG_MAX_PALETTE_LENGTH; + if (( -#ifdef PNG_MNG_FEATURES_SUPPORTED - !(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) && -#endif - num_pal == 0) || num_pal > 256) +# ifdef PNG_MNG_FEATURES_SUPPORTED + (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0 && +# endif /* MNG_FEATURES */ + num_pal == 0) || num_pal > max_palette_length) { if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { @@ -943,7 +2207,7 @@ png_write_PLTE(png_structrp png_ptr, png_const_colorp palette, } } - if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR)) + if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0) { png_warning(png_ptr, "Ignoring request to write a PLTE chunk in grayscale PNG"); @@ -951,195 +2215,23 @@ png_write_PLTE(png_structrp png_ptr, png_const_colorp palette, return; } - png_ptr->num_palette = (png_uint_16)num_pal; + png_ptr->num_palette = png_check_bits(png_ptr, num_pal, 9); png_debug1(3, "num_palette = %d", png_ptr->num_palette); - png_write_chunk_header(png_ptr, png_PLTE, (png_uint_32)(num_pal * 3)); -#ifdef PNG_POINTER_INDEXING_SUPPORTED + png_write_chunk_header(png_ptr, png_PLTE, num_pal * 3U); for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++) { buf[0] = pal_ptr->red; buf[1] = pal_ptr->green; buf[2] = pal_ptr->blue; - png_write_chunk_data(png_ptr, buf, (png_size_t)3); + png_write_chunk_data(png_ptr, buf, 3U); } -#else - /* This is a little slower but some buggy compilers need to do this - * instead - */ - pal_ptr=palette; - - for (i = 0; i < num_pal; i++) - { - buf[0] = pal_ptr[i].red; - buf[1] = pal_ptr[i].green; - buf[2] = pal_ptr[i].blue; - png_write_chunk_data(png_ptr, buf, (png_size_t)3); - } - -#endif png_write_chunk_end(png_ptr); png_ptr->mode |= PNG_HAVE_PLTE; } -/* This is similar to png_text_compress, above, except that it does not require - * all of the data at once and, instead of buffering the compressed result, - * writes it as IDAT chunks. Unlike png_text_compress it *can* png_error out - * because it calls the write interface. As a result it does its own error - * reporting and does not return an error code. In the event of error it will - * just call png_error. The input data length may exceed 32-bits. The 'flush' - * parameter is exactly the same as that to deflate, with the following - * meanings: - * - * Z_NO_FLUSH: normal incremental output of compressed data - * Z_SYNC_FLUSH: do a SYNC_FLUSH, used by png_write_flush - * Z_FINISH: this is the end of the input, do a Z_FINISH and clean up - * - * The routine manages the acquire and release of the png_ptr->zstream by - * checking and (at the end) clearing png_ptr->zowner, it does some sanity - * checks on the 'mode' flags while doing this. - */ -void /* PRIVATE */ -png_compress_IDAT(png_structrp png_ptr, png_const_bytep input, - png_alloc_size_t input_len, int flush) -{ - if (png_ptr->zowner != png_IDAT) - { - /* First time. Ensure we have a temporary buffer for compression and - * trim the buffer list if it has more than one entry to free memory. - * If 'WRITE_COMPRESSED_TEXT' is not set the list will never have been - * created at this point, but the check here is quick and safe. - */ - if (png_ptr->zbuffer_list == NULL) - { - png_ptr->zbuffer_list = png_voidcast(png_compression_bufferp, - png_malloc(png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr))); - png_ptr->zbuffer_list->next = NULL; - } - - else - png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list->next); - - /* It is a terminal error if we can't claim the zstream. */ - if (png_deflate_claim(png_ptr, png_IDAT, png_image_size(png_ptr)) != Z_OK) - png_error(png_ptr, png_ptr->zstream.msg); - - /* The output state is maintained in png_ptr->zstream, so it must be - * initialized here after the claim. - */ - png_ptr->zstream.next_out = png_ptr->zbuffer_list->output; - png_ptr->zstream.avail_out = png_ptr->zbuffer_size; - } - - /* Now loop reading and writing until all the input is consumed or an error - * terminates the operation. The _out values are maintained across calls to - * this function, but the input must be reset each time. - */ - png_ptr->zstream.next_in = PNGZ_INPUT_CAST(input); - png_ptr->zstream.avail_in = 0; /* set below */ - for (;;) - { - int ret; - - /* INPUT: from the row data */ - uInt avail = ZLIB_IO_MAX; - - if (avail > input_len) - avail = (uInt)input_len; /* safe because of the check */ - - png_ptr->zstream.avail_in = avail; - input_len -= avail; - - ret = deflate(&png_ptr->zstream, input_len > 0 ? Z_NO_FLUSH : flush); - - /* Include as-yet unconsumed input */ - input_len += png_ptr->zstream.avail_in; - png_ptr->zstream.avail_in = 0; - - /* OUTPUT: write complete IDAT chunks when avail_out drops to zero, note - * that these two zstream fields are preserved across the calls, therefore - * there is no need to set these up on entry to the loop. - */ - if (png_ptr->zstream.avail_out == 0) - { - png_bytep data = png_ptr->zbuffer_list->output; - uInt size = png_ptr->zbuffer_size; - - /* Write an IDAT containing the data then reset the buffer. The - * first IDAT may need deflate header optimization. - */ -# ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED - if (!(png_ptr->mode & PNG_HAVE_IDAT) && - png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE) - optimize_cmf(data, png_image_size(png_ptr)); -# endif - - png_write_complete_chunk(png_ptr, png_IDAT, data, size); - png_ptr->mode |= PNG_HAVE_IDAT; - - png_ptr->zstream.next_out = data; - png_ptr->zstream.avail_out = size; - - /* For SYNC_FLUSH or FINISH it is essential to keep calling zlib with - * the same flush parameter until it has finished output, for NO_FLUSH - * it doesn't matter. - */ - if (ret == Z_OK && flush != Z_NO_FLUSH) - continue; - } - - /* The order of these checks doesn't matter much; it just effect which - * possible error might be detected if multiple things go wrong at once. - */ - if (ret == Z_OK) /* most likely return code! */ - { - /* If all the input has been consumed then just return. If Z_FINISH - * was used as the flush parameter something has gone wrong if we get - * here. - */ - if (input_len == 0) - { - if (flush == Z_FINISH) - png_error(png_ptr, "Z_OK on Z_FINISH with output space"); - - return; - } - } - - else if (ret == Z_STREAM_END && flush == Z_FINISH) - { - /* This is the end of the IDAT data; any pending output must be - * flushed. For small PNG files we may still be at the beginning. - */ - png_bytep data = png_ptr->zbuffer_list->output; - uInt size = png_ptr->zbuffer_size - png_ptr->zstream.avail_out; - -# ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED - if (!(png_ptr->mode & PNG_HAVE_IDAT) && - png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE) - optimize_cmf(data, png_image_size(png_ptr)); -# endif - - png_write_complete_chunk(png_ptr, png_IDAT, data, size); - png_ptr->zstream.avail_out = 0; - png_ptr->zstream.next_out = NULL; - png_ptr->mode |= PNG_HAVE_IDAT | PNG_AFTER_IDAT; - - png_ptr->zowner = 0; /* Release the stream */ - return; - } - - else - { - /* This is an error condition. */ - png_zstream_error(png_ptr, ret); - png_error(png_ptr, png_ptr->zstream.msg); - } - } -} - /* Write an IEND chunk */ void /* PRIVATE */ png_write_IEND(png_structrp png_ptr) @@ -1150,6 +2242,40 @@ png_write_IEND(png_structrp png_ptr) png_ptr->mode |= PNG_HAVE_IEND; } +#if defined(PNG_WRITE_gAMA_SUPPORTED) || defined(PNG_WRITE_cHRM_SUPPORTED) +static int +png_save_int_31(png_structrp png_ptr, png_bytep buf, png_int_32 i) + /* Save a signed value as a PNG unsigned value; the argument is required to + * be in the range 0..0x7FFFFFFFU. If not a *warning* is produced and false + * is returned. Because this is only called from png_write_cHRM_fixed and + * png_write_gAMA_fixed below this is safe (we don't need either chunk, + * particularly if the value is bogus.) + * + * The warning is png_app_error; it may return if the app tells it to but the + * app can have it error out. JB 20150821: I believe the checking in png.c + * actually makes this error impossible, but this is safe. + */ +{ +#ifndef __COVERITY__ + if (i >= 0 && i <= 0x7FFFFFFF) +#else + /* Supress bogus Coverity complaint */ + if (i >= 0) +#endif + { + png_save_uint_32(buf, (png_uint_32)/*SAFE*/i); + return 1; + } + + else + { + png_chunk_report(png_ptr, "negative value in cHRM or gAMA", + PNG_CHUNK_WRITE_ERROR); + return 0; + } +} +#endif /* WRITE_gAMA || WRITE_cHRM */ + #ifdef PNG_WRITE_gAMA_SUPPORTED /* Write a gAMA chunk */ void /* PRIVATE */ @@ -1160,8 +2286,8 @@ png_write_gAMA_fixed(png_structrp png_ptr, png_fixed_point file_gamma) png_debug(1, "in png_write_gAMA"); /* file_gamma is saved in 1/100,000ths */ - png_save_uint_32(buf, (png_uint_32)file_gamma); - png_write_complete_chunk(png_ptr, png_gAMA, buf, (png_size_t)4); + if (png_save_int_31(png_ptr, buf, file_gamma)) + png_write_complete_chunk(png_ptr, png_gAMA, buf, (png_size_t)4); } #endif @@ -1175,10 +2301,10 @@ png_write_sRGB(png_structrp png_ptr, int srgb_intent) png_debug(1, "in png_write_sRGB"); if (srgb_intent >= PNG_sRGB_INTENT_LAST) - png_warning(png_ptr, - "Invalid sRGB rendering intent specified"); + png_chunk_report(png_ptr, "Invalid sRGB rendering intent specified", + PNG_CHUNK_WRITE_ERROR); - buf[0]=(png_byte)srgb_intent; + buf[0] = png_check_byte(png_ptr, srgb_intent); png_write_complete_chunk(png_ptr, png_sRGB, buf, (png_size_t)1); } #endif @@ -1187,59 +2313,37 @@ png_write_sRGB(png_structrp png_ptr, int srgb_intent) /* Write an iCCP chunk */ void /* PRIVATE */ png_write_iCCP(png_structrp png_ptr, png_const_charp name, - png_const_bytep profile) + png_const_voidp profile) { png_uint_32 name_len; png_uint_32 profile_len; png_byte new_name[81]; /* 1 byte for the compression byte */ - compression_state comp; png_debug(1, "in png_write_iCCP"); - /* These are all internal problems: the profile should have been checked - * before when it was stored. - */ - if (profile == NULL) - png_error(png_ptr, "No profile for iCCP chunk"); /* internal error */ + affirm(profile != NULL); profile_len = png_get_uint_32(profile); - - if (profile_len < 132) - png_error(png_ptr, "ICC profile too short"); - - if (profile_len & 0x03) - png_error(png_ptr, "ICC profile length invalid (not a multiple of 4)"); - - { - png_uint_32 embedded_profile_len = png_get_uint_32(profile); - - if (profile_len != embedded_profile_len) - png_error(png_ptr, "Profile length does not match profile"); - } - name_len = png_check_keyword(png_ptr, name, new_name); if (name_len == 0) - png_error(png_ptr, "iCCP: invalid keyword"); + { + png_chunk_report(png_ptr, "iCCP: invalid keyword", PNG_CHUNK_WRITE_ERROR); + return; + } - new_name[++name_len] = PNG_COMPRESSION_TYPE_BASE; + ++name_len; /* trailing '\0' */ + new_name[name_len++] = PNG_COMPRESSION_TYPE_BASE; - /* Make sure we include the NULL after the name and the compression type */ - ++name_len; - - png_text_compress_init(&comp, profile, profile_len); - - /* Allow for keyword terminator and compression byte */ - if (png_text_compress(png_ptr, png_iCCP, &comp, name_len) != Z_OK) - png_error(png_ptr, png_ptr->zstream.msg); - - png_write_chunk_header(png_ptr, png_iCCP, name_len + comp.output_len); - - png_write_chunk_data(png_ptr, new_name, name_len); - - png_write_compressed_data_out(png_ptr, &comp); - - png_write_chunk_end(png_ptr); + if (png_compress_chunk_data(png_ptr, png_iCCP, name_len, profile, + profile_len)) + { + png_write_chunk_header(png_ptr, png_iCCP, + name_len+png_length_compressed_chunk_data(png_ptr, name_len)); + png_write_chunk_data(png_ptr, new_name, name_len); + png_write_compressed_chunk_data(png_ptr); + png_write_chunk_end(png_ptr); + } } #endif @@ -1254,9 +2358,6 @@ png_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette) png_size_t entry_size = (spalette->depth == 8 ? 6 : 10); png_size_t palette_size = entry_size * spalette->nentries; png_sPLT_entryp ep; -#ifndef PNG_POINTER_INDEXING_SUPPORTED - int i; -#endif png_debug(1, "in png_write_sPLT"); @@ -1269,21 +2370,19 @@ png_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette) png_write_chunk_header(png_ptr, png_sPLT, (png_uint_32)(name_len + 2 + palette_size)); - png_write_chunk_data(png_ptr, (png_bytep)new_name, - (png_size_t)(name_len + 1)); + png_write_chunk_data(png_ptr, new_name, name_len + 1); - png_write_chunk_data(png_ptr, &spalette->depth, (png_size_t)1); + png_write_chunk_data(png_ptr, &spalette->depth, 1); /* Loop through each palette entry, writing appropriately */ -#ifdef PNG_POINTER_INDEXING_SUPPORTED for (ep = spalette->entries; epentries + spalette->nentries; ep++) { if (spalette->depth == 8) { - entrybuf[0] = (png_byte)ep->red; - entrybuf[1] = (png_byte)ep->green; - entrybuf[2] = (png_byte)ep->blue; - entrybuf[3] = (png_byte)ep->alpha; + entrybuf[0] = png_check_byte(png_ptr, ep->red); + entrybuf[1] = png_check_byte(png_ptr, ep->green); + entrybuf[2] = png_check_byte(png_ptr, ep->blue); + entrybuf[3] = png_check_byte(png_ptr, ep->alpha); png_save_uint_16(entrybuf + 4, ep->frequency); } @@ -1298,31 +2397,6 @@ png_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette) png_write_chunk_data(png_ptr, entrybuf, entry_size); } -#else - ep=spalette->entries; - for (i = 0; i>spalette->nentries; i++) - { - if (spalette->depth == 8) - { - entrybuf[0] = (png_byte)ep[i].red; - entrybuf[1] = (png_byte)ep[i].green; - entrybuf[2] = (png_byte)ep[i].blue; - entrybuf[3] = (png_byte)ep[i].alpha; - png_save_uint_16(entrybuf + 4, ep[i].frequency); - } - - else - { - png_save_uint_16(entrybuf + 0, ep[i].red); - png_save_uint_16(entrybuf + 2, ep[i].green); - png_save_uint_16(entrybuf + 4, ep[i].blue); - png_save_uint_16(entrybuf + 6, ep[i].alpha); - png_save_uint_16(entrybuf + 8, ep[i].frequency); - } - - png_write_chunk_data(png_ptr, entrybuf, entry_size); - } -#endif png_write_chunk_end(png_ptr); } @@ -1339,18 +2413,17 @@ png_write_sBIT(png_structrp png_ptr, png_const_color_8p sbit, int color_type) png_debug(1, "in png_write_sBIT"); /* Make sure we don't depend upon the order of PNG_COLOR_8 */ - if (color_type & PNG_COLOR_MASK_COLOR) + if ((color_type & PNG_COLOR_MASK_COLOR) != 0) { - png_byte maxbits; + unsigned int maxbits; - maxbits = (png_byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 : - png_ptr->usr_bit_depth); + maxbits = color_type==PNG_COLOR_TYPE_PALETTE ? 8 : png_ptr->bit_depth; if (sbit->red == 0 || sbit->red > maxbits || sbit->green == 0 || sbit->green > maxbits || sbit->blue == 0 || sbit->blue > maxbits) { - png_warning(png_ptr, "Invalid sBIT depth specified"); + png_app_error(png_ptr, "Invalid sBIT depth specified"); return; } @@ -1362,9 +2435,9 @@ png_write_sBIT(png_structrp png_ptr, png_const_color_8p sbit, int color_type) else { - if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth) + if (sbit->gray == 0 || sbit->gray > png_ptr->bit_depth) { - png_warning(png_ptr, "Invalid sBIT depth specified"); + png_app_error(png_ptr, "Invalid sBIT depth specified"); return; } @@ -1372,11 +2445,11 @@ png_write_sBIT(png_structrp png_ptr, png_const_color_8p sbit, int color_type) size = 1; } - if (color_type & PNG_COLOR_MASK_ALPHA) + if ((color_type & PNG_COLOR_MASK_ALPHA) != 0) { - if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth) + if (sbit->alpha == 0 || sbit->alpha > png_ptr->bit_depth) { - png_warning(png_ptr, "Invalid sBIT depth specified"); + png_app_error(png_ptr, "Invalid sBIT depth specified"); return; } @@ -1397,19 +2470,15 @@ png_write_cHRM_fixed(png_structrp png_ptr, const png_xy *xy) png_debug(1, "in png_write_cHRM"); /* Each value is saved in 1/100,000ths */ - png_save_int_32(buf, xy->whitex); - png_save_int_32(buf + 4, xy->whitey); - - png_save_int_32(buf + 8, xy->redx); - png_save_int_32(buf + 12, xy->redy); - - png_save_int_32(buf + 16, xy->greenx); - png_save_int_32(buf + 20, xy->greeny); - - png_save_int_32(buf + 24, xy->bluex); - png_save_int_32(buf + 28, xy->bluey); - - png_write_complete_chunk(png_ptr, png_cHRM, buf, 32); + if (png_save_int_31(png_ptr, buf, xy->whitex) && + png_save_int_31(png_ptr, buf + 4, xy->whitey) && + png_save_int_31(png_ptr, buf + 8, xy->redx) && + png_save_int_31(png_ptr, buf + 12, xy->redy) && + png_save_int_31(png_ptr, buf + 16, xy->greenx) && + png_save_int_31(png_ptr, buf + 20, xy->greeny) && + png_save_int_31(png_ptr, buf + 24, xy->bluex) && + png_save_int_31(png_ptr, buf + 28, xy->bluey)) + png_write_complete_chunk(png_ptr, png_cHRM, buf, 32); } #endif @@ -1425,31 +2494,37 @@ png_write_tRNS(png_structrp png_ptr, png_const_bytep trans_alpha, if (color_type == PNG_COLOR_TYPE_PALETTE) { - if (num_trans <= 0 || num_trans > png_ptr->num_palette) + affirm(num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH); { - /* This is an error which can only be reliably detected late. */ - png_app_error(png_ptr, - "Invalid number of transparent colors specified"); - return; - } +# ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + union + { + png_uint_32 u32[1]; + png_byte b8[PNG_MAX_PALETTE_LENGTH]; + } inverted_alpha; - /* Write the chunk out as it is */ - png_write_complete_chunk(png_ptr, png_tRNS, trans_alpha, - (png_size_t)num_trans); + /* Invert the alpha channel (in tRNS) if required */ + if (png_ptr->write_invert_alpha) + { + int i; + + memcpy(inverted_alpha.b8, trans_alpha, num_trans); + + for (i=0; 4*igray >= (1 << png_ptr->bit_depth)) - { - /* This can no longer happen because it is checked in png_set_tRNS */ - png_app_error(png_ptr, - "Ignoring attempt to write tRNS chunk out-of-range for bit_depth"); - - return; - } - + affirm(tran->gray < (1 << png_ptr->bit_depth)); png_save_uint_16(buf, tran->gray); png_write_complete_chunk(png_ptr, png_tRNS, buf, (png_size_t)2); } @@ -1460,26 +2535,12 @@ png_write_tRNS(png_structrp png_ptr, png_const_bytep trans_alpha, png_save_uint_16(buf, tran->red); png_save_uint_16(buf + 2, tran->green); png_save_uint_16(buf + 4, tran->blue); -#ifdef PNG_WRITE_16BIT_SUPPORTED - if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) -#else - if (buf[0] | buf[2] | buf[4]) -#endif - { - /* Also checked in png_set_tRNS */ - png_app_error(png_ptr, - "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8"); - return; - } - + affirm(png_ptr->bit_depth == 8 || (buf[0] | buf[2] | buf[4]) == 0); png_write_complete_chunk(png_ptr, png_tRNS, buf, (png_size_t)6); } - else - { - /* Checked in png_set_tRNS */ - png_app_error(png_ptr, "Can't write tRNS with an alpha channel"); - } + else /* Already checked in png_set_tRNS */ + impossible("invalid tRNS"); } #endif @@ -1495,13 +2556,13 @@ png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type) if (color_type == PNG_COLOR_TYPE_PALETTE) { if ( -#ifdef PNG_MNG_FEATURES_SUPPORTED - (png_ptr->num_palette || - (!(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE))) && -#endif +# ifdef PNG_MNG_FEATURES_SUPPORTED + (png_ptr->num_palette != 0 || + (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0) && +# endif /* MNG_FEATURES */ back->index >= png_ptr->num_palette) { - png_warning(png_ptr, "Invalid background palette index"); + png_app_error(png_ptr, "Invalid background palette index"); return; } @@ -1509,18 +2570,18 @@ png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type) png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)1); } - else if (color_type & PNG_COLOR_MASK_COLOR) + else if ((color_type & PNG_COLOR_MASK_COLOR) != 0) { png_save_uint_16(buf, back->red); png_save_uint_16(buf + 2, back->green); png_save_uint_16(buf + 4, back->blue); #ifdef PNG_WRITE_16BIT_SUPPORTED - if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) + if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]) != 0) #else - if (buf[0] | buf[2] | buf[4]) + if ((buf[0] | buf[2] | buf[4]) != 0) #endif { - png_warning(png_ptr, + png_app_error(png_ptr, "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8"); return; @@ -1533,7 +2594,7 @@ png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type) { if (back->gray >= (1 << png_ptr->bit_depth)) { - png_warning(png_ptr, + png_app_error(png_ptr, "Ignoring attempt to write bKGD chunk out-of-range for bit_depth"); return; @@ -1582,7 +2643,7 @@ void /* PRIVATE */ png_write_tEXt(png_structrp png_ptr, png_const_charp key, png_const_charp text, png_size_t text_len) { - png_uint_32 key_len; + unsigned int key_len; png_byte new_key[80]; png_debug(1, "in png_write_tEXt"); @@ -1590,7 +2651,10 @@ png_write_tEXt(png_structrp png_ptr, png_const_charp key, png_const_charp text, key_len = png_check_keyword(png_ptr, key, new_key); if (key_len == 0) - png_error(png_ptr, "tEXt: invalid keyword"); + { + png_chunk_report(png_ptr, "tEXt: invalid keyword", PNG_CHUNK_WRITE_ERROR); + return; + } if (text == NULL || *text == '\0') text_len = 0; @@ -1599,7 +2663,10 @@ png_write_tEXt(png_structrp png_ptr, png_const_charp key, png_const_charp text, text_len = strlen(text); if (text_len > PNG_UINT_31_MAX - (key_len+1)) - png_error(png_ptr, "tEXt: text too long"); + { + png_chunk_report(png_ptr, "tEXt: text too long", PNG_CHUNK_WRITE_ERROR); + return; + } /* Make sure we include the 0 after the key */ png_write_chunk_header(png_ptr, png_tEXt, @@ -1623,51 +2690,38 @@ png_write_tEXt(png_structrp png_ptr, png_const_charp key, png_const_charp text, /* Write a compressed text chunk */ void /* PRIVATE */ png_write_zTXt(png_structrp png_ptr, png_const_charp key, png_const_charp text, - png_size_t text_len, int compression) + int compression) { - png_uint_32 key_len; + unsigned int key_len; png_byte new_key[81]; - compression_state comp; png_debug(1, "in png_write_zTXt"); - PNG_UNUSED(text_len) /* Always use strlen */ - - if (compression == PNG_TEXT_COMPRESSION_NONE) - { - png_write_tEXt(png_ptr, key, text, 0); - return; - } if (compression != PNG_TEXT_COMPRESSION_zTXt) - png_error(png_ptr, "zTXt: invalid compression type"); + png_app_warning(png_ptr, "zTXt: invalid compression type ignored"); key_len = png_check_keyword(png_ptr, key, new_key); if (key_len == 0) - png_error(png_ptr, "zTXt: invalid keyword"); + { + png_chunk_report(png_ptr, "zTXt: invalid keyword", PNG_CHUNK_WRITE_ERROR); + return; + } /* Add the compression method and 1 for the keyword separator. */ - new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE; ++key_len; + new_key[key_len++] = PNG_COMPRESSION_TYPE_BASE; - /* Compute the compressed data; do it now for the length */ - png_text_compress_init(&comp, (png_const_bytep)text, - text == NULL ? 0 : strlen(text)); + if (png_compress_chunk_data(png_ptr, png_zTXt, key_len, text, strlen(text))) + { + png_write_chunk_header(png_ptr, png_zTXt, + key_len+png_length_compressed_chunk_data(png_ptr, key_len)); + png_write_chunk_data(png_ptr, new_key, key_len); + png_write_compressed_chunk_data(png_ptr); + png_write_chunk_end(png_ptr); + } - if (png_text_compress(png_ptr, png_zTXt, &comp, key_len) != Z_OK) - png_error(png_ptr, png_ptr->zstream.msg); - - /* Write start of chunk */ - png_write_chunk_header(png_ptr, png_zTXt, key_len + comp.output_len); - - /* Write key */ - png_write_chunk_data(png_ptr, new_key, key_len); - - /* Write the compressed data */ - png_write_compressed_data_out(png_ptr, &comp); - - /* Close the chunk */ - png_write_chunk_end(png_ptr); + /* else chunk report already issued and ignored */ } #endif @@ -1677,99 +2731,139 @@ void /* PRIVATE */ png_write_iTXt(png_structrp png_ptr, int compression, png_const_charp key, png_const_charp lang, png_const_charp lang_key, png_const_charp text) { - png_uint_32 key_len, prefix_len; - png_size_t lang_len, lang_key_len; - png_byte new_key[82]; - compression_state comp; + png_uint_32 key_len, prefix_len, data_len; + png_size_t lang_len, lang_key_len, text_len; + png_byte new_key[82]; /* 80 bytes for the key, 2 byte compression info */ png_debug(1, "in png_write_iTXt"); key_len = png_check_keyword(png_ptr, key, new_key); if (key_len == 0) - png_error(png_ptr, "iTXt: invalid keyword"); + { + png_chunk_report(png_ptr, "iTXt: invalid keyword", PNG_CHUNK_WRITE_ERROR); + return; + } + + debug(new_key[key_len] == 0); + ++key_len; /* terminating 0 added by png_check_keyword */ /* Set the compression flag */ switch (compression) { case PNG_ITXT_COMPRESSION_NONE: case PNG_TEXT_COMPRESSION_NONE: - compression = new_key[++key_len] = 0; /* no compression */ + compression = new_key[key_len++] = 0; /* no compression */ break; case PNG_TEXT_COMPRESSION_zTXt: case PNG_ITXT_COMPRESSION_zTXt: - compression = new_key[++key_len] = 1; /* compressed */ + compression = new_key[key_len++] = 1; /* compressed */ break; default: - png_error(png_ptr, "iTXt: invalid compression"); + png_chunk_report(png_ptr, "iTXt: invalid compression", + PNG_CHUNK_WRITE_ERROR); + return; } - new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE; - ++key_len; /* for the keywod separator */ + new_key[key_len++] = PNG_COMPRESSION_TYPE_BASE; /* We leave it to the application to meet PNG-1.0 requirements on the * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of - * any non-Latin-1 characters except for NEWLINE. ISO PNG, however, - * specifies that the text is UTF-8 and this really doesn't require any - * checking. + * any non-Latin-1 characters except for NEWLINE (yes, this is really weird + * in an 'international' text string. ISO PNG, however, specifies that the + * text is UTF-8 and this *IS NOT YET CHECKED*, so invalid sequences may be + * present. * * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. * * TODO: validate the language tag correctly (see the spec.) */ if (lang == NULL) lang = ""; /* empty language is valid */ - lang_len = strlen(lang)+1; + lang_len = strlen(lang)+1U; if (lang_key == NULL) lang_key = ""; /* may be empty */ - lang_key_len = strlen(lang_key)+1; + lang_key_len = strlen(lang_key)+1U; if (text == NULL) text = ""; /* may be empty */ - prefix_len = key_len; - if (lang_len > PNG_UINT_31_MAX-prefix_len) - prefix_len = PNG_UINT_31_MAX; - else - prefix_len = (png_uint_32)(prefix_len + lang_len); + if (lang_len > PNG_UINT_31_MAX-key_len || + lang_key_len > PNG_UINT_31_MAX-key_len-lang_len) + { + png_chunk_report(png_ptr, "iTXt: prefix too long", PNG_CHUNK_WRITE_ERROR); + return; + } - if (lang_key_len > PNG_UINT_31_MAX-prefix_len) - prefix_len = PNG_UINT_31_MAX; - else - prefix_len = (png_uint_32)(prefix_len + lang_key_len); - - png_text_compress_init(&comp, (png_const_bytep)text, strlen(text)); + prefix_len = (png_uint_32)/*SAFE*/(key_len+lang_len+lang_key_len); + text_len = strlen(text); /* no trailing '\0' */ if (compression != 0) { - if (png_text_compress(png_ptr, png_iTXt, &comp, prefix_len) != Z_OK) - png_error(png_ptr, png_ptr->zstream.msg); + if (png_compress_chunk_data(png_ptr, png_iTXt, prefix_len, text, + text_len)) + data_len = png_length_compressed_chunk_data(png_ptr, prefix_len); + + else + return; /* chunk report already issued and ignored */ } else { - if (comp.input_len > PNG_UINT_31_MAX-prefix_len) - png_error(png_ptr, "iTXt: uncompressed text too long"); + if (text_len > PNG_UINT_31_MAX-prefix_len) + { + png_chunk_report(png_ptr, "iTXt: text too long", + PNG_CHUNK_WRITE_ERROR); + return; + } - /* So the string will fit in a chunk: */ - comp.output_len = (png_uint_32)/*SAFE*/comp.input_len; + data_len = (png_uint_32)/*SAFE*/text_len; } - png_write_chunk_header(png_ptr, png_iTXt, comp.output_len + prefix_len); - + png_write_chunk_header(png_ptr, png_iTXt, prefix_len+data_len); png_write_chunk_data(png_ptr, new_key, key_len); - - png_write_chunk_data(png_ptr, (png_const_bytep)lang, lang_len); - - png_write_chunk_data(png_ptr, (png_const_bytep)lang_key, lang_key_len); + png_write_chunk_data(png_ptr, lang, lang_len); + png_write_chunk_data(png_ptr, lang_key, lang_key_len); if (compression != 0) - png_write_compressed_data_out(png_ptr, &comp); + png_write_compressed_chunk_data(png_ptr); else - png_write_chunk_data(png_ptr, (png_const_bytep)text, comp.output_len); + png_write_chunk_data(png_ptr, text, data_len); png_write_chunk_end(png_ptr); } -#endif +#endif /* WRITE_iTXt */ + +#if defined(PNG_WRITE_oFFs_SUPPORTED) ||\ + defined(PNG_WRITE_pCAL_SUPPORTED) +/* PNG signed integers are saved in 32-bit 2's complement format. ANSI C-90 + * defines a cast of a signed integer to an unsigned integer either to preserve + * the value, if it is positive, or to calculate: + * + * (UNSIGNED_MAX+1) + integer + * + * Where UNSIGNED_MAX is the appropriate maximum unsigned value, so when the + * negative integral value is added the result will be an unsigned value + * correspnding to the 2's complement representation. + */ +static int +save_int_32(png_structrp png_ptr, png_bytep buf, png_int_32 j) +{ + png_uint_32 i = 0xFFFFFFFFU & (png_uint_32)/*SAFE & CORRECT*/j; + + if (i != 0x80000000U/*value not permitted*/) + { + png_save_uint_32(buf, i); + return 1; + } + + else + { + png_chunk_report(png_ptr, "invalid value in oFFS or pCAL", + PNG_CHUNK_WRITE_ERROR); + return 0; + } +} +#endif /* WRITE_oFFs || WRITE_pCAL */ #ifdef PNG_WRITE_oFFs_SUPPORTED /* Write the oFFs chunk */ @@ -1784,13 +2878,18 @@ png_write_oFFs(png_structrp png_ptr, png_int_32 x_offset, png_int_32 y_offset, if (unit_type >= PNG_OFFSET_LAST) png_warning(png_ptr, "Unrecognized unit type for oFFs chunk"); - png_save_int_32(buf, x_offset); - png_save_int_32(buf + 4, y_offset); - buf[8] = (png_byte)unit_type; - - png_write_complete_chunk(png_ptr, png_oFFs, buf, (png_size_t)9); + if (save_int_32(png_ptr, buf, x_offset) && + save_int_32(png_ptr, buf + 4, y_offset)) + { + /* unit type is 0 or 1, this has been checked already so the following + * is safe: + */ + buf[8] = unit_type != 0; + png_write_complete_chunk(png_ptr, png_oFFs, buf, (png_size_t)9); + } } -#endif +#endif /* WRITE_oFFs */ + #ifdef PNG_WRITE_pCAL_SUPPORTED /* Write the pCAL chunk (described in the PNG extensions document) */ void /* PRIVATE */ @@ -1799,11 +2898,9 @@ png_write_pCAL(png_structrp png_ptr, png_charp purpose, png_int_32 X0, png_charpp params) { png_uint_32 purpose_len; - png_size_t units_len, total_len; - png_size_tp params_len; + size_t units_len; png_byte buf[10]; png_byte new_purpose[80]; - int i; png_debug1(1, "in png_write_pCAL (%d parameters)", nparams); @@ -1820,41 +2917,44 @@ png_write_pCAL(png_structrp png_ptr, png_charp purpose, png_int_32 X0, png_debug1(3, "pCAL purpose length = %d", (int)purpose_len); units_len = strlen(units) + (nparams == 0 ? 0 : 1); png_debug1(3, "pCAL units length = %d", (int)units_len); - total_len = purpose_len + units_len + 10; - params_len = (png_size_tp)png_malloc(png_ptr, - (png_alloc_size_t)(nparams * (sizeof (png_size_t)))); - - /* Find the length of each parameter, making sure we don't count the - * null terminator for the last parameter. - */ - for (i = 0; i < nparams; i++) + if (save_int_32(png_ptr, buf, X0) && + save_int_32(png_ptr, buf + 4, X1)) { - params_len[i] = strlen(params[i]) + (i == nparams - 1 ? 0 : 1); - png_debug2(3, "pCAL parameter %d length = %lu", i, - (unsigned long)params_len[i]); - total_len += params_len[i]; + png_size_tp params_len = png_voidcast(png_size_tp, + png_malloc(png_ptr, nparams * sizeof (png_size_t))); + int i; + size_t total_len = purpose_len + units_len + 10; + + /* Find the length of each parameter, making sure we don't count the + * null terminator for the last parameter. + */ + for (i = 0; i < nparams; i++) + { + params_len[i] = strlen(params[i]) + (i == nparams - 1 ? 0 : 1); + png_debug2(3, "pCAL parameter %d length = %lu", i, + (unsigned long)params_len[i]); + total_len += params_len[i]; + } + + png_debug1(3, "pCAL total length = %d", (int)total_len); + png_write_chunk_header(png_ptr, png_pCAL, (png_uint_32)total_len); + png_write_chunk_data(png_ptr, new_purpose, purpose_len); + buf[8] = png_check_byte(png_ptr, type); + buf[9] = png_check_byte(png_ptr, nparams); + png_write_chunk_data(png_ptr, buf, (png_size_t)10); + png_write_chunk_data(png_ptr, (png_const_bytep)units, + (png_size_t)units_len); + + for (i = 0; i < nparams; i++) + png_write_chunk_data(png_ptr, (png_const_bytep)params[i], + params_len[i]); + + png_free(png_ptr, params_len); + png_write_chunk_end(png_ptr); } - - png_debug1(3, "pCAL total length = %d", (int)total_len); - png_write_chunk_header(png_ptr, png_pCAL, (png_uint_32)total_len); - png_write_chunk_data(png_ptr, new_purpose, purpose_len); - png_save_int_32(buf, X0); - png_save_int_32(buf + 4, X1); - buf[8] = (png_byte)type; - buf[9] = (png_byte)nparams; - png_write_chunk_data(png_ptr, buf, (png_size_t)10); - png_write_chunk_data(png_ptr, (png_const_bytep)units, (png_size_t)units_len); - - for (i = 0; i < nparams; i++) - { - png_write_chunk_data(png_ptr, (png_const_bytep)params[i], params_len[i]); - } - - png_free(png_ptr, params_len); - png_write_chunk_end(png_ptr); } -#endif +#endif /* WRITE_pCAL */ #ifdef PNG_WRITE_sCAL_SUPPORTED /* Write the sCAL chunk */ @@ -1877,7 +2977,7 @@ png_write_sCAL_s(png_structrp png_ptr, int unit, png_const_charp width, return; } - buf[0] = (png_byte)unit; + buf[0] = png_check_byte(png_ptr, unit); memcpy(buf + 1, width, wlen + 1); /* Append the '\0' here */ memcpy(buf + wlen + 2, height, hlen); /* Do NOT append the '\0' here */ @@ -1902,7 +3002,7 @@ png_write_pHYs(png_structrp png_ptr, png_uint_32 x_pixels_per_unit, png_save_uint_32(buf, x_pixels_per_unit); png_save_uint_32(buf + 4, y_pixels_per_unit); - buf[8] = (png_byte)unit_type; + buf[8] = png_check_byte(png_ptr, unit_type); png_write_complete_chunk(png_ptr, png_pHYs, buf, (png_size_t)9); } @@ -1938,1125 +3038,2017 @@ png_write_tIME(png_structrp png_ptr, png_const_timep mod_time) } #endif -#ifdef PNG_WRITE_FILTER_SUPPORTED -void /* PRIVATE */ -png_write_alloc_filter_row_buffers(png_structrp png_ptr, int filters) - /* Allocate row buffers for any filters that need them, this is also called - * from png_set_filter if the filters are changed during write to ensure that - * the required buffers exist. png_set_filter ensures that up/avg/paeth are - * only set if png_ptr->prev_row is allocated. - */ +static void +png_end_IDAT(png_structrp png_ptr) { - /* The buffer size is determined just by the output row size, not any - * processing requirements. - */ - png_alloc_size_t buf_size = png_ptr->rowbytes + 1; + png_zlib_statep ps = png_ptr->zlib_state; - if ((filters & PNG_FILTER_SUB) != 0 && png_ptr->sub_row == NULL) - { - png_ptr->sub_row = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size)); - png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; - } + png_ptr->zowner = 0U; /* release the stream */ - if ((filters & PNG_FILTER_UP) != 0 && png_ptr->up_row == NULL) - { - png_ptr->up_row = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size)); - png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; - } - - if ((filters & PNG_FILTER_AVG) != 0 && png_ptr->avg_row == NULL) - { - png_ptr->avg_row = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size)); - png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; - } - - if ((filters & PNG_FILTER_PAETH) != 0 && png_ptr->paeth_row == NULL) - { - png_ptr->paeth_row = png_voidcast(png_bytep, png_malloc(png_ptr, - buf_size)); - png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; - } + if (ps != NULL) + png_deflate_release(png_ptr, ps, 1/*check*/); } -#endif /* PNG_WRITE_FILTER_SUPPORTED */ -/* Initializes the row writing capability of libpng */ -void /* PRIVATE */ -png_write_start_row(png_structrp png_ptr) +static void +png_write_IDAT(png_structrp png_ptr, int flush) { -#ifdef PNG_WRITE_INTERLACING_SUPPORTED - /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + png_zlib_statep ps = png_ptr->zlib_state; + png_uint_32 IDAT_size; - /* Start of interlace block */ - static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; - - /* Offset to next interlace block */ - static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; - - /* Start of interlace block in the y direction */ - static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; - - /* Offset to next interlace block in the y direction */ - static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; -#endif - -#ifdef PNG_WRITE_FILTER_SUPPORTED - int filters; -#endif - - png_alloc_size_t buf_size; - int usr_pixel_depth; - - png_debug(1, "in png_write_start_row"); - - if (png_ptr == NULL) - return; - - usr_pixel_depth = png_ptr->usr_channels * png_ptr->usr_bit_depth; - buf_size = PNG_ROWBYTES(usr_pixel_depth, png_ptr->width) + 1; - - /* 1.5.6: added to allow checking in the row write code. */ - png_ptr->transformed_pixel_depth = png_ptr->pixel_depth; - png_ptr->maximum_pixel_depth = (png_byte)usr_pixel_depth; - - /* Set up row buffer */ - png_ptr->row_buf = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size)); - - png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE; - -#ifdef PNG_WRITE_FILTER_SUPPORTED - filters = png_ptr->do_filter; - - if (png_ptr->height == 1) - filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH); - - if (png_ptr->width == 1) - filters &= ~(PNG_FILTER_SUB|PNG_FILTER_AVG|PNG_FILTER_PAETH); - - if (filters == 0) - filters = PNG_FILTER_NONE; - - /* We only need to keep the previous row if we are using one of the following - * filters. + /* Check for a correctly initialized list, the requirement that the end + * pointer is NULL means that the end of the list can be easily detected. */ - if (filters & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH)) - png_ptr->prev_row = png_voidcast(png_bytep, png_calloc(png_ptr, - buf_size)); + affirm(ps != NULL && ps->s.end != NULL && *ps->s.end == NULL); + png_zlib_compress_validate(&png_ptr->zlib_state->s, 0/*in_use*/); - png_write_alloc_filter_row_buffers(png_ptr, filters); - - png_ptr->do_filter = (png_byte)filters; /* in case it was changed above */ -#else - png_ptr->do_filter = PNG_FILTER_NONE; -#endif /* PNG_WRITE_FILTER_SUPPORTED */ - -#ifdef PNG_WRITE_INTERLACING_SUPPORTED - /* If interlaced, we need to set up width and height of pass */ - if (png_ptr->interlaced) + IDAT_size = png_ptr->IDAT_size; + if (IDAT_size == 0U) { - if (!(png_ptr->transformations & PNG_INTERLACE)) + switch (pz_get(ps, IDAT, png_level, PNG_DEFAULT_COMPRESSION_LEVEL)) { - png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - - png_pass_ystart[0]) / png_pass_yinc[0]; + case PNG_COMPRESSION_COMPAT: /* Legacy */ + IDAT_size = 8192U; + break; - png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 - - png_pass_start[0]) / png_pass_inc[0]; + case PNG_COMPRESSION_LOW_MEMORY: + case PNG_COMPRESSION_HIGH_SPEED: + case PNG_COMPRESSION_LOW: + /* png_compress uses PNG_ROW_BUFFER_SIZE buffers for the compressed + * data. Optimize to allocate only one of these: + */ + IDAT_size = PNG_ROW_BUFFER_SIZE; + break; + + default: + case PNG_COMPRESSION_MEDIUM: + IDAT_size = PNG_ZBUF_SIZE; + break; + + case PNG_COMPRESSION_HIGH_READ_SPEED: + /* Assume the reader reads partial IDAT chunks (pretty much a + * requirement given that some PNG encoders produce just one IDAT) + */ + case PNG_COMPRESSION_HIGH: + /* This doesn't control the amount of memory allocated unless the + * PNG IDAT data really is this big. + * + * TODO: review handling out-of-memory from png_compress() by + * flushing an IDAT. + */ + IDAT_size = PNG_UINT_31_MAX; + break; + } + } + + /* Write IDAT chunks while either 'flush' is true or there are at + * least png_ptr->IDAT_size bytes available to be written. + */ + for (;;) + { + png_uint_32 len = IDAT_size; + + if (ps->s.overflow == 0U) + { + png_uint_32 avail = ps->s.len; + + if (avail < len) + { + /* When end_of_image is true everything gets written, otherwise + * there must be at least IDAT_size bytes available. + */ + if (!flush) + return; + + if (avail == 0U) + break; + + len = avail; + } } - else + png_write_chunk_header(png_ptr, png_IDAT, len); + + /* Write bytes from the buffer list, adjusting {overflow,len} as they are + * written. + */ + do { - png_ptr->num_rows = png_ptr->height; - png_ptr->usr_width = png_ptr->width; + png_compression_bufferp next = ps->s.list; + unsigned int avail = sizeof next->output; + unsigned int start = ps->s.start; + unsigned int written; + + affirm(next != NULL); + + if (next->next == NULL) /* end of list */ + { + /* The z_stream should always be pointing into this output buffer, + * the buffer may not be full: + */ + debug(ps->s.zs.next_out + ps->s.zs.avail_out == + next->output + sizeof next->output); + avail -= ps->s.zs.avail_out; + } + + else /* not end of list */ + debug((ps->s.zs.next_out < next->output || + ps->s.zs.next_out > next->output + sizeof next->output) && + (ps->s.overflow > 0 || + ps->s.start + ps->s.len >= sizeof next->output)); + + /* First, if this is the very first IDAT (PNG_HAVE_IDAT not set) + * fix the Zlib CINFO field if required: + */ + if ((png_ptr->mode & PNG_HAVE_IDAT) == 0U && + avail >= start+2U /* enough for the zlib header */) + { + debug(start == 0U); + fix_cinfo(ps, next->output+start, png_image_size(png_ptr)); + } + + else /* always expect to see at least 2 bytes: */ + debug((png_ptr->mode & PNG_HAVE_IDAT) != 0U); + + /* Set this now to prevent the above happening again second time round + * the loop: + */ + png_ptr->mode |= PNG_HAVE_IDAT; + + if (avail <= start+len) + { + /* Write all of this buffer: */ + affirm(avail > start); /* else overflow on the subtract */ + written = avail-start; + png_write_chunk_data(png_ptr, next->output+start, written); + + /* At the end there are no buffers in the list but the z_stream + * still points into the old (just released) buffer. This can + * happen when the old buffer is not full if the compressed bytes + * exactly match the IDAT length; it should always happen when + * end_of_image is set. + */ + ps->s.list = next->next; + + if (next->next == NULL) + { + debug(avail == start+len); + ps->s.end = &ps->s.list; + ps->s.zs.next_out = NULL; + ps->s.zs.avail_out = 0U; + } + + next->next = ps->stash; + ps->stash = next; + ps->s.start = 0U; + } + + else /* write only part of this buffer */ + { + written = len; + png_write_chunk_data(png_ptr, next->output+start, written); + ps->s.start = (unsigned int)/*SAFE*/(start + written); + } + + /* 'written' bytes were written: */ + len -= written; + + if (written <= ps->s.len) + ps->s.len -= written; + + else + { + affirm(ps->s.overflow > 0U); + --ps->s.overflow; + ps->s.len += 0x80000000U - written; + UNTESTED + } + } + while (len > 0U); + + png_write_chunk_end(png_ptr); + } + + /* avail == 0 && flush */ + png_end_IDAT(png_ptr); + png_ptr->mode |= PNG_AFTER_IDAT; +} + +/* This is is a convenience wrapper to handle IDAT compression; it takes a + * pointer to the input data and places no limit on the size of the output but + * is otherwise the same as png_compress(). It also handles the use of the + * stash (only used for IDAT compression.) + */ +static int +png_compress_IDAT_data(png_structrp png_ptr, png_zlib_statep ps, + png_zlib_compressp pz, png_const_voidp input, uInt input_len, int flush) +{ + /* Delay initialize the z_stream. */ + if (png_ptr->zowner != png_IDAT) + png_deflate_claim(png_ptr, png_IDAT, 0U); + + affirm(png_ptr->zowner == png_IDAT && pz->end != NULL && *pz->end == NULL); + + /* z_stream::{next,avail}_out are set by png_compress to point into the + * buffer list. next_in must be set here, avail_in comes from the input_len + * parameter: + */ + pz->zs.next_in = PNGZ_INPUT_CAST(png_voidcast(const Bytef*, input)); + *pz->end = ps->stash; /* May be NULL */ + ps->stash = NULL; + + /* zlib buffers the output, the maximum amount of compressed data that can be + * produced here is governed by the amount of buffering. + */ + { + int ret = png_compress(pz, input_len, 0U/*unlimited*/, flush); + + affirm(pz->end != NULL && ps->stash == NULL); + ps->stash = *pz->end; /* May be NULL */ + *pz->end = NULL; + + /* Z_FINISH should give Z_STREAM_END, everything else should give Z_OK, in + * either case all the input should have been consumed: + */ + implies(ret == Z_OK || ret == Z_FINISH, pz->zs.avail_in == 0U && + (ret == Z_STREAM_END) == (flush == Z_FINISH)); + pz->zs.next_in = NULL; + pz->zs.avail_in = 0U; /* safety */ + png_zlib_compress_validate(pz, 0/*in_use*/); + + return ret; + } +} + +/* Compress some image data using the main png_zlib_compress. Write the result + * out if there is sufficient data. + */ +static void +png_compress_IDAT(png_structrp png_ptr, png_const_voidp input, uInt input_len, + int flush) +{ + png_zlib_statep ps = png_ptr->zlib_state; + int ret = png_compress_IDAT_data(png_ptr, ps, &ps->s, input, input_len, + flush); + + /* Check the return code. */ + if (ret == Z_OK || ret == Z_STREAM_END) + png_write_IDAT(png_ptr, flush == Z_FINISH); + + else /* ret != Z_OK && ret != Z_STREAM_END */ + { + /* This is an error condition. It is fatal. */ + png_end_IDAT(png_ptr); + png_zstream_error(&ps->s.zs, ret); + png_error(png_ptr, ps->s.zs.msg); + } +} + +/* This is called at the end of every row to handle the required callbacks and + * advance png_struct::row_number and png_struct::pass. + */ +static void +png_write_end_row(png_structrp png_ptr, int flush) +{ + png_uint_32 row_number = png_ptr->row_number; + unsigned int pass = png_ptr->pass; + + debug(pass < 7U); + implies(flush == Z_FINISH, png_ptr->zowner == 0U); + + /* API NOTE: the write callback is made before any changes to the row number + * or pass however, in 1.7.0, the zlib stream can be closed before the + * callback is made (this is new). The application flush function happens + * afterward as was the case before. In 1.7.0 this is solely determined by + * the order of the code that follows. + */ + if (png_ptr->write_row_fn != NULL) + png_ptr->write_row_fn(png_ptr, row_number, pass); + +# ifdef PNG_WRITE_FLUSH_SUPPORTED + if (flush == Z_SYNC_FLUSH) + { + if (png_ptr->output_flush_fn != NULL) + png_ptr->output_flush_fn(png_ptr); + png_ptr->zlib_state->flush_rows = 0U; + } +# else /* !WRITE_FLUSH */ + PNG_UNUSED(flush) +# endif /* !WRITE_FLUSH */ + + /* Finally advance to the next row/pass: */ + if (png_ptr->interlaced == PNG_INTERLACE_NONE) + { + debug(row_number < png_ptr->height); + + if (++row_number == png_ptr->height) /* last row */ + { + row_number = 0U; + debug(flush == Z_FINISH); + png_ptr->pass = 7U; + } + } + +# ifdef PNG_WRITE_INTERLACING_SUPPORTED + else /* interlaced */ if (png_ptr->do_interlace) + { + /* This gets called only for rows that are processed; i.e. rows that + * are in the pass of a pass which is itself in the output. + */ + debug(row_number < png_ptr->height && + PNG_PASS_IN_IMAGE(png_ptr->width, png_ptr->height, pass) && + pass <= PNG_LAST_PASS(png_ptr->width, png_ptr->height) && + PNG_ROW_IN_INTERLACE_PASS(row_number, pass)); + + /* NOTE: the last row of the original image may not be in the pass, in + * this case the code which skipped the row must do the increment + * below! See 'interlace_row' in pngwrite.c and the code in + * png_write_png_rows below. + * + * In that case an earlier row will be the last one in the pass (if the + * pass is in the output), check this here: + */ + implies(pass == PNG_LAST_PASS(png_ptr->width, png_ptr->height) && + PNG_LAST_PASS_ROW(row_number, pass, png_ptr->height), + flush == Z_FINISH); + + if (++row_number == png_ptr->height) /* last row */ + { + row_number = 0U; + png_ptr->pass = 0x7U & ++pass; + } + } +# endif /* WRITE_INTERLACING */ + + else /* application does interlace */ + { + implies(png_ptr->height == 1U, pass != 6U); + debug(PNG_PASS_IN_IMAGE(png_ptr->width, png_ptr->height, pass) && + row_number < PNG_PASS_ROWS(png_ptr->height, pass)); + + if (++row_number == PNG_PASS_ROWS(png_ptr->height, pass)) + { + /* last row in this pass, next one may be empty. */ + row_number = 0U; + + do + ++pass; + while (pass < 7U && + !PNG_PASS_IN_IMAGE(png_ptr->width, png_ptr->height, pass)); + + implies(png_ptr->height == 1U, pass != 6U); + implies(pass == 7U, flush == Z_FINISH); + png_ptr->pass = 0x7U & pass; + } + } + + png_ptr->row_number = row_number; +} + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Flush the current output buffers now */ +void PNGAPI +png_write_flush(png_structrp png_ptr) +{ + png_debug(1, "in png_write_flush"); + + /* Force a flush at the end of the current row by setting 'flush_rows' to the + * maximum: + */ + if (png_ptr != NULL && png_ptr->zlib_state != NULL) + png_ptr->zlib_state->flush_rows = 0xEFFFFFFF; +} + +/* Return the correct flush to use */ +static int +row_flush(png_zlib_statep ps, unsigned int row_info_flags) +{ + if (PNG_IDAT_END(row_info_flags)) + return Z_FINISH; + + else if ((row_info_flags & png_row_end) != 0 && + ++ps->flush_rows >= ps->flush_dist) + return Z_SYNC_FLUSH; + + else + return Z_NO_FLUSH; +} +#else /* !WRITE_FLUSH */ +# define row_flush(ps, ri) (PNG_IDAT_END(ri) ? Z_FINISH : Z_NO_FLUSH) +#endif /* !WRITE_FLUSH */ + +static void +write_filtered_row(png_structrp png_ptr, png_const_voidp filtered_row, + unsigned int row_bytes, unsigned int filter /*if at start of row*/, + int flush) +{ + /* This handles writing a row that has been filtered, or did not need to be + * filtered. If the data row has a partial pixel it must have been handled + * correctly in the caller; filters generate a full 8 bits even if the pixel + * only has one significant bit! + */ + debug(row_bytes > 0); + affirm(row_bytes <= ZLIB_IO_MAX); /* I.e. it fits in a uInt */ + + if (filter < PNG_FILTER_VALUE_LAST) /* start of row */ + { + png_byte buffer[1]; + + buffer[0] = PNG_BYTE(filter); + png_compress_IDAT(png_ptr, buffer, 1U/*len*/, Z_NO_FLUSH); + } + + png_compress_IDAT(png_ptr, filtered_row, row_bytes, flush); +} + +static void +write_unfiltered_rowbits(png_structrp png_ptr, png_const_bytep filtered_row, + unsigned int row_bits, png_byte filter /*if at start of row*/, + int flush) +{ + /* Same as above, but it correctly clears the unused bits in a partial + * byte. + */ + const png_uint_32 row_bytes = row_bits >> 3; + + debug(filter == PNG_FILTER_VALUE_NONE || filter == PNG_FILTER_VALUE_LAST); + + if (row_bytes > 0U) + { + row_bits -= row_bytes << 3; + write_filtered_row(png_ptr, filtered_row, row_bytes, filter, + row_bits == 0U ? flush : Z_NO_FLUSH); + filter = PNG_FILTER_VALUE_LAST; /* written */ + } + + /* Handle a partial byte. */ + if (row_bits > 0U) + { + png_byte buffer[1]; + + buffer[0] = PNG_BYTE(filtered_row[row_bytes] & ~(0xFFU >> row_bits)); + write_filtered_row(png_ptr, buffer, 1U, filter, flush); + } +} + +#ifdef PNG_WRITE_FILTER_SUPPORTED +static void +filter_block_singlebyte(unsigned int row_bytes, png_bytep sub_row, + png_bytep up_row, png_bytep avg_row, png_bytep paeth_row, + png_const_bytep row, png_const_bytep prev_row, png_bytep prev_pixels) +{ + /* Calculate rows for all four filters where the input has one byte per pixel + * (more accurately per filter-unit). + */ + png_byte a = prev_pixels[0]; + png_byte c = prev_pixels[1]; + + while (row_bytes-- > 0U) + { + const png_byte x = *row++; + const png_byte b = prev_row == NULL ? 0U : *prev_row++; + + /* Calculate each filtered byte in turn: */ + if (sub_row != NULL) *sub_row++ = 0xFFU & (x - a); + if (up_row != NULL) *up_row++ = 0xFFU & (x - b); + if (avg_row != NULL) *avg_row++ = 0xFFU & (x - (a+b)/2U); + + /* Paeth is a little more difficult: */ + if (paeth_row != NULL) + { + int pa = b-c; /* a+b-c - a */ + int pb = a-c; /* a+b-c - b */ + int pc = pa+pb; /* a+b-c - c = b-c + a-c */ + png_byte p = a; + + pa = abs(pa); + pb = abs(pb); + if (pa > pb) pa = pb, p = b; + if (pa > abs(pc)) p = c; + + *paeth_row++ = 0xFFU & (x - p); + } + + /* And set a and c for the next pixel: */ + a = x; + c = b; + } + + /* Store a and c for the next block: */ + prev_pixels[0] = a; + prev_pixels[1] = c; +} + +static void +filter_block_multibyte(unsigned int row_bytes, + const unsigned int bpp, png_bytep sub_row, png_bytep up_row, + png_bytep avg_row, png_bytep paeth_row, png_const_bytep row, + png_const_bytep prev_row, png_bytep prev_pixels) +{ + /* Calculate rows for all four filters, the input is a block of bytes such + * that row_bytes is a multiple of bpp. bpp can be 2, 3, 4, 6 or 8. + * prev_pixels will be updated to the last pixels processed. + */ + while (row_bytes >= bpp) + { + unsigned int i; + + for (i=0; i pb) pa = pb, p = b; + if (pa > abs(pc)) p = c; + + *paeth_row++ = 0xFFU & (x - p); + } + } + + row_bytes -= i; + } +} + +static void +filter_block(png_const_bytep prev_row, png_bytep prev_pixels, + png_const_bytep unfiltered_row, unsigned int row_bits, + const unsigned int bpp, png_bytep sub_row, png_bytep up_row, + png_bytep avg_row, png_bytep paeth_row) +{ + const unsigned int row_bytes = row_bits >> 3; /* complete bytes */ + + if (bpp <= 8U) + { + /* There may be a partial byte at the end. */ + if (row_bytes > 0) + filter_block_singlebyte(row_bytes, sub_row, up_row, avg_row, paeth_row, + unfiltered_row, prev_row, prev_pixels); + + /* The partial byte must be handled correctly here; both the previous row + * value and the current value need to have non-present bits cleared. + */ + if ((row_bits & 7U) != 0) + { + const png_byte mask = PNG_BYTE(~(0xFFU >> (row_bits & 7U))); + png_byte buffer[2]; + + buffer[0] = unfiltered_row[row_bytes] & mask; + + if (prev_row != NULL) + buffer[1U] = prev_row[row_bytes] & mask; + + else + buffer[1U] = 0U; + + filter_block_singlebyte(1U, + sub_row == NULL ? NULL : sub_row+row_bytes, + up_row == NULL ? NULL : up_row+row_bytes, + avg_row == NULL ? NULL : avg_row+row_bytes, + paeth_row == NULL ? NULL : paeth_row+row_bytes, + buffer, buffer+1U, prev_pixels); } } else -#endif + filter_block_multibyte(row_bytes, bpp >> 3, + sub_row, up_row, avg_row, paeth_row, + unfiltered_row, prev_row, prev_pixels); +} + +static void +filter_row(png_structrp png_ptr, png_const_bytep prev_row, + png_bytep prev_pixels, png_const_bytep unfiltered_row, + unsigned int row_bits, unsigned int bpp, unsigned int filter, + int start_of_row, int flush) +{ + /* filters_to_try identifies a single filter and it is not PNG_FILTER_NONE. + */ + png_byte filtered_row[PNG_ROW_BUFFER_SIZE]; + + affirm((row_bits+7U) >> 3 <= PNG_ROW_BUFFER_SIZE && + filter >= PNG_FILTER_VALUE_SUB && filter <= PNG_FILTER_VALUE_PAETH); + debug((row_bits % bpp) == 0U); + + filter_block(prev_row, prev_pixels, unfiltered_row, row_bits, bpp, + filter == PNG_FILTER_VALUE_SUB ? filtered_row : NULL, + filter == PNG_FILTER_VALUE_UP ? filtered_row : NULL, + filter == PNG_FILTER_VALUE_AVG ? filtered_row : NULL, + filter == PNG_FILTER_VALUE_PAETH ? filtered_row : NULL); + + write_filtered_row(png_ptr, filtered_row, (row_bits+7U)>>3, + start_of_row ? filter : PNG_FILTER_VALUE_LAST, flush); +} + +/* Allow the application to select one or more row filters to use. */ +static png_int_32 +set_filter(png_zlib_statep ps, unsigned int filtersIn) +{ + /* Notice that PNG_NO_FILTERS is 0 and passes this test; this is OK because + * filters then gets set to PNG_FILTER_NONE, as is required. + * + * The argument to this routine is actually an (int), but conversion to + * (unsigned int) is safe because it leaves the top bits set which results in + * PNG_EDOM below. + */ + if (filtersIn < PNG_FILTER_NONE) + filtersIn = PNG_FILTER_MASK(filtersIn); + + /* PNG_ALL_FILTERS is a constant, unfortunately it is nominally signed, for + * historical reasons, hence the PNG_BIC_MASK here. + */ + if ((filtersIn & PNG_BIC_MASK(PNG_ALL_FILTERS)) == 0U) { - png_ptr->num_rows = png_ptr->height; - png_ptr->usr_width = png_ptr->width; +# ifndef PNG_SELECT_FILTER_SUPPORTED + filtersIn &= -filtersIn; /* Use lowest set bit */ +# endif /* !SELECT_FILTER */ + + return ps->filter_mask = filtersIn & PNG_ALL_FILTERS; + } + + else /* Out-of-range filtersIn: */ + return PNG_EDOM; +} +#endif /* WRITE_FILTER */ + +#ifdef PNG_WRITE_FILTER_SUPPORTED +void /* PRIVATE */ +png_write_start_IDAT(png_structrp png_ptr) +{ + png_zlib_statep ps = get_zlib_state(png_ptr); + + /* Set up the IDAT compression state. Expect the state to have been released + * by the previous owner, but it doesn't much matter if there was an error. + * Note that the stream is not claimed yet. + */ + debug(png_ptr->zowner == 0U); + + /* This sets the buffer limits and write_row_size, which is used below. */ + png_zlib_state_set_buffer_limits(png_ptr, ps); + + if (ps->filter_mask == 0) + { +# ifdef PNG_SELECT_FILTER_SUPPORTED + /* Now default the filter mask if it hasn't been set already: */ + int png_level = + pz_get(ps, IDAT, png_level, PNG_DEFAULT_COMPRESSION_LEVEL); + + /* If the bit depth is less than 8, so pixels are not byte aligned, PNG + * filtering hardly ever helps because there is no correlation between + * the bytes on which the filter works and the actual pixel values. + * Note that GIF is a whole lot better at this because it uses LZW to + * compress a bit-stream, not a byte stream as in the deflate + * implementation of LZ77. + * + * If the row size is less than 256 bytes filter selection algorithms + * are flakey because the restricted range of codes in each row can + * lead to poor selection of filters, particularly if the bytes in the + * image are themselves limited. (This happens when a low bit-depth + * image is encoded with 8-bit channels.) + * + * By experiment with the test set of images the breakpoint between + * not filtering and filtering based on which gives best compression by + * row size is as follows: + * + * NONE FAST ALL + * PAL <=anything [even 8-bit palette images larger if filtered] + * G<8 <=anything [low bit depth gray images] + * G8 <=16 [+~1%] >16 + * G16 <=128 [+~1%] >128 + * GA8 <=64 [+~1%] >64 + * GA16 <=anything [always better without filtering!] + * RGB8 <=32 [+0-2%(1)] >32 + * RGB16 <=1024 [+~1%] >1024 + * RGBA8 <=64 [+~~1%] >64 + * RGBA16 <=128 {+~0.5%] >128 + * + * (1) The largest 24-bit RGB image (RGB8) faired better, by 1.3%, + * with 'fast' filters. This is assumed to be random. + * + * Aggregated across all color types and bit depths the breakpoint for + * filtering is >16 bytes, but the size increase only exceeds 0.5% for + * images with rows between 64 and 128 bytes, hence the choices below. + * + * Across all the test images that change (not including selecting just + * the 'fast' filters by default) does not change the compressed size + * significantly (+0.06% across the whole test set), however it does + * substantially increase the number of images without filtering. + * + * Using just none and sub filters results in overall compressed sizes + * somewhere around the geometric mean of no filtering and 'fast'. + * + * The image size also plays a part. Filtering is not an advantage for + * images of size <= 512 bytes. This is also reflected below. + * + * NOTE: the libpng 1.6 (and earlier) algorithm seems to work + * because it biases the byte codes in the output towards 0 and 255. + * Zlib doesn't care what the codes are, but Huffman encoding always + * benefits from a biased distribution and the filters themselves were + * designed to produce values in this range. + * + * In a raw comparison with the legacy code selection of specific sets + * of filters always increased the compressed size of the test set, as + * follows: + * + * PNG_ALL_FILTERS: +0.26% + * PNG_FAST_FILTERS: +1.9% + * NONE+SUB: +5.8% + * PNG_NO_FILTERS: +14% + * + * This mainly proves that a static selection of filters (without + * considering the PNG format) is always worse than the legacy + * algorithm below. + * + * NOTE: ps->filter_mask must be set to a mask value, not a simple + * PNG_FILTER_VALUE_ number. + */ + if (ps->write_row_size == 0U /* row cannot be buffered */) + ps->filter_mask = PNG_FILTER_NONE; + + else if (png_level == PNG_COMPRESSION_COMPAT/* Legacy */) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE || + png_ptr->bit_depth < 8U) + ps->filter_mask = PNG_FILTER_NONE; + + else + ps->filter_mask = PNG_ALL_FILTERS; + } + + /* NOTE: overall with the following size tests (row and image size) the + * test set of images end up 0.06% larger, however some color types are + * smaller and some larger; the differences are minute. If the test is + * <=128 (which means <=129 bytes per row with the filter byte) the + * resultant inclusion of 32x32 RGBA images results in significantly + * increased compressed size. + * + * The test on png_level captures the following settings: + * + * PNG_COMPRESSION_LOW_MEMORY + * PNG_COMPRESSION_HIGH_SPEED + * PNG_COMPRESSION_HIGH_READ_SPEED + * + * NOTE: this relies on the exact values in png.h! + */ + else if (png_level <= PNG_COMPRESSION_HIGH_READ_SPEED + || png_ptr->color_type == PNG_COLOR_TYPE_PALETTE + || png_ptr->bit_depth < 8U + || ps->write_row_size/*does not include filter*/ < 128U + || png_image_size(png_ptr) <= 512U) + ps->filter_mask = PNG_FILTER_NONE; + + /* ELSE: there are at least 128 bytes in every row and the pixels + * are multiples of a byte. + */ + else switch (png_level) + { + default: /* For GCC */ + case PNG_COMPRESSION_LOW: + ps->filter_mask = PNG_FILTER_NONE+PNG_FILTER_SUB; + break; + + case PNG_COMPRESSION_MEDIUM: + ps->filter_mask = PNG_FAST_FILTERS; + break; + + case PNG_COMPRESSION_HIGH: + ps->filter_mask = PNG_ALL_FILTERS; + break; + } +# else /* !SELECT_FILTER */ + ps->filter_mask = PNG_FILTER_NONE; +# endif /* !SELECT_FILTER */ } } -/* Internal use only. Called when finished processing a row of data. */ -void /* PRIVATE */ -png_write_finish_row(png_structrp png_ptr) +static png_byte +png_write_start_row(png_zlib_statep ps, int start_of_pass, int no_previous_row) + /* Called at the start of a row to set up anything required for filter + * handling in the row. Sets png_zlib_state::filters to a single filter. + */ { -#ifdef PNG_WRITE_INTERLACING_SUPPORTED - /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + unsigned int mask = ps->filter_mask; - /* Start of interlace block */ - static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + /* If we see a previous-row filter in mask and png_zlib_state::save_row is + * still unset set it. This means that the first time a previous-row filter + * is seen row-saving gets turned on. + */ + if (ps->save_row == SAVE_ROW_UNSET && (mask & PREVIOUS_ROW_FILTERS) != 0U) + ps->save_row = SAVE_ROW_DEFAULT; - /* Offset to next interlace block */ - static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + if ((no_previous_row /* row not stored */ && !start_of_pass) || + ps->save_row == SAVE_ROW_OFF /* disabled by app */ || + ps->write_row_size == 0U /* row too large to buffer */) + mask &= PNG_BIC_MASK(PREVIOUS_ROW_FILTERS); - /* Start of interlace block in the y direction */ - static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + /* On the first row of a pass Paeth is equivalent to sub and up is equivalent + * to none, so try to simplify the mask in in this case. + */ + else if (start_of_pass) { +# define MATCH(flags) ((mask & (flags)) == (flags)) + if (MATCH(PNG_FILTER_NONE|PNG_FILTER_UP)) + mask &= PNG_BIC_MASK(PNG_FILTER_UP); - /* Offset to next interlace block in the y direction */ - static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; -#endif + if (MATCH(PNG_FILTER_SUB|PNG_FILTER_PAETH)) + mask &= PNG_BIC_MASK(PNG_FILTER_PAETH); +# undef MATCH + } - png_debug(1, "in png_write_finish_row"); - - /* Next row */ - png_ptr->row_number++; - - /* See if we are done */ - if (png_ptr->row_number < png_ptr->num_rows) - return; - -#ifdef PNG_WRITE_INTERLACING_SUPPORTED - /* If interlaced, go to next pass */ - if (png_ptr->interlaced) +# ifdef PNG_SELECT_FILTER_SUPPORTED + if ((mask & (mask-1U)) == 0U /* single bit set */ || + ps->write_row_size == 0U /* row cannot be buffered */) +# endif /* SELECT_FILTER */ + /* Convert the lowest set bit into the corresponding value. If no bits + * are set select NONE. After this switch statement the value of + * ps->filters is guaranteed to just be a single filter. + */ + switch (mask & -mask) { - png_ptr->row_number = 0; - if (png_ptr->transformations & PNG_INTERLACE) + default: mask = PNG_FILTER_VALUE_NONE; break; + case PNG_FILTER_SUB: mask = PNG_FILTER_VALUE_SUB; break; + case PNG_FILTER_UP: mask = PNG_FILTER_VALUE_UP; break; + case PNG_FILTER_AVG: mask = PNG_FILTER_VALUE_AVG; break; + case PNG_FILTER_PAETH: mask = PNG_FILTER_VALUE_PAETH; break; + } + + return ps->filters = PNG_BYTE(mask); +} + +static png_bytep +allocate_row(png_structrp png_ptr, png_const_bytep data, png_alloc_size_t size) + /* Utility to allocate and save some row bytes. If the result is NULL the + * allocation failed and the png_zlib_struct will have been updated to + * prevent further allocation attempts. + */ +{ + const png_zlib_statep ps = png_ptr->zlib_state; + png_bytep buffer; + + debug(ps->write_row_size > 0U); + + /* OOM is handled silently, as is the case where the row is too large to + * buffer. + */ + buffer = png_voidcast(png_bytep, + png_malloc_base(png_ptr, ps->write_row_size)); + + /* Setting write_row_size to 0 switches on the code for handling a row that + * is too large to buffer. This will kick in next time round, i.e. on the + * next row. + */ + if (buffer == NULL) + ps->write_row_size = 0U; + + else + memcpy(buffer, data, size); + + return buffer; +} +#endif /* WRITE_FILTER */ + +#ifdef PNG_SELECT_FILTER_SUPPORTED +/* Bit set operations. Not in ANSI C-90 but commonly available in highly + * optimized versions, hence the ifndef. These operations just work on bitsets + * of size 256. The second argument (the code index) may be evaluated multiple + * times. + */ +#ifndef PNG_CODE_SET /* Can be set in pngpriv.h */ +typedef png_uint_32 png_codeset[8]; +# define PNG_CODE_MASK(i) (((png_uint_32)1U) << ((i) & 0x1FU)) +# define PNG_CODE_IS_SET(c,i) (((c)[(i) >> 5] & PNG_CODE_MASK(i))) +# define PNG_CODE_SET(c,i) (((c)[(i) >> 5] |= PNG_CODE_MASK(i))) +# define PNG_CODE_CLEAR(c,i) (((c)[(i) >> 5] &= ~PNG_CODE_MASK(i))) +#endif /* !PNG_CODE_SET */ + +typedef struct filter_selector +{ + /* Persistent filter selection information (stored across row boundaries). + * A code is not considered if it last occured more than 'window' bytes ago. + * The deflate algorithm means that 'window' cannot exceed 8453377, however + * practical versions may be far less. When 'distance' reaches 'window' any + * code where: + * + * distance - code_distance[code] > window + * + * at the end of a row 'code' is removed from codeset. Otherwise + * (rearranging the above): + * + * distance - window <= code_distance[code] + * + * and so the distances of the still active codes can be reduced: + * + * code_distance[code] -= distance-window + * distance = window + * + * This prevents any wrap of 'distance' on a row which is shorter than + * 2^32-window. + * + * However when then row is 2^32-window or more bytes long (the row can be up + * to just under 2^34 bytes long) this algorithm doesn't work; 'distance' + * will overflow in the middle of the row and all codes are relevant. This + * is handled below simply by reseting the set of present codes at the start + * of the row and ignoring the overflow. + */ + unsigned int code_count; /* Number of distinct codes seen */ + int png_level; /* Cached compression level */ + png_uint_32 filter_select_max_width; + /* The maximum number of pixels which can be fitted in the window without + * filling the entire window (i.e. the maximum number that can be fitted + * in (window-1) bytes). + */ + png_uint_32 sum_bias[PNG_FILTER_VALUE_LAST]; + /* For each filter a measure of its cost in the filter sum calculation. + * This allows filter selection based on the sum-of-absolute-dfferences + * method to be biased to favour particular filters. There was no such + * bias before 1.7 and the filter byte was ignored. + */ + png_uint_32 distance; /* Distance from beginning */ + png_codeset codeset; /* Set of seen codes */ + png_uint_32 code_distance[256]; /* Distance at last occurence */ +} filter_selector; + +static const filter_selector * +png_start_filter_select(png_zlib_statep ps, unsigned int bpp) +{ +# define png_ptr ps_png_ptr(ps) + filter_selector *fs = ps->selector; + + if (fs == NULL) + { + fs = png_voidcast(filter_selector*, png_malloc_base(png_ptr, sizeof *fs)); + + if (fs != NULL) { - png_ptr->pass++; + png_uint_32 window = ps->filter_select_window; + fs->png_level = pz_get(ps, IDAT, png_level, + PNG_DEFAULT_COMPRESSION_LEVEL); + + /* Delay initialize this here: */ + if (window < 3U || window > PNG_FILTER_SELECT_WINDOW_MAX) + ps->filter_select_window = window = PNG_FILTER_SELECT_WINDOW_MAX; + + fs->code_count = 0; + + switch (fs->png_level) + { + default: + /* TODO: investigate other settings */ + { + unsigned int f; + + for (f=0; fsum_bias[f] = f; + } + ps->filter_select_threshold = 64U; /* 6bit RGB */ + ps->filter_select_threshold2 = 50U; /* TODO: experiment! */ + break; + + case PNG_COMPRESSION_COMPAT: /* Legacy */ + memset(fs->sum_bias, 0U, sizeof fs->sum_bias); + ps->filter_select_threshold = 1U; /* disabled */ + ps->filter_select_threshold2 = 1U; + break; + } + + /* This is the maximum row width, in pixels, of a row which fits and + * leaves 1 byte free in the window. For any bigger row filter + * selection ignores the previous rows. + */ + fs->filter_select_max_width = ((window-2U/*filter+last byte*/)*8U)/bpp; + fs->distance = 0U; + memset(fs->codeset, 0U, sizeof fs->codeset); + /* fs->code_distance is left uninitialized because fs->codeset says + * whether or not each entry has been initialized. + */ + ps->selector = fs; } else - { - /* Loop until we find a non-zero width or height pass */ - do - { - png_ptr->pass++; - - if (png_ptr->pass >= 7) - break; - - png_ptr->usr_width = (png_ptr->width + - png_pass_inc[png_ptr->pass] - 1 - - png_pass_start[png_ptr->pass]) / - png_pass_inc[png_ptr->pass]; - - png_ptr->num_rows = (png_ptr->height + - png_pass_yinc[png_ptr->pass] - 1 - - png_pass_ystart[png_ptr->pass]) / - png_pass_yinc[png_ptr->pass]; - - if (png_ptr->transformations & PNG_INTERLACE) - break; - - } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0); - - } - - /* Reset the row above the image for the next pass */ - if (png_ptr->pass < 7) - { - if (png_ptr->prev_row != NULL) - memset(png_ptr->prev_row, 0, - (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels* - png_ptr->usr_bit_depth, png_ptr->width)) + 1); - - return; - } + ps->write_row_size = 0U; /* OOM */ } -#endif +# undef png_ptr - /* If we get here, we've just written the last row, so we need - to flush the compressor */ - png_compress_IDAT(png_ptr, NULL, 0, Z_FINISH); + return fs; } -#ifdef PNG_WRITE_INTERLACING_SUPPORTED -/* Pick out the correct pixels for the interlace pass. - * The basic idea here is to go through the row with a source - * pointer and a destination pointer (sp and dp), and copy the - * correct pixels for the pass. As the row gets compacted, - * sp will always be >= dp, so we should never overwrite anything. - * See the default: case for the easiest code to understand. +typedef struct +{ + /* Per-filter data. This remains separate from the above until the filter + * selection has been made. It reflects the above however the codeset only + * records codes present in this row. + * + * The 'sum' fields are the sum of the absolute deviation of each code from + * 0, the algorithm from 1.6 and earlier. In other words: + * + * if (code >= 128) + * sum += code; + * else + * sum += 256-code; + */ + unsigned int code_count; /* Number of distinct codes seen in row */ + unsigned int new_code_count; /* Number of new codes seen in row */ + png_uint_32 sum_low; /* Low 31 bits of code sum */ + png_uint_32 sum_high; /* High 32 bits of code sum */ + png_codeset codeset; /* Set of codes seen in this row */ + png_uint_32 code_distance[256]; /* Distance at last occurence in this row */ +} filter_data; + +static void +filter_data_init(filter_data *fd, png_uint_32 distance, unsigned int filter, + unsigned int code_is_set, png_uint_32 bias) +{ + fd->code_count = 1U; + fd->new_code_count = !code_is_set; + fd->sum_low = bias; + fd->sum_high = 0U; + memset(&fd->codeset, 0U, sizeof fd->codeset); + PNG_CODE_SET(fd->codeset, filter); + fd->code_distance[filter] = distance; +} + +static void +add_code(const filter_selector *fs, filter_data *fd, png_uint_32 distance, + unsigned int code) +{ + if (!PNG_CODE_IS_SET(fd->codeset, code)) + { + PNG_CODE_SET(fd->codeset, code); + ++(fd->code_count); + fd->code_distance[code] = distance; + if (!PNG_CODE_IS_SET(fs->codeset, code)) + ++(fd->new_code_count); + } + + { + png_uint_32 low = fd->sum_low; + + if (code <= 128U) + low += code; + + else + low += 256U-code; + + /* Handle overflow into the top bit: */ + if (low & 0x80000000U) + fd->sum_low = low & 0x7FFFFFFFU, ++fd->sum_high; + + else + fd->sum_low = low; + } +} + +static png_byte +filter_data_select(png_zlib_statep ps, filter_data fd[PNG_FILTER_VALUE_LAST], + unsigned int filter, png_uint_32 distance, png_uint_32 w) +{ +# define png_ptr ps_png_ptr(ps) + /* Choose how to do this depending on the row and window size. */ + filter_selector *fs = ps->selector; + png_uint_32 window = ps->filter_select_window; + + affirm(fs != NULL); + + /* Check the width against the maximum number of pixels that can fit in a + * window without filling it: + */ + if (w > fs->filter_select_max_width) + { + /* The cache is not used */ + fs->distance = 0U; /* for next row */ + fs->code_count = 0U; + memset(fs->codeset, 0U, sizeof fs->codeset); + } + + else + { + /* Merge the two code sets, discounting codes that last occurred before + * the start of the window. + */ + png_uint_32 adjust, code_count; + unsigned int code; + + /* filter_selector::distance is the distance of the first byte in the row + * (the filter byte), but 'distance' can wrap on long rows. The above + * test is meant to exclude the wrap case by excluding any case where the + * row has as many bytes as the window, so: + */ + affirm(distance > fs->distance && distance - fs->distance < window); + + /* Set 'adjust' to the current distance of the start of the window. I.e: + * + * +---------------+--------+ + * | before window | window | future data + * +---------------+--------+ + * A A + * | | + * adjust + + distance + * + * If the window isn't full yet 'adjust' will be zero, otherwise all the + * distances will be reduced by 'adjust' so that the first byte of the + * window has distance 0. + */ + if (distance > window) + adjust = distance-window; + + else + adjust = 0; + + + /* This may be decreased below if some old codes only occured before the + * start of the window. + */ + code_count = fs->code_count + fd->new_code_count; + + for (code=0U; code<256U; ++code) + { + if (PNG_CODE_IS_SET(fd[filter].codeset, code)) + { + PNG_CODE_SET(fs->codeset, code); + debug(fd[filter].code_distance[code] >= adjust); + fs->code_distance[code] = fd[filter].code_distance[code] - adjust; + } + + else if (PNG_CODE_IS_SET(fs->codeset, code) && adjust > 0) + { + /* The code did not occur in this row, the old distance may now be + * outside the window (because adjust is non-zero). + */ + const png_uint_32 d = fs->code_distance[code]; + + if (d >= adjust) + fs->code_distance[code] = d-adjust; + + else + PNG_CODE_CLEAR(fs->codeset, code), --code_count; + } + } + + fs->code_count = code_count; + fs->distance = distance - adjust; /* I.e. either distance or window! */ + } + + return ps->filters = PNG_BYTE(filter); +# undef png_ptr +} + +static png_byte +select_filter(png_zlib_statep ps, png_const_bytep row, + png_const_bytep prev, unsigned int bpp, png_uint_32 width, int start_of_pass) + /* Select a filter from the list provided by png_write_start_row. */ +{ + png_byte filters = png_write_start_row(ps, start_of_pass, prev == NULL); + +# define png_ptr ps_png_ptr(ps) + if (filters >= PNG_FILTER_NONE) /* multiple filters to test */ + { + const png_uint_32 max_pixels = ps->row_buffer_max_pixels; + const png_uint_32 block_pixels = ps->row_buffer_max_aligned_pixels; + const filter_selector *fs = ps->selector; + png_uint_32 pixels_to_go = width; + png_uint_32 distance; + unsigned int bits_at_end = 0U; + png_byte prev_pixels[4*2*2]; /* 2 pixels up to 4x2-bytes each */ + filter_data fd[PNG_FILTER_VALUE_LAST]; + + debug((filters & (filters-1)) != 0U); /* Expect more than one bit! */ + + if (fs == NULL) + { + /* Delay initialize with a quiet OOM handler */ + fs = png_start_filter_select(ps, bpp); + if (fs == NULL) + { + ps->filters = PNG_FILTER_VALUE_NONE; + return PNG_FILTER_VALUE_NONE; + } + } + + /* If PNG_FILTER_NONE is in the list check it first. */ + if (filters & PNG_FILTER_NONE) + { + png_const_bytep rp = row; + png_uint_32 w = width; + + distance = fs->distance; + filter_data_init(fd+PNG_FILTER_VALUE_NONE, distance++, + PNG_FILTER_VALUE_NONE, + PNG_CODE_IS_SET(fs->codeset, PNG_FILTER_VALUE_NONE), + fs->sum_bias[PNG_FILTER_VALUE_NONE]); + + if (bpp >= 8) /* complete bytes */ + { + const unsigned int bytes = bpp/8U; + + while (w > 0) + { + unsigned int b; + for (b=0; b= ppb) + { + add_code(fs, fd+PNG_FILTER_VALUE_NONE, distance++, *rp++); + w -= ppb; + } + + if (w > 0) /* partial byte at end */ + add_code(fs, fd+PNG_FILTER_VALUE_NONE, distance++, + *rp & (0xFFU >> (w*bpp) /* zero unused bits */)); + } + + /* For PNG data with a small number of codes it is worth skipping the + * filtering because it almost always increases the code count + * significantly. This is controlled by + * png_zlib_state::filter_select_threshold and causes an early return + * here. + */ + if (fd[PNG_FILTER_VALUE_NONE].new_code_count + + fs->code_count < ps->filter_select_threshold) + return filter_data_select(ps, fd, PNG_FILTER_VALUE_NONE, distance, + width); + } /* PNG_FILTER_NONE */ + + memset(prev_pixels, 0U, sizeof prev_pixels); + distance = fs->distance; + + { + unsigned int i; + + for (i=PNG_FILTER_VALUE_NONE+1U; icodeset, i), fs->sum_bias[i]); + } + + ++distance; + + while (pixels_to_go || bits_at_end) + { + unsigned int bits, i; + union + { + PNG_ROW_BUFFER_ALIGN_TYPE force_buffer_alignment; + png_byte row[4][PNG_ROW_BUFFER_SIZE]; + } filtered; + union + { + PNG_ROW_BUFFER_ALIGN_TYPE force_buffer_alignment; + png_byte byte; + } last; + + if (pixels_to_go) + { + if (pixels_to_go > max_pixels) + { + /* Maintain alignment by consuming on block_pixels at once */ + bits = block_pixels * bpp; + pixels_to_go -= block_pixels; /* May be 0 */ + } + + else + { + bits = pixels_to_go * bpp; + bits_at_end = bits & 0x7U; + bits -= bits_at_end; + pixels_to_go = 0U; /* +bits_at_end */ + } + } + + else /* incomplete byte at the end of the pixel */ + { + /* Make sure the unused bits are cleared (to zero, although this is + * an arbitrary choice): + */ + last.byte = PNG_BYTE(*row & ~(0xFFU >> bits_at_end)); + row = &last.byte; + bits = bits_at_end; + bits_at_end = 0U; + } + + filter_block(prev, prev_pixels, row, bits, bpp, + filtered.row[0/*sub*/], filtered.row[1/*up*/], + filtered.row[2/*avg*/], filtered.row[3/*Paeth*/]); + + /* A block of (bits+7)/8 bytes is now available to process. */ + for (i=0; 8U*i < bits; ++i, ++distance) + { + unsigned int f; + + for (f=PNG_FILTER_VALUE_NONE+1U; f> 3; + + row += bits >> 3; + } + + /* Now look at the candidate filters, including 'none' and select the + * best. We know that 'none' increases the code count beyond the + * threshold, so if the old code count is below the threshold and there is + * a filter which does not increase the code count select it; doing so + * should do no harm to the overall compression. + */ + if (fs->code_count < ps->filter_select_threshold) + { + unsigned int f, min_new_count = 257U, min_f = PNG_FILTER_VALUE_NONE; + + for (f=PNG_FILTER_VALUE_NONE+1U; fcode_count + min_new_count < ps->filter_select_threshold2) + return filter_data_select(ps, fd, min_f, distance, width); + } + + /* Now fall back to the libpng 1.6 and earlier algorithm. This favours + * the filter which produces least deviation in the codes from 0. When + * this works it does so by reducing the distribution of code values. The + * filters implicitly encode the difference between a predictor based on + * adjacent values, the assumption is that this will result in values + * close to 0. + */ + { + png_uint_32 high = -1; + png_uint_32 low = -1; + unsigned int min_f = 0 /*unset, but safe*/; + unsigned int f; + + for (f=PNG_FILTER_VALUE_NONE; ffilters = filters; +# undef png_ptr +} +#else /* !SELECT_FILTER */ + /* Filter selection not being done, just call png_write_start_row: */ +# define select_filter(ps, rp, pp, bpp, width, start_of_pass)\ + png_write_start_row((ps), (start_of_pass), (pp) == NULL) +#endif /* !SELECT_FILTER */ + +/* This is the common function to write multiple rows of PNG data. The data is + * in the relevant PNG format but has had no filtering done. */ void /* PRIVATE */ -png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) +png_write_png_rows(png_structrp png_ptr, png_const_bytep *rows, + png_uint_32 num_rows) { - /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + const png_zlib_statep ps = png_ptr->zlib_state; + const unsigned int bpp = png_ptr->row_output_pixel_depth; +# ifdef PNG_WRITE_FILTER_SUPPORTED + png_const_bytep previous_row = ps->previous_write_row; +# else /* !WRITE_FILTER */ + /* These are constant in the no-filer case: */ + const png_byte filter = PNG_FILTER_VALUE_NONE; + const png_uint_32 max_pixels = ps->zlib_max_pixels; + const png_uint_32 block_pixels = ps->zlib_max_aligned_pixels; +# endif /* !WRITE_FILTER */ + /* Write the given rows handling the png_compress_IDAT argument limitations + * (uInt) and any valid row width. + */ + png_uint_32 last_row_in_pass = 0U; /* Actual last, not last+1! */ + png_uint_32 pixels_in_pass = 0U; + unsigned int first_row_in_pass = 0U; /* For do_interlace */ + unsigned int pixels_at_end = 0U; /* for a partial byte at the end */ + unsigned int base_info_flags = png_row_end; + int pass = -1; /* Invalid: force calculation first time round */ - /* Start of interlace block */ - static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + debug(png_ptr->row_output_pixel_depth == PNG_PIXEL_DEPTH(*png_ptr)); - /* Offset to next interlace block */ - static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; - - png_debug(1, "in png_do_write_interlace"); - - /* We don't have to do anything on the last pass (6) */ - if (pass < 6) + while (num_rows-- > 0U) { - /* Each pixel depth is handled separately */ - switch (row_info->pixel_depth) + if (png_ptr->pass != pass) { - case 1: + /* Recalcuate the row bytes and partial bits */ + pass = png_ptr->pass; + pixels_in_pass = png_ptr->width; + + if (png_ptr->interlaced == PNG_INTERLACE_NONE) { - png_bytep sp; - png_bytep dp; - int shift; - int d; - int value; - png_uint_32 i; - png_uint_32 row_width = row_info->width; + debug(pass == 0); + last_row_in_pass = png_ptr->height - 1U; + base_info_flags |= png_pass_last; /* there is only one */ + } - dp = row; - d = 0; - shift = 7; + else + { + const png_uint_32 height = png_ptr->height; - for (i = png_pass_start[pass]; i < row_width; - i += png_pass_inc[pass]) - { - sp = row + (png_size_t)(i >> 3); - value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01; - d |= (value << shift); + last_row_in_pass = PNG_PASS_ROWS(height, pass); + debug(pass >= 0 && pass < 7); - if (shift == 0) +# ifdef PNG_WRITE_INTERLACING_SUPPORTED + if (png_ptr->do_interlace) { - shift = 7; - *dp++ = (png_byte)d; - d = 0; + /* libpng is doing the interlace handling, the row number is + * actually the row in the image. + * + * This overflows when the PNG height is such that the are no + * rows in this pass. This does not matter; because there are + * no rows the value doesn't get used. + */ + last_row_in_pass = + PNG_ROW_FROM_PASS_ROW(last_row_in_pass-1U, pass); + first_row_in_pass = PNG_PASS_START_ROW(pass); } - else - shift--; - - } - if (shift != 7) - *dp = (png_byte)d; - - break; - } - - case 2: - { - png_bytep sp; - png_bytep dp; - int shift; - int d; - int value; - png_uint_32 i; - png_uint_32 row_width = row_info->width; - - dp = row; - shift = 6; - d = 0; - - for (i = png_pass_start[pass]; i < row_width; - i += png_pass_inc[pass]) + else /* Application handles the interlace */ +# endif /* WRITE_INTERLACING */ { - sp = row + (png_size_t)(i >> 2); - value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03; - d |= (value << shift); - - if (shift == 0) - { - shift = 6; - *dp++ = (png_byte)d; - d = 0; - } - - else - shift -= 2; + /* The row does exist, so this works without checking the column + * count. + */ + debug(last_row_in_pass > 0U); + last_row_in_pass -= 1U; } - if (shift != 6) - *dp = (png_byte)d; - break; + if (pass == PNG_LAST_PASS(pixels_in_pass/*PNG width*/, height)) + base_info_flags |= png_pass_last; + + /* Finally, adjust pixels_in_pass for the interlacing (skip the + * final pass; it is full width). + */ + if (pass < 6) + pixels_in_pass = PNG_PASS_COLS(pixels_in_pass, pass); } - case 4: - { - png_bytep sp; - png_bytep dp; - int shift; - int d; - int value; - png_uint_32 i; - png_uint_32 row_width = row_info->width; + /* Mask out the bits in a partial byte. */ + pixels_at_end = pixels_in_pass & PNG_ADDOF(bpp); - dp = row; - shift = 4; - d = 0; - for (i = png_pass_start[pass]; i < row_width; - i += png_pass_inc[pass]) - { - sp = row + (png_size_t)(i >> 1); - value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f; - d |= (value << shift); - - if (shift == 0) - { - shift = 4; - *dp++ = (png_byte)d; - d = 0; - } - - else - shift -= 4; - } - if (shift != 4) - *dp = (png_byte)d; - - break; - } - - default: - { - png_bytep sp; - png_bytep dp; - png_uint_32 i; - png_uint_32 row_width = row_info->width; - png_size_t pixel_bytes; - - /* Start at the beginning */ - dp = row; - - /* Find out how many bytes each pixel takes up */ - pixel_bytes = (row_info->pixel_depth >> 3); - - /* Loop through the row, only looking at the pixels that matter */ - for (i = png_pass_start[pass]; i < row_width; - i += png_pass_inc[pass]) - { - /* Find out where the original pixel is */ - sp = row + (png_size_t)i * pixel_bytes; - - /* Move the pixel */ - if (dp != sp) - memcpy(dp, sp, pixel_bytes); - - /* Next pixel */ - dp += pixel_bytes; - } - break; - } +# ifdef PNG_WRITE_FILTER_SUPPORTED + /* Reset the previous_row pointer correctly; NULL at the start of + * the pass. If row_number is not 0 then a previous write_rows was + * interrupted in mid-pass and any required buffer should be in + * previous_write_row (set in the initializer). + */ + if (png_ptr->row_number == first_row_in_pass) + previous_row = NULL; +# endif /* WRITE_FILTER */ } - /* Set new row width */ - row_info->width = (row_info->width + - png_pass_inc[pass] - 1 - - png_pass_start[pass]) / - png_pass_inc[pass]; - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, - row_info->width); - } +# ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* When libpng is handling the interlace we see rows that must be + * skipped. + */ + if (!png_ptr->do_interlace || + PNG_ROW_IN_INTERLACE_PASS(png_ptr->row_number, pass)) +# endif /* WRITE_INTERLACING */ + { + const unsigned int row_info_flags = base_info_flags | + (png_ptr->row_number == + first_row_in_pass ? png_pass_first_row : 0) | + (png_ptr->row_number == last_row_in_pass ? png_pass_last_row : 0); + const int flush = row_flush(ps, row_info_flags); + png_const_bytep row = *rows; + png_uint_32 pixels_to_go = pixels_in_pass; +# ifdef PNG_WRITE_FILTER_SUPPORTED + /* The filter can change each time round. Call png_write_start_row + * to resolve any changes. Note that when this function is used to + * do filter selection from png_write_png_data on the first row + * png_write_start_row will get called twice. + */ + const png_byte filter = select_filter(ps, row, previous_row, bpp, + pixels_in_pass, png_ptr->row_number == first_row_in_pass); + const png_uint_32 max_pixels = filter == PNG_FILTER_VALUE_NONE ? + ps->zlib_max_pixels : ps->row_buffer_max_pixels; + const png_uint_32 block_pixels = filter == PNG_FILTER_VALUE_NONE ? + ps->zlib_max_aligned_pixels : ps->row_buffer_max_aligned_pixels; + + /* The row handling uses png_compress_IDAT directly if there is no + * filter to be applied, otherwise it uses filter_row. + */ + if (filter != PNG_FILTER_VALUE_NONE) + { + int start_of_row = 1; + png_byte prev_pixels[4*2*2]; /* 2 pixels up to 4x2-bytes each */ + + memset(prev_pixels, 0U, sizeof prev_pixels); + + while (pixels_to_go > max_pixels) + { + /* Write a block at once to maintain alignment */ + filter_row(png_ptr, previous_row, prev_pixels, row, + bpp * block_pixels, bpp, filter, start_of_row, + Z_NO_FLUSH); + + if (previous_row != NULL) + previous_row += (block_pixels * bpp) >> 3; + + row += (block_pixels * bpp) >> 3; + pixels_to_go -= block_pixels; + start_of_row = 0; + } + + /* The filter code handles the partial byte at the end correctly, + * so this is all that is required: + */ + if (pixels_to_go > 0) + filter_row(png_ptr, previous_row, prev_pixels, row, + bpp * pixels_to_go, bpp, filter, start_of_row, flush); + } + + else +# endif /* WRITE_FILTER */ + + { + /* The no-filter case. */ + const uInt block_bytes = (uInt)/*SAFE*/( + bpp <= 8U ? + block_pixels >> PNG_SHIFTOF(bpp) : + block_pixels * (bpp >> 3)); + + /* png_write_start_IDAT guarantees this, but double check for + * overflow above in debug: + */ + debug((block_bytes & (PNG_ROW_BUFFER_BYTE_ALIGN-1U)) == 0U); + + /* The filter has to be written here: */ + png_compress_IDAT(png_ptr, &filter, 1U/*len*/, Z_NO_FLUSH); + + /* Process blocks of pixels up to the limit. */ + while (pixels_to_go > max_pixels) + { + png_compress_IDAT(png_ptr, row, block_bytes, Z_NO_FLUSH); + row += block_bytes; + pixels_to_go -= block_pixels; + } + + /* Now compress the remainder; pixels_to_go <= max_pixels so it will + * fit in a uInt. + */ + { + const png_uint_32 remainder = + bpp <= 8U + ? (pixels_to_go-pixels_at_end) >> PNG_SHIFTOF(bpp) + : (pixels_to_go-pixels_at_end) * (bpp >> 3); + + if (remainder > 0U) + png_compress_IDAT(png_ptr, row, remainder, + pixels_at_end > 0U ? Z_NO_FLUSH : flush); + + else + debug(pixels_at_end > 0U); + + if (pixels_at_end > 0U) + { + /* There is a final partial byte. This is PNG format so the + * left-most bits are the most significant. + */ + const png_byte last = PNG_BYTE(row[remainder] & + ~(0xFFU >> (pixels_at_end * bpp))); + + png_compress_IDAT(png_ptr, &last, 1U, flush); + } + } + } + + png_write_end_row(png_ptr, flush); + +# ifdef PNG_WRITE_FILTER_SUPPORTED + previous_row = *rows; +# endif /* WRITE_FILTER */ +# undef HANDLE + } /* row in pass */ + +# ifdef PNG_WRITE_INTERLACING_SUPPORTED + else /* row not in pass; just skip it */ + { + if (++png_ptr->row_number >= png_ptr->height) + { + debug(png_ptr->row_number == png_ptr->height); + + png_ptr->row_number = 0U; + png_ptr->pass = 0x7U & (pass+1U); + } + } +# endif /* WRITE_INTERLACING */ + + ++rows; + } /* while num_rows */ + +# ifdef PNG_WRITE_FILTER_SUPPORTED + /* previous_row must be copied back unless we don't need it because the + * next row is the first one in the pass (this relies on png_write_end_row + * setting row_number to 0 at the end!) + */ + if (png_ptr->row_number != 0U && previous_row != NULL && SAVE_ROW(ps) && + ps->previous_write_row != previous_row/*all rows skipped*/) + { +# ifdef PNG_SELECT_FILTER_SUPPORTED + /* We might be able to avoid any copy. */ + if (ps->current_write_row == previous_row) + { + png_bytep old = ps->previous_write_row; + ps->previous_write_row = ps->current_write_row; + ps->current_write_row = old; /* may be NULL */ + } + + else +# endif /* SELECT_FILTER */ + + if (ps->previous_write_row != NULL) + memcpy(ps->previous_write_row, previous_row, + png_calc_rowbytes(png_ptr, bpp, pixels_in_pass)); + + else + ps->previous_write_row = allocate_row(png_ptr, previous_row, + png_calc_rowbytes(png_ptr, bpp, pixels_in_pass)); + } +# endif /* WRITE_FILTER */ } -#endif +#ifdef PNG_WRITE_FILTER_SUPPORTED /* This filters the row, chooses which filter to use, if it has not already * been specified by the application, and then writes the row out with the * chosen filter. */ -static void png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row, - png_size_t row_bytes); - -#define PNG_MAXSUM (((png_uint_32)(-1)) >> 1) -#define PNG_HISHIFT 10 -#define PNG_LOMASK ((png_uint_32)0xffffL) -#define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT)) -void /* PRIVATE */ -png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) -{ - png_bytep best_row; -#ifdef PNG_WRITE_FILTER_SUPPORTED - png_bytep prev_row, row_buf; - png_uint_32 mins, bpp; - png_byte filter_to_do = png_ptr->do_filter; - png_size_t row_bytes = row_info->rowbytes; -#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED - int num_p_filters = png_ptr->num_prev_filters; -#endif - - png_debug(1, "in png_write_find_filter"); - -#ifndef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED - if (png_ptr->row_number == 0 && filter_to_do == PNG_ALL_FILTERS) - { - /* These will never be selected so we need not test them. */ - filter_to_do &= ~(PNG_FILTER_UP | PNG_FILTER_PAETH); - } -#endif - - /* Find out how many bytes offset each pixel is */ - bpp = (row_info->pixel_depth + 7) >> 3; - - prev_row = png_ptr->prev_row; -#endif - best_row = png_ptr->row_buf; -#ifdef PNG_WRITE_FILTER_SUPPORTED - row_buf = best_row; - mins = PNG_MAXSUM; - - /* The prediction method we use is to find which method provides the - * smallest value when summing the absolute values of the distances - * from zero, using anything >= 128 as negative numbers. This is known - * as the "minimum sum of absolute differences" heuristic. Other - * heuristics are the "weighted minimum sum of absolute differences" - * (experimental and can in theory improve compression), and the "zlib - * predictive" method (not implemented yet), which does test compressions - * of lines using different filter methods, and then chooses the - * (series of) filter(s) that give minimum compressed data size (VERY - * computationally expensive). - * - * GRR 980525: consider also - * - * (1) minimum sum of absolute differences from running average (i.e., - * keep running sum of non-absolute differences & count of bytes) - * [track dispersion, too? restart average if dispersion too large?] - * - * (1b) minimum sum of absolute differences from sliding average, probably - * with window size <= deflate window (usually 32K) - * - * (2) minimum sum of squared differences from zero or running average - * (i.e., ~ root-mean-square approach) - */ - - - /* We don't need to test the 'no filter' case if this is the only filter - * that has been chosen, as it doesn't actually do anything to the data. - */ - if ((filter_to_do & PNG_FILTER_NONE) && filter_to_do != PNG_FILTER_NONE) - { - png_bytep rp; - png_uint_32 sum = 0; - png_size_t i; - int v; - - for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++) - { - v = *rp; - sum += (v < 128) ? v : 256 - v; - } - -#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED - if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) - { - png_uint_32 sumhi, sumlo; - int j; - sumlo = sum & PNG_LOMASK; - sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; /* Gives us some footroom */ - - /* Reduce the sum if we match any of the previous rows */ - for (j = 0; j < num_p_filters; j++) - { - if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) - { - sumlo = (sumlo * png_ptr->filter_weights[j]) >> - PNG_WEIGHT_SHIFT; - - sumhi = (sumhi * png_ptr->filter_weights[j]) >> - PNG_WEIGHT_SHIFT; - } - } - - /* Factor in the cost of this filter (this is here for completeness, - * but it makes no sense to have a "cost" for the NONE filter, as - * it has the minimum possible computational cost - none). - */ - sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> - PNG_COST_SHIFT; - - sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> - PNG_COST_SHIFT; - - if (sumhi > PNG_HIMASK) - sum = PNG_MAXSUM; - - else - sum = (sumhi << PNG_HISHIFT) + sumlo; - } -#endif - mins = sum; - } - - /* Sub filter */ - if (filter_to_do == PNG_FILTER_SUB) - /* It's the only filter so no testing is needed */ - { - png_bytep rp, lp, dp; - png_size_t i; - - for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; - i++, rp++, dp++) - { - *dp = *rp; - } - - for (lp = row_buf + 1; i < row_bytes; - i++, rp++, lp++, dp++) - { - *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); - } - - best_row = png_ptr->sub_row; - } - - else if (filter_to_do & PNG_FILTER_SUB) - { - png_bytep rp, dp, lp; - png_uint_32 sum = 0, lmins = mins; - png_size_t i; - int v; - -#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED - /* We temporarily increase the "minimum sum" by the factor we - * would reduce the sum of this filter, so that we can do the - * early exit comparison without scaling the sum each time. - */ - if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) - { - int j; - png_uint_32 lmhi, lmlo; - lmlo = lmins & PNG_LOMASK; - lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; - - for (j = 0; j < num_p_filters; j++) - { - if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) - { - lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> - PNG_WEIGHT_SHIFT; - - lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> - PNG_WEIGHT_SHIFT; - } - } - - lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> - PNG_COST_SHIFT; - - lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> - PNG_COST_SHIFT; - - if (lmhi > PNG_HIMASK) - lmins = PNG_MAXSUM; - - else - lmins = (lmhi << PNG_HISHIFT) + lmlo; - } -#endif - - for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; - i++, rp++, dp++) - { - v = *dp = *rp; - - sum += (v < 128) ? v : 256 - v; - } - - for (lp = row_buf + 1; i < row_bytes; - i++, rp++, lp++, dp++) - { - v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); - - sum += (v < 128) ? v : 256 - v; - - if (sum > lmins) /* We are already worse, don't continue. */ - break; - } - -#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED - if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) - { - int j; - png_uint_32 sumhi, sumlo; - sumlo = sum & PNG_LOMASK; - sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; - - for (j = 0; j < num_p_filters; j++) - { - if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) - { - sumlo = (sumlo * png_ptr->inv_filter_weights[j]) >> - PNG_WEIGHT_SHIFT; - - sumhi = (sumhi * png_ptr->inv_filter_weights[j]) >> - PNG_WEIGHT_SHIFT; - } - } - - sumlo = (sumlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> - PNG_COST_SHIFT; - - sumhi = (sumhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> - PNG_COST_SHIFT; - - if (sumhi > PNG_HIMASK) - sum = PNG_MAXSUM; - - else - sum = (sumhi << PNG_HISHIFT) + sumlo; - } -#endif - - if (sum < mins) - { - mins = sum; - best_row = png_ptr->sub_row; - } - } - - /* Up filter */ - if (filter_to_do == PNG_FILTER_UP) - { - png_bytep rp, dp, pp; - png_size_t i; - - for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, - pp = prev_row + 1; i < row_bytes; - i++, rp++, pp++, dp++) - { - *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff); - } - - best_row = png_ptr->up_row; - } - - else if (filter_to_do & PNG_FILTER_UP) - { - png_bytep rp, dp, pp; - png_uint_32 sum = 0, lmins = mins; - png_size_t i; - int v; - - -#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED - if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) - { - int j; - png_uint_32 lmhi, lmlo; - lmlo = lmins & PNG_LOMASK; - lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; - - for (j = 0; j < num_p_filters; j++) - { - if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) - { - lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> - PNG_WEIGHT_SHIFT; - - lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> - PNG_WEIGHT_SHIFT; - } - } - - lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> - PNG_COST_SHIFT; - - lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> - PNG_COST_SHIFT; - - if (lmhi > PNG_HIMASK) - lmins = PNG_MAXSUM; - - else - lmins = (lmhi << PNG_HISHIFT) + lmlo; - } -#endif - - for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, - pp = prev_row + 1; i < row_bytes; i++) - { - v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); - - sum += (v < 128) ? v : 256 - v; - - if (sum > lmins) /* We are already worse, don't continue. */ - break; - } - -#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED - if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) - { - int j; - png_uint_32 sumhi, sumlo; - sumlo = sum & PNG_LOMASK; - sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; - - for (j = 0; j < num_p_filters; j++) - { - if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) - { - sumlo = (sumlo * png_ptr->filter_weights[j]) >> - PNG_WEIGHT_SHIFT; - - sumhi = (sumhi * png_ptr->filter_weights[j]) >> - PNG_WEIGHT_SHIFT; - } - } - - sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> - PNG_COST_SHIFT; - - sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> - PNG_COST_SHIFT; - - if (sumhi > PNG_HIMASK) - sum = PNG_MAXSUM; - - else - sum = (sumhi << PNG_HISHIFT) + sumlo; - } -#endif - - if (sum < mins) - { - mins = sum; - best_row = png_ptr->up_row; - } - } - - /* Avg filter */ - if (filter_to_do == PNG_FILTER_AVG) - { - png_bytep rp, dp, pp, lp; - png_uint_32 i; - - for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, - pp = prev_row + 1; i < bpp; i++) - { - *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); - } - - for (lp = row_buf + 1; i < row_bytes; i++) - { - *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) - & 0xff); - } - best_row = png_ptr->avg_row; - } - - else if (filter_to_do & PNG_FILTER_AVG) - { - png_bytep rp, dp, pp, lp; - png_uint_32 sum = 0, lmins = mins; - png_size_t i; - int v; - -#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED - if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) - { - int j; - png_uint_32 lmhi, lmlo; - lmlo = lmins & PNG_LOMASK; - lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; - - for (j = 0; j < num_p_filters; j++) - { - if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_AVG) - { - lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> - PNG_WEIGHT_SHIFT; - - lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> - PNG_WEIGHT_SHIFT; - } - } - - lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> - PNG_COST_SHIFT; - - lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> - PNG_COST_SHIFT; - - if (lmhi > PNG_HIMASK) - lmins = PNG_MAXSUM; - - else - lmins = (lmhi << PNG_HISHIFT) + lmlo; - } -#endif - - for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, - pp = prev_row + 1; i < bpp; i++) - { - v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); - - sum += (v < 128) ? v : 256 - v; - } - - for (lp = row_buf + 1; i < row_bytes; i++) - { - v = *dp++ = - (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) & 0xff); - - sum += (v < 128) ? v : 256 - v; - - if (sum > lmins) /* We are already worse, don't continue. */ - break; - } - -#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED - if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) - { - int j; - png_uint_32 sumhi, sumlo; - sumlo = sum & PNG_LOMASK; - sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; - - for (j = 0; j < num_p_filters; j++) - { - if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) - { - sumlo = (sumlo * png_ptr->filter_weights[j]) >> - PNG_WEIGHT_SHIFT; - - sumhi = (sumhi * png_ptr->filter_weights[j]) >> - PNG_WEIGHT_SHIFT; - } - } - - sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> - PNG_COST_SHIFT; - - sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> - PNG_COST_SHIFT; - - if (sumhi > PNG_HIMASK) - sum = PNG_MAXSUM; - - else - sum = (sumhi << PNG_HISHIFT) + sumlo; - } -#endif - - if (sum < mins) - { - mins = sum; - best_row = png_ptr->avg_row; - } - } - - /* Paeth filter */ - if (filter_to_do == PNG_FILTER_PAETH) - { - png_bytep rp, dp, pp, cp, lp; - png_size_t i; - - for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, - pp = prev_row + 1; i < bpp; i++) - { - *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); - } - - for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) - { - int a, b, c, pa, pb, pc, p; - - b = *pp++; - c = *cp++; - a = *lp++; - - p = b - c; - pc = a - c; - -#ifdef PNG_USE_ABS - pa = abs(p); - pb = abs(pc); - pc = abs(p + pc); -#else - pa = p < 0 ? -p : p; - pb = pc < 0 ? -pc : pc; - pc = (p + pc) < 0 ? -(p + pc) : p + pc; -#endif - - p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; - - *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); - } - best_row = png_ptr->paeth_row; - } - - else if (filter_to_do & PNG_FILTER_PAETH) - { - png_bytep rp, dp, pp, cp, lp; - png_uint_32 sum = 0, lmins = mins; - png_size_t i; - int v; - -#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED - if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) - { - int j; - png_uint_32 lmhi, lmlo; - lmlo = lmins & PNG_LOMASK; - lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; - - for (j = 0; j < num_p_filters; j++) - { - if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) - { - lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> - PNG_WEIGHT_SHIFT; - - lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> - PNG_WEIGHT_SHIFT; - } - } - - lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> - PNG_COST_SHIFT; - - lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> - PNG_COST_SHIFT; - - if (lmhi > PNG_HIMASK) - lmins = PNG_MAXSUM; - - else - lmins = (lmhi << PNG_HISHIFT) + lmlo; - } -#endif - - for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, - pp = prev_row + 1; i < bpp; i++) - { - v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); - - sum += (v < 128) ? v : 256 - v; - } - - for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) - { - int a, b, c, pa, pb, pc, p; - - b = *pp++; - c = *cp++; - a = *lp++; - -#ifndef PNG_SLOW_PAETH - p = b - c; - pc = a - c; -#ifdef PNG_USE_ABS - pa = abs(p); - pb = abs(pc); - pc = abs(p + pc); -#else - pa = p < 0 ? -p : p; - pb = pc < 0 ? -pc : pc; - pc = (p + pc) < 0 ? -(p + pc) : p + pc; -#endif - p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; -#else /* PNG_SLOW_PAETH */ - p = a + b - c; - pa = abs(p - a); - pb = abs(p - b); - pc = abs(p - c); - - if (pa <= pb && pa <= pc) - p = a; - - else if (pb <= pc) - p = b; - - else - p = c; -#endif /* PNG_SLOW_PAETH */ - - v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); - - sum += (v < 128) ? v : 256 - v; - - if (sum > lmins) /* We are already worse, don't continue. */ - break; - } - -#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED - if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) - { - int j; - png_uint_32 sumhi, sumlo; - sumlo = sum & PNG_LOMASK; - sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; - - for (j = 0; j < num_p_filters; j++) - { - if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) - { - sumlo = (sumlo * png_ptr->filter_weights[j]) >> - PNG_WEIGHT_SHIFT; - - sumhi = (sumhi * png_ptr->filter_weights[j]) >> - PNG_WEIGHT_SHIFT; - } - } - - sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> - PNG_COST_SHIFT; - - sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> - PNG_COST_SHIFT; - - if (sumhi > PNG_HIMASK) - sum = PNG_MAXSUM; - - else - sum = (sumhi << PNG_HISHIFT) + sumlo; - } -#endif - - if (sum < mins) - { - best_row = png_ptr->paeth_row; - } - } -#endif /* PNG_WRITE_FILTER_SUPPORTED */ - - /* Do the actual writing of the filtered row data from the chosen filter. */ - png_write_filtered_row(png_ptr, best_row, row_info->rowbytes+1); - -#ifdef PNG_WRITE_FILTER_SUPPORTED -#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED - /* Save the type of filter we picked this time for future calculations */ - if (png_ptr->num_prev_filters > 0) - { - int j; - - for (j = 1; j < num_p_filters; j++) - { - png_ptr->prev_filters[j] = png_ptr->prev_filters[j - 1]; - } - - png_ptr->prev_filters[j] = best_row[0]; - } -#endif -#endif /* PNG_WRITE_FILTER_SUPPORTED */ -} - - -/* Do the actual writing of a previously filtered row. */ static void -png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row, - png_size_t full_row_length/*includes filter byte*/) +write_png_data(png_structrp png_ptr, png_const_bytep prev_row, + png_bytep prev_pixels, png_const_bytep unfiltered_row, png_uint_32 x, + unsigned int row_bits, unsigned int row_info_flags) + /* This filters the row appropriately and returns an updated prev_row + * (updated for 'x'). + */ { - png_debug(1, "in png_write_filtered_row"); + const png_zlib_statep ps = png_ptr->zlib_state; + const unsigned int bpp = png_ptr->row_output_pixel_depth; + const int flush = row_flush(ps, row_info_flags); + const png_byte filter = ps->filters; /* just one */ - png_debug1(2, "filter = %d", filtered_row[0]); + /* These invariants are expected from the caller: */ + affirm(row_bits <= 8U*PNG_ROW_BUFFER_SIZE); + debug(filter < PNG_FILTER_VALUE_LAST/*sic: last+1*/); - png_compress_IDAT(png_ptr, filtered_row, full_row_length, Z_NO_FLUSH); + /* Now choose the correct filter implementation according to the number of + * filters in the filters_to_try list. The prev_row parameter is made + * NULL on the first row because it is uninitialized at that point. + */ + if (filter == PNG_FILTER_VALUE_NONE) + write_unfiltered_rowbits(png_ptr, unfiltered_row, row_bits, + x == 0 ? PNG_FILTER_VALUE_NONE : PNG_FILTER_VALUE_LAST, flush); -#ifdef PNG_WRITE_FILTER_SUPPORTED - /* Swap the current and previous rows */ - if (png_ptr->prev_row != NULL) - { - png_bytep tptr; + else + filter_row(png_ptr, + (row_info_flags & png_pass_first_row) ? NULL : prev_row, + prev_pixels, unfiltered_row, row_bits, bpp, filter, x == 0, flush); - tptr = png_ptr->prev_row; - png_ptr->prev_row = png_ptr->row_buf; - png_ptr->row_buf = tptr; - } -#endif /* PNG_WRITE_FILTER_SUPPORTED */ - - /* Finish row - updates counters and flushes zlib if last row */ - png_write_finish_row(png_ptr); - -#ifdef PNG_WRITE_FLUSH_SUPPORTED - png_ptr->flush_rows++; - - if (png_ptr->flush_dist > 0 && - png_ptr->flush_rows >= png_ptr->flush_dist) - { - png_write_flush(png_ptr); - } -#endif + /* Handle end of row: */ + if ((row_info_flags & png_row_end) != 0) + png_write_end_row(png_ptr, flush); } -#endif /* PNG_WRITE_SUPPORTED */ + +void /* PRIVATE */ +png_write_png_data(png_structrp png_ptr, png_bytep prev_pixels, + png_const_bytep unfiltered_row, png_uint_32 x, + unsigned int width/*pixels*/, unsigned int row_info_flags) +{ + const png_zlib_statep ps = png_ptr->zlib_state; + + affirm(ps != NULL); + + { + const unsigned int bpp = png_ptr->row_output_pixel_depth; + const unsigned int row_bits = width * bpp; + png_bytep prev_row = ps->previous_write_row; + + debug(bpp <= 64U && width <= 65535U && + width < 65535U/bpp); /* Expensive: only matters on 16-bit */ + + /* This is called once before starting a new row here, but below it is + * only called once between starting a new list of rows. + */ + if (x == 0) + png_write_start_row(ps, (row_info_flags & png_pass_first_row) != 0, + prev_row == NULL); + + /* If filter selection is required the filter will have at least one mask + * bit set. + */ +# ifdef PNG_SELECT_FILTER_SUPPORTED + if (ps->filters >= PNG_FILTER_NONE/*lowest mask bit*/) + { + /* If the entire row is passed in the input process it via + * immediately, otherwise the row must be buffered for later + * analysis. + */ + png_const_bytep row; + + if (x > 0 || (row_info_flags & png_row_end) == 0) + { + /* The row must be saved for later. */ + png_bytep buffer = ps->current_write_row; + + /* png_write_start row should always check this: */ + debug(ps->write_row_size > 0U); + + if (buffer != NULL) + memcpy(buffer + png_calc_rowbytes(png_ptr, bpp, x), + unfiltered_row, (row_bits + 7U) >> 3); + + + else if (x == 0U) + ps->current_write_row = buffer = allocate_row(png_ptr, + unfiltered_row, (row_bits + 7U) >> 3); + + row = buffer; + } + + else + row = unfiltered_row; + + if (row != NULL) /* else out of memory */ + { + /* At row end, process the save buffer. */ + if ((row_info_flags & png_row_end) != 0) + png_write_png_rows(png_ptr, &row, 1U); + + /* Early return to skip the single-filter code */ + return; + } + + /* Caching the row failed, so process the row using the lowest set + * filter. The allocation error should only ever happen at the + * start of the row. If this goes wrong the output will have been + * damaged. + */ + affirm(x == 0U); + } +# endif /* SELECT_FILTER */ + + /* prev_row is either NULL or the position in the previous row buffer */ + if (prev_row != NULL && x > 0) + prev_row += png_calc_rowbytes(png_ptr, bpp, x); + + /* This is the single filter case (no selection): */ + write_png_data(png_ptr, prev_row, prev_pixels, unfiltered_row, x, + row_bits, row_info_flags); + + /* Copy the current row into the previous row buffer, if available, unless + * this is the last row in the pass, when there is no point. Note that + * write_previous_row may have garbage in a partial byte at the end as a + * result of this memcpy. + */ + if (!(row_info_flags & png_pass_last_row) && SAVE_ROW(ps)) { + if (prev_row != NULL) + memcpy(prev_row, unfiltered_row, (row_bits + 7U) >> 3); + + /* NOTE: if the application sets png_zlib_state::save_row in a callback + * it isn't possible to do the save until the next row. allocate_row + * handles OOM silently by turning off the save. + */ + else if (x == 0) /* can allocate the save buffer */ + ps->previous_write_row = + allocate_row(png_ptr, unfiltered_row, (row_bits + 7U) >> 3); + } + } +} +#else /* !WRITE_FILTER */ +void /* PRIVATE */ +png_write_start_IDAT(png_structrp png_ptr) +{ + png_zlib_statep ps = get_zlib_state(png_ptr); + + /* Set up the IDAT compression state. Expect the state to have been released + * by the previous owner, but it doesn't much matter if there was an error. + * Note that the stream is not claimed yet. + */ + debug(png_ptr->zowner == 0U); + + /* This sets the buffer limits and write_row_size, which is used below. */ + png_zlib_state_set_buffer_limits(png_ptr, ps); +} + +void /* PRIVATE */ +png_write_png_data(png_structrp png_ptr, png_bytep prev_pixels, + png_const_bytep unfiltered_row, png_uint_32 x, + unsigned int width/*pixels*/, unsigned int row_info_flags) +{ + const unsigned int bpp = png_ptr->row_output_pixel_depth; + int flush; + png_uint_32 row_bits; + + row_bits = width; + row_bits *= bpp; + /* These invariants are expected from the caller: */ + affirm(width < 65536U && bpp <= 64U && width < 65536U/bpp && + row_bits <= 8U*PNG_ROW_BUFFER_SIZE); + + affirm(png_ptr->zlib_state != NULL); + flush = row_flush(png_ptr->zlib_state, row_info_flags); + + write_unfiltered_rowbits(png_ptr, unfiltered_row, row_bits, + x == 0 ? PNG_FILTER_VALUE_NONE : PNG_FILTER_VALUE_LAST, flush); + + PNG_UNUSED(prev_pixels) + + /* Handle end of row: */ + if ((row_info_flags & png_row_end) != 0) + png_write_end_row(png_ptr, flush); +} +#endif /* !WRITE_FILTER */ + +png_int_32 /* PRIVATE */ +png_write_setting(png_structrp png_ptr, png_uint_32 setting, + png_uint_32 parameter, png_int_32 value) +{ + /* Caller checks the arguments for basic validity */ + int only_get = (setting & PNG_SF_GET) != 0U; + + setting &= ~PNG_SF_GET; + + switch (setting) + { + /* Settings in png_struct: */ + case PNG_SW_IDAT_size: + if (parameter > 0 && parameter <= PNG_UINT_31_MAX) + { + if (!only_get) + png_ptr->IDAT_size = parameter; + + return 0; /* set ok */ + } + + else + return PNG_EINVAL; + + /* Settings in zlib_state: */ + case PNG_SW_COMPRESS_png_level: + return compression_setting(png_ptr, parameter, png_level, value, + only_get); + +# ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED + case PNG_SW_COMPRESS_zlib_level: + return compression_setting(png_ptr, parameter, level, value, + only_get); + + case PNG_SW_COMPRESS_windowBits: + return compression_setting(png_ptr, parameter, windowBits, value, + only_get); + + case PNG_SW_COMPRESS_memLevel: + return compression_setting(png_ptr, parameter, memLevel, value, + only_get); + + case PNG_SW_COMPRESS_strategy: + return compression_setting(png_ptr, parameter, strategy, value, + only_get); + + case PNG_SW_COMPRESS_method: + if (value != 8) /* Only supported method */ + return PNG_EINVAL; + return 8; /* old method */ +# endif /* WRITE_CUSTOMIZE_COMPRESSION */ + +# ifdef PNG_WRITE_FILTER_SUPPORTED + case PNG_SW_COMPRESS_filters: + /* The method must match that in the IHDR: */ + if (parameter == png_ptr->filter_method) + { + if (!only_get) + return set_filter(get_zlib_state(png_ptr), value); + + else if (png_ptr->zlib_state != NULL && + png_ptr->zlib_state->filter_mask != 0U/*unset*/) + return png_ptr->zlib_state->filter_mask; + + else + return PNG_UNSET; + } + + else /* Invalid filter method */ + return PNG_EINVAL; + + case PNG_SW_COMPRESS_row_buffers: + /* New in 1.7.0: direct control of the buffering. */ + switch (parameter) + { + case 0: + if (!only_get) + get_zlib_state(png_ptr)->save_row = SAVE_ROW_OFF; + return 0; + + case 1: + if (!only_get) + get_zlib_state(png_ptr)->save_row = SAVE_ROW_ON; + return 1; + + default: + return PNG_ENOSYS; /* no support for bigger values */ + } +# endif /* WRITE_FILTER */ + +# ifdef PNG_WRITE_FLUSH_SUPPORTED + case PNG_SW_FLUSH: + /* Set the automatic flush interval or 0 to turn flushing off */ + if (!only_get) + get_zlib_state(png_ptr)->flush_dist = + value <= 0 ? 0xEFFFFFFFU : (png_uint_32)/*SAFE*/value; + + return 0; +# endif /* WRITE_FLUSH */ + +# ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED + case PNG_SRW_CHECK_FOR_INVALID_INDEX: + /* The 'enabled' value is a FORTRAN style three-state: */ + if (value > 0) + png_ptr->palette_index_check = PNG_PALETTE_CHECK_ON; + + else if (value < 0) + png_ptr->palette_index_check = PNG_PALETTE_CHECK_OFF; + + else + png_ptr->palette_index_check = PNG_PALETTE_CHECK_DEFAULT; + + return 0; +# endif /* WRITE_CHECK_FOR_INVALID_INDEX */ + +# ifdef PNG_BENIGN_WRITE_ERRORS_SUPPORTED + case PNG_SRW_ERROR_HANDLING: + /* The parameter is a bit mask of what to set, the value is what to + * set it to. PNG_IDAT_ERRORS is ignored on write. + */ + if (value >= PNG_IGNORE && value <= PNG_ERROR && + parameter <= PNG_ALL_ERRORS) + { + if ((parameter & PNG_BENIGN_ERRORS) != 0U) + png_ptr->benign_error_action = value & 0x3U; + + if ((parameter & PNG_APP_WARNINGS) != 0U) + png_ptr->app_warning_action = value & 0x3U; + + if ((parameter & PNG_APP_ERRORS) != 0U) + png_ptr->app_error_action = value & 0x3U; + + return 0; + } + + return PNG_EINVAL; +# endif /* BENIGN_WRITE_ERRORS */ + + default: + return PNG_ENOSYS; /* not supported (whatever it is) */ + } +} +#endif /* WRITE */ diff --git a/ext/libzip/Android.mk b/ext/libzip/Android.mk index e05b292f44..544765db10 100644 --- a/ext/libzip/Android.mk +++ b/ext/libzip/Android.mk @@ -20,57 +20,116 @@ LOCAL_MODULE := libzip LOCAL_SRC_FILES :=\ zip_add.c \ zip_add_dir.c \ + zip_add_entry.c \ + zip_algorithm_deflate.c \ + zip_buffer.c \ zip_close.c \ zip_delete.c \ + zip_dir_add.c \ zip_dirent.c \ - zip_entry_free.c \ - zip_entry_new.c \ - zip_err_str.c \ + zip_discard.c \ + zip_entry.c \ zip_error.c \ zip_error_clear.c \ zip_error_get.c \ zip_error_get_sys_type.c \ zip_error_strerror.c \ zip_error_to_str.c \ + zip_extra_field.c \ + zip_extra_field_api.c \ zip_fclose.c \ + zip_fdopen.c \ + zip_file_add.c \ zip_file_error_clear.c \ zip_file_error_get.c \ + zip_file_get_comment.c \ + zip_file_get_external_attributes.c \ zip_file_get_offset.c \ + zip_file_rename.c \ + zip_file_replace.c \ + zip_file_set_comment.c \ + zip_file_set_encryption.c \ + zip_file_set_external_attributes.c \ + zip_file_set_mtime.c \ zip_file_strerror.c \ - zip_filerange_crc.c \ zip_fopen.c \ + zip_fopen_encrypted.c \ zip_fopen_index.c \ + zip_fopen_index_encrypted.c \ zip_fread.c \ - zip_free.c \ + zip_fseek.c \ + zip_ftell.c \ zip_get_archive_comment.c \ zip_get_archive_flag.c \ + zip_get_encryption_implementation.c \ zip_get_file_comment.c \ - zip_get_num_files.c \ zip_get_name.c \ + zip_get_num_entries.c \ + zip_get_num_files.c \ + zip_hash.c \ + zip_io_util.c \ + zip_libzip_version.c \ zip_memdup.c \ zip_name_locate.c \ zip_new.c \ zip_open.c \ + zip_pkware.c \ + zip_progress.c \ zip_rename.c \ zip_replace.c \ zip_set_archive_comment.c \ zip_set_archive_flag.c \ + zip_set_default_password.c \ zip_set_file_comment.c \ + zip_set_file_compression.c \ + zip_set_name.c \ + zip_source_accept_empty.c \ + zip_source_begin_write.c \ + zip_source_begin_write_cloning.c \ zip_source_buffer.c \ - zip_source_file.c \ - zip_source_filep.c \ + zip_source_call.c \ + zip_source_close.c \ + zip_source_commit_write.c \ + zip_source_compress.c \ + zip_source_crc.c \ + zip_source_error.c \ + zip_source_file_common.c \ + zip_source_file_stdio.c \ zip_source_free.c \ zip_source_function.c \ + zip_source_get_file_attributes.c \ + zip_source_is_deleted.c \ + zip_source_layered.c \ + zip_source_open.c \ + zip_source_pkware_decode.c \ + zip_source_pkware_encode.c \ + zip_source_read.c \ + zip_source_remove.c \ + zip_source_rollback_write.c \ + zip_source_seek.c \ + zip_source_seek_write.c \ + zip_source_stat.c \ + zip_source_supports.c \ + zip_source_tell.c \ + zip_source_tell_write.c \ + zip_source_window.c \ + zip_source_write.c \ zip_source_zip.c \ - zip_set_name.c \ + zip_source_zip_new.c \ zip_stat.c \ zip_stat_index.c \ zip_stat_init.c \ zip_strerror.c \ + zip_string.c \ zip_unchange.c \ zip_unchange_all.c \ zip_unchange_archive.c \ - zip_unchange_data.c + zip_unchange_data.c \ + zip_utf-8.c \ + zip_err_str.c \ + zip_mkstempm.c \ + zip_source_file_stdio_named.c \ + zip_random_unix.c LOCAL_CFLAGS := -O2 diff --git a/ext/libzip/CMakeLists.txt b/ext/libzip/CMakeLists.txt index c819364fb4..975ae4d7a5 100644 --- a/ext/libzip/CMakeLists.txt +++ b/ext/libzip/CMakeLists.txt @@ -1,60 +1,131 @@ set(SRCS -mkstemp.c -zip_add.c -zip_add_dir.c -zip_close.c -zip_delete.c -zip_dirent.c -zip_entry_free.c -zip_entry_new.c -zip_err_str.c -zip_error.c -zip_error_clear.c -zip_error_get.c -zip_error_get_sys_type.c -zip_error_strerror.c -zip_error_to_str.c -zip_fclose.c -zip_file_error_clear.c -zip_file_error_get.c -zip_file_get_offset.c -zip_file_strerror.c -zip_filerange_crc.c -zip_fopen.c -zip_fopen_index.c -zip_fread.c -zip_free.c -zip_get_archive_comment.c -zip_get_archive_flag.c -zip_get_file_comment.c -zip_get_name.c -zip_get_num_files.c -zip_memdup.c -zip_name_locate.c -zip_new.c -zip_open.c -zip_rename.c -zip_replace.c -zip_set_archive_comment.c -zip_set_archive_flag.c -zip_set_file_comment.c -zip_set_name.c -zip_source_buffer.c -zip_source_file.c -zip_source_filep.c -zip_source_free.c -zip_source_function.c -zip_source_zip.c -zip_stat.c -zip_stat_index.c -zip_stat_init.c -zip_strerror.c -zip_unchange.c -zip_unchange_all.c -zip_unchange_archive.c -zip_unchange_data.c) + zip_add.c + zip_add_dir.c + zip_add_entry.c + zip_algorithm_deflate.c + zip_buffer.c + zip_close.c + zip_delete.c + zip_dir_add.c + zip_dirent.c + zip_discard.c + zip_entry.c + zip_error.c + zip_error_clear.c + zip_error_get.c + zip_error_get_sys_type.c + zip_error_strerror.c + zip_error_to_str.c + zip_extra_field.c + zip_extra_field_api.c + zip_fclose.c + zip_fdopen.c + zip_file_add.c + zip_file_error_clear.c + zip_file_error_get.c + zip_file_get_comment.c + zip_file_get_external_attributes.c + zip_file_get_offset.c + zip_file_rename.c + zip_file_replace.c + zip_file_set_comment.c + zip_file_set_encryption.c + zip_file_set_external_attributes.c + zip_file_set_mtime.c + zip_file_strerror.c + zip_fopen.c + zip_fopen_encrypted.c + zip_fopen_index.c + zip_fopen_index_encrypted.c + zip_fread.c + zip_fseek.c + zip_ftell.c + zip_get_archive_comment.c + zip_get_archive_flag.c + zip_get_encryption_implementation.c + zip_get_file_comment.c + zip_get_name.c + zip_get_num_entries.c + zip_get_num_files.c + zip_hash.c + zip_io_util.c + zip_libzip_version.c + zip_memdup.c + zip_name_locate.c + zip_new.c + zip_open.c + zip_pkware.c + zip_progress.c + zip_rename.c + zip_replace.c + zip_set_archive_comment.c + zip_set_archive_flag.c + zip_set_default_password.c + zip_set_file_comment.c + zip_set_file_compression.c + zip_set_name.c + zip_source_accept_empty.c + zip_source_begin_write.c + zip_source_begin_write_cloning.c + zip_source_buffer.c + zip_source_call.c + zip_source_close.c + zip_source_commit_write.c + zip_source_compress.c + zip_source_crc.c + zip_source_error.c + zip_source_file_common.c + zip_source_file_stdio.c + zip_source_free.c + zip_source_function.c + zip_source_get_file_attributes.c + zip_source_is_deleted.c + zip_source_layered.c + zip_source_open.c + zip_source_pkware_decode.c + zip_source_pkware_encode.c + zip_source_read.c + zip_source_remove.c + zip_source_rollback_write.c + zip_source_seek.c + zip_source_seek_write.c + zip_source_stat.c + zip_source_supports.c + zip_source_tell.c + zip_source_tell_write.c + zip_source_window.c + zip_source_write.c + zip_source_zip.c + zip_source_zip_new.c + zip_stat.c + zip_stat_index.c + zip_stat_init.c + zip_strerror.c + zip_string.c + zip_unchange.c + zip_unchange_all.c + zip_unchange_archive.c + zip_unchange_data.c + zip_utf-8.c + zip_err_str.c) -set(SRCS ${SRCS}) +if(WIN32) + set(SRCS ${SRCS} + zip_source_file_win32.c + zip_source_file_win32_named.c + zip_source_file_win32_utf16.c + zip_source_file_win32_utf8.c) + if(CMAKE_SYSTEM_NAME MATCHES WindowsPhone OR CMAKE_SYSTEM_NAME MATCHES WindowsStore) + set(SRCS ${SRCS} zip_random_uwp.c) + else() + set(SRCS ${SRCS} zip_source_file_win32_ansi.c zip_random_win32.c) + endif() +else() + set(SRCS ${SRCS} + zip_mkstempm.c + zip_source_file_stdio_named.c + zip_random_unix.c) +endif() add_library(zip STATIC ${SRCS}) diff --git a/ext/libzip/compat.h b/ext/libzip/compat.h new file mode 100644 index 0000000000..a5cf728040 --- /dev/null +++ b/ext/libzip/compat.h @@ -0,0 +1,194 @@ +#ifndef _HAD_LIBZIP_COMPAT_H +#define _HAD_LIBZIP_COMPAT_H + +/* + compat.h -- compatibility defines. + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "zipconf.h" + +#include "config.h" + +/* to have *_MAX definitions for all types when compiling with g++ */ +#define __STDC_LIMIT_MACROS + +#ifdef _WIN32 +#ifndef ZIP_EXTERN +#ifndef ZIP_STATIC +#define ZIP_EXTERN __declspec(dllexport) +#endif +#endif +/* for dup(), close(), etc. */ +#include +#endif + +#ifdef HAVE_STDBOOL_H +#include +#else +typedef char bool; +#define true 1 +#define false 0 +#endif + +#include + +/* at least MinGW does not provide EOPNOTSUPP, see + * http://sourceforge.net/p/mingw/bugs/263/ + */ +#ifndef EOPNOTSUPP +#define EOPNOTSUPP EINVAL +#endif + +/* at least MinGW does not provide EOVERFLOW, see + * http://sourceforge.net/p/mingw/bugs/242/ + */ +#ifndef EOVERFLOW +#define EOVERFLOW EFBIG +#endif + +/* not supported on at least Windows */ +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif + +#ifdef _WIN32 +#if defined(HAVE__CLOSE) +#define close _close +#endif +#if defined(HAVE__DUP) +#define dup _dup +#endif +/* crashes reported when using fdopen instead of _fdopen on Windows/Visual Studio 10/Win64 */ +#if defined(HAVE__FDOPEN) +#define fdopen _fdopen +#endif +#if !defined(HAVE_FILENO) && defined(HAVE__FILENO) +#define fileno _fileno +#endif +#if defined(HAVE__SNPRINTF) +#define snprintf _snprintf +#endif +#if defined(HAVE__STRDUP) +#if !defined(HAVE_STRDUP) || defined(_WIN32) +#undef strdup +#define strdup _strdup +#endif +#endif +#if !defined(HAVE__SETMODE) && defined(HAVE_SETMODE) +#define _setmode setmode +#endif +#if !defined(HAVE_STRTOLL) && defined(HAVE__STRTOI64) +#define strtoll _strtoi64 +#endif +#if !defined(HAVE_STRTOULL) && defined(HAVE__STRTOUI64) +#define strtoull _strtoui64 +#endif +#if defined(HAVE__UNLINK) +#define unlink _unlink +#endif +#endif + +#ifndef HAVE_FSEEKO +#define fseeko(s, o, w) (fseek((s), (long int)(o), (w))) +#endif + +#ifndef HAVE_FTELLO +#define ftello(s) ((long)ftell((s))) +#endif + +#if !defined(HAVE_STRCASECMP) +#if defined(HAVE__STRICMP) +#define strcasecmp _stricmp +#elif defined(HAVE_STRICMP) +#define strcasecmp stricmp +#endif +#endif + +#if SIZEOF_OFF_T == 8 +#define ZIP_OFF_MAX ZIP_INT64_MAX +#define ZIP_OFF_MIN ZIP_INT64_MIN +#elif SIZEOF_OFF_T == 4 +#define ZIP_OFF_MAX ZIP_INT32_MAX +#define ZIP_OFF_MIN ZIP_INT32_MIN +#elif SIZEOF_OFF_T == 2 +#define ZIP_OFF_MAX ZIP_INT16_MAX +#define ZIP_OFF_MIN ZIP_INT16_MIN +#else +#error unsupported size of off_t +#endif + +#if defined(HAVE_FTELLO) && defined(HAVE_FSEEKO) +#define ZIP_FSEEK_MAX ZIP_OFF_MAX +#define ZIP_FSEEK_MIN ZIP_OFF_MIN +#else +#include +#define ZIP_FSEEK_MAX LONG_MAX +#define ZIP_FSEEK_MIN LONG_MIN +#endif + +#ifndef SIZE_MAX +#if SIZEOF_SIZE_T == 8 +#define SIZE_MAX ZIP_INT64_MAX +#elif SIZEOF_SIZE_T == 4 +#define SIZE_MAX ZIP_INT32_MAX +#elif SIZEOF_SIZE_T == 2 +#define SIZE_MAX ZIP_INT16_MAX +#else +#error unsupported size of size_t +#endif +#endif + +#ifndef PRId64 +#ifdef _MSC_VER +#define PRId64 "I64d" +#else +#define PRId64 "lld" +#endif +#endif + +#ifndef PRIu64 +#ifdef _MSC_VER +#define PRIu64 "I64u" +#else +#define PRIu64 "llu" +#endif +#endif + +#ifndef S_ISDIR +#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) +#endif + +#ifndef S_ISREG +#define S_ISREG(mode) (((mode)&S_IFMT) == S_IFREG) +#endif + +#endif /* compat.h */ diff --git a/ext/libzip/config.h b/ext/libzip/config.h index 456884aff4..a5d26ea015 100644 --- a/ext/libzip/config.h +++ b/ext/libzip/config.h @@ -1,103 +1,60 @@ -/* Config header for Android */ -/* Define to 1 if you have the declaration of `tzname', and to 0 if you don't. - */ -/* #undef HAVE_DECL_TZNAME */ +#ifndef HAD_CONFIG_H +#define HAD_CONFIG_H +#ifndef _HAD_ZIPCONF_H +#include "zipconf.h" +#endif -/* Define to 1 if you have the header file. */ -#define HAVE_DLFCN_H 0 +#if defined(WINAPI_FAMILY) && defined(WINAPI_FAMILY_PARTITION) +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP +#define MS_UWP +#define _WIN32_WINNT 0x0602 +#endif +#endif -/* Define to 1 if you have the `fseeko' function. */ -#define HAVE_FSEEKO 0 - -/* Define to 1 if you have the `ftello' function. */ -#define HAVE_FTELLO 0 - -/* Define to 1 if you have the header file. */ -#define HAVE_INTTYPES_H 0 - -/* Define to 1 if you have the `z' library (-lz). */ -#define HAVE_LIBZ 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_MEMORY_H 0 - -/* Define to 1 if you have the `mkstemp' function. */ +/* BEGIN DEFINES */ +#ifdef _WIN32 +#define HAVE__CLOSE +#define HAVE__DUP +#define HAVE__FDOPEN +#define HAVE__FILENO +#define HAVE__SETMODE +#define HAVE__STRDUP +#define HAVE__STRICMP +#define HAVE__STRTOI64 +#define HAVE__STRTOUI64 +#define HAVE__UMASK +#define HAVE__UNLINK +#endif +#ifndef MS_UWP +#define HAVE_FILENO +#define HAVE_FSEEKO +#define HAVE_FTELLO +#define HAVE_GETPROGNAME +#endif #ifndef _WIN32 +#define HAVE_LOCALTIME_R #define HAVE_MKSTEMP 1 #endif - -/* Define to 1 if you have the `MoveFileExA' function. */ -#ifdef _WIN32 -#define HAVE_MOVEFILEEXA 1 +#define HAVE_SNPRINTF +#define HAVE_STRDUP +#if !defined(__MINGW32__) && defined(_WIN32) +#define HAVE_STRICMP +#else +#define HAVE_STRCASECMP #endif - -/* Define to 1 if you have the header file. */ -#define HAVE_STDINT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the header file. */ #ifndef _WIN32 #define HAVE_STRINGS_H 1 +#define HAVE_UNISTD_H #endif - -/* Define to 1 if you have the header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if `tm_zone' is a member of `struct tm'. */ -// #define HAVE_STRUCT_TM_TM_ZONE 0 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_STAT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TYPES_H 1 - -/* Define to 1 if your `struct tm' has `tm_zone'. Deprecated, use - `HAVE_STRUCT_TM_TM_ZONE' instead. */ -#define HAVE_TM_ZONE 1 - -/* Define to 1 if you don't have `tm_zone' but do have the external array - `tzname'. */ -/* #undef HAVE_TZNAME */ - -/* Define to 1 if you have the header file. */ -#define HAVE_UNISTD_H 1 - -/* Define to the sub-directory in which libtool stores uninstalled libraries. - */ -#define LT_OBJDIR ".libs/" - -/* Define to 1 if your C compiler doesn't accept -c and -o together. */ -/* #undef NO_MINUS_C_MINUS_O */ - -/* Name of package */ +#if defined(_M_X64) || defined(__amd64__) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64) || defined(__mips64__) +#define SIZEOF_OFF_T 8 +#define SIZEOF_SIZE_T 8 +#else +#define SIZEOF_OFF_T 4 +#define SIZEOF_SIZE_T 4 +#endif +/* END DEFINES */ #define PACKAGE "libzip" +#define VERSION "1.7.3" -/* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "libzip@nih.at" - -/* Define to the full name of this package. */ -#define PACKAGE_NAME "libzip" - -/* Define to the full name and version of this package. */ -#define PACKAGE_STRING "libzip 0.9.3" - -/* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "libzip" - -/* Define to the home page for this package. */ -#define PACKAGE_URL "" - -/* Define to the version of this package. */ -#define PACKAGE_VERSION "0.9.3" - -/* Define to 1 if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Define to 1 if your declares `struct tm'. */ -/* #undef TM_IN_SYS_TIME */ - -/* Version number of package */ -#define VERSION "0.9.3" +#endif /* HAD_CONFIG_H */ diff --git a/ext/libzip/mkstemp.c b/ext/libzip/mkstemp.c deleted file mode 100644 index c61e279f9d..0000000000 --- a/ext/libzip/mkstemp.c +++ /dev/null @@ -1,177 +0,0 @@ -/* $NiH: mkstemp.c,v 1.3 2006/04/23 14:51:45 wiz Exp $ */ - -/* Adapted from NetBSB libc by Dieter Baron */ - -/* NetBSD: gettemp.c,v 1.13 2003/12/05 00:57:36 uebayasi Exp */ - -/* - * Copyright (c) 1987, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "config.h" -#ifndef HAVE_MKSTEMP -#include -#include - -#include -#include -#include -#include -#include -#include - -#if defined(_WIN32) && !defined(__MINGW32__) -typedef int pid_t; -#define getpid rand -#endif - -#ifndef O_BINARY -#define O_BINARY 0 -#endif - -int -#ifdef UNICODE -_zip_mkstemp(wchar_t *path) -#else -_zip_mkstemp(char *path) -#endif -{ -#ifndef _WIN32 - int fd; -#endif -#ifdef UNICODE - wchar_t *start, *trv; - struct _stat sbuf; -#else - char *start, *trv; - struct stat sbuf; -#endif - - pid_t pid; - - /* To guarantee multiple calls generate unique names even if - the file is not created. 676 different possibilities with 7 - or more X's, 26 with 6 or less. */ - // Urgh, not threadsafe at all. -#ifdef UNICODE - static wchar_t xtra[3] = L"aa"; -#else - static char xtra[3] = "aa"; -#endif - int xcnt = 0; - - pid = getpid(); - - /* Move to end of path and count trailing X's. */ - for (trv = path; *trv; ++trv) - if (*trv == 'X') - xcnt++; - else - xcnt = 0; - - /* Use at least one from xtra. Use 2 if more than 6 X's. */ - if (*(trv - 1) == 'X') - *--trv = xtra[0]; - if (xcnt > 6 && *(trv - 1) == 'X') - *--trv = xtra[1]; - - /* Set remaining X's to pid digits with 0's to the left. */ - while (*--trv == 'X') { - *trv = (pid % 10) + '0'; - pid /= 10; - } - - /* update xtra for next call. */ - if (xtra[0] != 'z') - xtra[0]++; - else { - xtra[0] = 'a'; - if (xtra[1] != 'z') - xtra[1]++; - else - xtra[1] = 'a'; - } - - /* - * check the target directory; if you have six X's and it - * doesn't exist this runs for a *very* long time. - */ - for (start = trv + 1;; --trv) { - if (trv <= path) - break; - if (*trv == '/') { - *trv = '\0'; -#ifdef UNICODE - if (_wstat(path, &sbuf)) -#else - if (stat(path, &sbuf)) -#endif - return (0); -#ifndef _WIN32 - if (!S_ISDIR(sbuf.st_mode)) { - errno = ENOTDIR; - return (0); - } -#endif - *trv = '/'; - break; - } - } - - -#ifdef _WIN32 - return 0; -#else - - for (;;) { - if ((fd=open(path, O_CREAT|O_EXCL|O_RDWR|O_BINARY, 0600)) >= 0) - return (fd); - if (errno != EEXIST) - return (0); - - /* tricky little algorithm for backward compatibility */ - for (trv = start;;) { - if (!*trv) - return (0); - if (*trv == 'z') - *trv++ = 'a'; - else { - if (isdigit((unsigned char)*trv)) - *trv = 'a'; - else - ++*trv; - break; - } - } - } -#endif - - /*NOTREACHED*/ -} -#endif - diff --git a/ext/libzip/zip.h b/ext/libzip/zip.h index af753687bb..eda150aea0 100644 --- a/ext/libzip/zip.h +++ b/ext/libzip/zip.h @@ -3,7 +3,7 @@ /* zip.h -- exported declarations. - Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -20,7 +20,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -34,206 +34,443 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - - -#ifdef _MSC_VER -#ifndef ZIP_EXTERN -#define ZIP_EXTERN -// __declspec(dllexport) -#endif -#if defined(_M_X64) || defined(_M_ARM64) -typedef __int64 ssize_t; -#else -typedef __int32 ssize_t; -#endif -#else -#define ZIP_EXTERN -#endif #ifdef __cplusplus extern "C" { +#if 0 +} /* fix autoindent */ +#endif #endif -#include -#include -#include -#include +#include "zipconf.h" +#ifndef ZIP_EXTERN +#ifndef ZIP_STATIC +#ifdef _WIN32 +#define ZIP_EXTERN __declspec(dllimport) +#elif defined(__GNUC__) && __GNUC__ >= 4 +#define ZIP_EXTERN __attribute__((visibility("default"))) +#else +#define ZIP_EXTERN +#endif +#else +#define ZIP_EXTERN +#endif +#endif + +#include +#include +#include /* flags for zip_open */ -#define ZIP_CREATE 1 -#define ZIP_EXCL 2 -#define ZIP_CHECKCONS 4 +#define ZIP_CREATE 1 +#define ZIP_EXCL 2 +#define ZIP_CHECKCONS 4 +#define ZIP_TRUNCATE 8 +#define ZIP_RDONLY 16 /* flags for zip_name_locate, zip_fopen, zip_stat, ... */ -#define ZIP_FL_NOCASE 1 /* ignore case on name lookup */ -#define ZIP_FL_NODIR 2 /* ignore directory component */ -#define ZIP_FL_COMPRESSED 4 /* read compressed data */ -#define ZIP_FL_UNCHANGED 8 /* use original data, ignoring changes */ -#define ZIP_FL_RECOMPRESS 16 /* force recompression of data */ +#define ZIP_FL_NOCASE 1u /* ignore case on name lookup */ +#define ZIP_FL_NODIR 2u /* ignore directory component */ +#define ZIP_FL_COMPRESSED 4u /* read compressed data */ +#define ZIP_FL_UNCHANGED 8u /* use original data, ignoring changes */ +#define ZIP_FL_RECOMPRESS 16u /* force recompression of data */ +#define ZIP_FL_ENCRYPTED 32u /* read encrypted data (implies ZIP_FL_COMPRESSED) */ +#define ZIP_FL_ENC_GUESS 0u /* guess string encoding (is default) */ +#define ZIP_FL_ENC_RAW 64u /* get unmodified string */ +#define ZIP_FL_ENC_STRICT 128u /* follow specification strictly */ +#define ZIP_FL_LOCAL 256u /* in local header */ +#define ZIP_FL_CENTRAL 512u /* in central directory */ +/* 1024u reserved for internal use */ +#define ZIP_FL_ENC_UTF_8 2048u /* string is UTF-8 encoded */ +#define ZIP_FL_ENC_CP437 4096u /* string is CP437 encoded */ +#define ZIP_FL_OVERWRITE 8192u /* zip_file_add: if file with name exists, overwrite (replace) it */ /* archive global flags flags */ -#define ZIP_AFL_TORRENT 1 /* torrent zipped */ +#define ZIP_AFL_RDONLY 2u /* read only -- cannot be cleared */ + + +/* create a new extra field */ + +#define ZIP_EXTRA_FIELD_ALL ZIP_UINT16_MAX +#define ZIP_EXTRA_FIELD_NEW ZIP_UINT16_MAX + /* libzip error codes */ -#define ZIP_ER_OK 0 /* N No error */ -#define ZIP_ER_MULTIDISK 1 /* N Multi-disk zip archives not supported */ -#define ZIP_ER_RENAME 2 /* S Renaming temporary file failed */ -#define ZIP_ER_CLOSE 3 /* S Closing zip archive failed */ -#define ZIP_ER_SEEK 4 /* S Seek error */ -#define ZIP_ER_READ 5 /* S Read error */ -#define ZIP_ER_WRITE 6 /* S Write error */ -#define ZIP_ER_CRC 7 /* N CRC error */ -#define ZIP_ER_ZIPCLOSED 8 /* N Containing zip archive was closed */ -#define ZIP_ER_NOENT 9 /* N No such file */ -#define ZIP_ER_EXISTS 10 /* N File already exists */ -#define ZIP_ER_OPEN 11 /* S Can't open file */ -#define ZIP_ER_TMPOPEN 12 /* S Failure to create temporary file */ -#define ZIP_ER_ZLIB 13 /* Z Zlib error */ -#define ZIP_ER_MEMORY 14 /* N Malloc failure */ -#define ZIP_ER_CHANGED 15 /* N Entry has been changed */ -#define ZIP_ER_COMPNOTSUPP 16 /* N Compression method not supported */ -#define ZIP_ER_EOF 17 /* N Premature EOF */ -#define ZIP_ER_INVAL 18 /* N Invalid argument */ -#define ZIP_ER_NOZIP 19 /* N Not a zip archive */ -#define ZIP_ER_INTERNAL 20 /* N Internal error */ -#define ZIP_ER_INCONS 21 /* N Zip archive inconsistent */ -#define ZIP_ER_REMOVE 22 /* S Can't remove file */ -#define ZIP_ER_DELETED 23 /* N Entry has been deleted */ - +#define ZIP_ER_OK 0 /* N No error */ +#define ZIP_ER_MULTIDISK 1 /* N Multi-disk zip archives not supported */ +#define ZIP_ER_RENAME 2 /* S Renaming temporary file failed */ +#define ZIP_ER_CLOSE 3 /* S Closing zip archive failed */ +#define ZIP_ER_SEEK 4 /* S Seek error */ +#define ZIP_ER_READ 5 /* S Read error */ +#define ZIP_ER_WRITE 6 /* S Write error */ +#define ZIP_ER_CRC 7 /* N CRC error */ +#define ZIP_ER_ZIPCLOSED 8 /* N Containing zip archive was closed */ +#define ZIP_ER_NOENT 9 /* N No such file */ +#define ZIP_ER_EXISTS 10 /* N File already exists */ +#define ZIP_ER_OPEN 11 /* S Can't open file */ +#define ZIP_ER_TMPOPEN 12 /* S Failure to create temporary file */ +#define ZIP_ER_ZLIB 13 /* Z Zlib error */ +#define ZIP_ER_MEMORY 14 /* N Malloc failure */ +#define ZIP_ER_CHANGED 15 /* N Entry has been changed */ +#define ZIP_ER_COMPNOTSUPP 16 /* N Compression method not supported */ +#define ZIP_ER_EOF 17 /* N Premature end of file */ +#define ZIP_ER_INVAL 18 /* N Invalid argument */ +#define ZIP_ER_NOZIP 19 /* N Not a zip archive */ +#define ZIP_ER_INTERNAL 20 /* N Internal error */ +#define ZIP_ER_INCONS 21 /* N Zip archive inconsistent */ +#define ZIP_ER_REMOVE 22 /* S Can't remove file */ +#define ZIP_ER_DELETED 23 /* N Entry has been deleted */ +#define ZIP_ER_ENCRNOTSUPP 24 /* N Encryption method not supported */ +#define ZIP_ER_RDONLY 25 /* N Read-only archive */ +#define ZIP_ER_NOPASSWD 26 /* N No password provided */ +#define ZIP_ER_WRONGPASSWD 27 /* N Wrong password provided */ +#define ZIP_ER_OPNOTSUPP 28 /* N Operation not supported */ +#define ZIP_ER_INUSE 29 /* N Resource still in use */ +#define ZIP_ER_TELL 30 /* S Tell error */ +#define ZIP_ER_COMPRESSED_DATA 31 /* N Compressed data invalid */ +#define ZIP_ER_CANCELLED 32 /* N Operation cancelled */ /* type of system error value */ -#define ZIP_ET_NONE 0 /* sys_err unused */ -#define ZIP_ET_SYS 1 /* sys_err is errno */ -#define ZIP_ET_ZLIB 2 /* sys_err is zlib error code */ +#define ZIP_ET_NONE 0 /* sys_err unused */ +#define ZIP_ET_SYS 1 /* sys_err is errno */ +#define ZIP_ET_ZLIB 2 /* sys_err is zlib error code */ /* compression methods */ -#define ZIP_CM_DEFAULT -1 /* better of deflate or store */ -#define ZIP_CM_STORE 0 /* stored (uncompressed) */ -#define ZIP_CM_SHRINK 1 /* shrunk */ -#define ZIP_CM_REDUCE_1 2 /* reduced with factor 1 */ -#define ZIP_CM_REDUCE_2 3 /* reduced with factor 2 */ -#define ZIP_CM_REDUCE_3 4 /* reduced with factor 3 */ -#define ZIP_CM_REDUCE_4 5 /* reduced with factor 4 */ -#define ZIP_CM_IMPLODE 6 /* imploded */ +#define ZIP_CM_DEFAULT -1 /* better of deflate or store */ +#define ZIP_CM_STORE 0 /* stored (uncompressed) */ +#define ZIP_CM_SHRINK 1 /* shrunk */ +#define ZIP_CM_REDUCE_1 2 /* reduced with factor 1 */ +#define ZIP_CM_REDUCE_2 3 /* reduced with factor 2 */ +#define ZIP_CM_REDUCE_3 4 /* reduced with factor 3 */ +#define ZIP_CM_REDUCE_4 5 /* reduced with factor 4 */ +#define ZIP_CM_IMPLODE 6 /* imploded */ /* 7 - Reserved for Tokenizing compression algorithm */ -#define ZIP_CM_DEFLATE 8 /* deflated */ -#define ZIP_CM_DEFLATE64 9 /* deflate64 */ -#define ZIP_CM_PKWARE_IMPLODE 10 /* PKWARE imploding */ +#define ZIP_CM_DEFLATE 8 /* deflated */ +#define ZIP_CM_DEFLATE64 9 /* deflate64 */ +#define ZIP_CM_PKWARE_IMPLODE 10 /* PKWARE imploding */ /* 11 - Reserved by PKWARE */ -#define ZIP_CM_BZIP2 12 /* compressed using BZIP2 algorithm */ +#define ZIP_CM_BZIP2 12 /* compressed using BZIP2 algorithm */ /* 13 - Reserved by PKWARE */ -#define ZIP_CM_LZMA 14 /* LZMA (EFS) */ +#define ZIP_CM_LZMA 14 /* LZMA (EFS) */ /* 15-17 - Reserved by PKWARE */ -#define ZIP_CM_TERSE 18 /* compressed using IBM TERSE (new) */ -#define ZIP_CM_LZ77 19 /* IBM LZ77 z Architecture (PFS) */ -#define ZIP_CM_WAVPACK 97 /* WavPack compressed data */ -#define ZIP_CM_PPMD 98 /* PPMd version I, Rev 1 */ +#define ZIP_CM_TERSE 18 /* compressed using IBM TERSE (new) */ +#define ZIP_CM_LZ77 19 /* IBM LZ77 z Architecture (PFS) */ +#define ZIP_CM_LZMA2 33 +#define ZIP_CM_XZ 95 /* XZ compressed data */ +#define ZIP_CM_JPEG 96 /* Compressed Jpeg data */ +#define ZIP_CM_WAVPACK 97 /* WavPack compressed data */ +#define ZIP_CM_PPMD 98 /* PPMd version I, Rev 1 */ /* encryption methods */ -#define ZIP_EM_NONE 0 /* not encrypted */ -#define ZIP_EM_TRAD_PKWARE 1 /* traditional PKWARE encryption */ -#if 0 /* Strong Encryption Header not parsed yet */ -#define ZIP_EM_DES 0x6601 /* strong encryption: DES */ -#define ZIP_EM_RC2_OLD 0x6602 /* strong encryption: RC2, version < 5.2 */ -#define ZIP_EM_3DES_168 0x6603 -#define ZIP_EM_3DES_112 0x6609 -#define ZIP_EM_AES_128 0x660e -#define ZIP_EM_AES_192 0x660f -#define ZIP_EM_AES_256 0x6610 -#define ZIP_EM_RC2 0x6702 /* strong encryption: RC2, version >= 5.2 */ -#define ZIP_EM_RC4 0x6801 +#define ZIP_EM_NONE 0 /* not encrypted */ +#define ZIP_EM_TRAD_PKWARE 1 /* traditional PKWARE encryption */ +#if 0 /* Strong Encryption Header not parsed yet */ +#define ZIP_EM_DES 0x6601 /* strong encryption: DES */ +#define ZIP_EM_RC2_OLD 0x6602 /* strong encryption: RC2, version < 5.2 */ +#define ZIP_EM_3DES_168 0x6603 +#define ZIP_EM_3DES_112 0x6609 +#define ZIP_EM_PKZIP_AES_128 0x660e +#define ZIP_EM_PKZIP_AES_192 0x660f +#define ZIP_EM_PKZIP_AES_256 0x6610 +#define ZIP_EM_RC2 0x6702 /* strong encryption: RC2, version >= 5.2 */ +#define ZIP_EM_RC4 0x6801 #endif -#define ZIP_EM_UNKNOWN 0xffff /* unknown algorithm */ +#define ZIP_EM_AES_128 0x0101 /* Winzip AES encryption */ +#define ZIP_EM_AES_192 0x0102 +#define ZIP_EM_AES_256 0x0103 +#define ZIP_EM_UNKNOWN 0xffff /* unknown algorithm */ + +#define ZIP_OPSYS_DOS 0x00u +#define ZIP_OPSYS_AMIGA 0x01u +#define ZIP_OPSYS_OPENVMS 0x02u +#define ZIP_OPSYS_UNIX 0x03u +#define ZIP_OPSYS_VM_CMS 0x04u +#define ZIP_OPSYS_ATARI_ST 0x05u +#define ZIP_OPSYS_OS_2 0x06u +#define ZIP_OPSYS_MACINTOSH 0x07u +#define ZIP_OPSYS_Z_SYSTEM 0x08u +#define ZIP_OPSYS_CPM 0x09u +#define ZIP_OPSYS_WINDOWS_NTFS 0x0au +#define ZIP_OPSYS_MVS 0x0bu +#define ZIP_OPSYS_VSE 0x0cu +#define ZIP_OPSYS_ACORN_RISC 0x0du +#define ZIP_OPSYS_VFAT 0x0eu +#define ZIP_OPSYS_ALTERNATE_MVS 0x0fu +#define ZIP_OPSYS_BEOS 0x10u +#define ZIP_OPSYS_TANDEM 0x11u +#define ZIP_OPSYS_OS_400 0x12u +#define ZIP_OPSYS_OS_X 0x13u + +#define ZIP_OPSYS_DEFAULT ZIP_OPSYS_UNIX - enum zip_source_cmd { - ZIP_SOURCE_OPEN, /* prepare for reading */ - ZIP_SOURCE_READ, /* read data */ - ZIP_SOURCE_CLOSE, /* reading is done */ - ZIP_SOURCE_STAT, /* get meta information */ - ZIP_SOURCE_ERROR, /* get error information */ - ZIP_SOURCE_FREE /* cleanup and free resources */ + ZIP_SOURCE_OPEN, /* prepare for reading */ + ZIP_SOURCE_READ, /* read data */ + ZIP_SOURCE_CLOSE, /* reading is done */ + ZIP_SOURCE_STAT, /* get meta information */ + ZIP_SOURCE_ERROR, /* get error information */ + ZIP_SOURCE_FREE, /* cleanup and free resources */ + ZIP_SOURCE_SEEK, /* set position for reading */ + ZIP_SOURCE_TELL, /* get read position */ + ZIP_SOURCE_BEGIN_WRITE, /* prepare for writing */ + ZIP_SOURCE_COMMIT_WRITE, /* writing is done */ + ZIP_SOURCE_ROLLBACK_WRITE, /* discard written changes */ + ZIP_SOURCE_WRITE, /* write data */ + ZIP_SOURCE_SEEK_WRITE, /* set position for writing */ + ZIP_SOURCE_TELL_WRITE, /* get write position */ + ZIP_SOURCE_SUPPORTS, /* check whether source supports command */ + ZIP_SOURCE_REMOVE, /* remove file */ + ZIP_SOURCE_RESERVED_1, /* previously used internally */ + ZIP_SOURCE_BEGIN_WRITE_CLONING, /* like ZIP_SOURCE_BEGIN_WRITE, but keep part of original file */ + ZIP_SOURCE_ACCEPT_EMPTY, /* whether empty files are valid archives */ + ZIP_SOURCE_GET_FILE_ATTRIBUTES /* get additional file attributes */ +}; +typedef enum zip_source_cmd zip_source_cmd_t; + +#define ZIP_SOURCE_MAKE_COMMAND_BITMASK(cmd) (((zip_int64_t)1) << (cmd)) + +/* clang-format off */ + +#define ZIP_SOURCE_SUPPORTS_READABLE (ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_OPEN) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_READ) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_CLOSE) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_STAT) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_ERROR) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_FREE)) + +#define ZIP_SOURCE_SUPPORTS_SEEKABLE (ZIP_SOURCE_SUPPORTS_READABLE \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SEEK) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_TELL) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SUPPORTS)) + +#define ZIP_SOURCE_SUPPORTS_WRITABLE (ZIP_SOURCE_SUPPORTS_SEEKABLE \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_COMMIT_WRITE) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_ROLLBACK_WRITE) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_WRITE) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SEEK_WRITE) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_TELL_WRITE) \ + | ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_REMOVE)) + +/* clang-format on */ + +/* for use by sources */ +struct zip_source_args_seek { + zip_int64_t offset; + int whence; }; -typedef ssize_t (*zip_source_callback)(void *state, void *data, - size_t len, enum zip_source_cmd cmd); +typedef struct zip_source_args_seek zip_source_args_seek_t; +#define ZIP_SOURCE_GET_ARGS(type, data, len, error) ((len) < sizeof(type) ? zip_error_set((error), ZIP_ER_INVAL, 0), (type *)NULL : (type *)(data)) + + +/* error information */ +/* use zip_error_*() to access */ +struct zip_error { + int zip_err; /* libzip error code (ZIP_ER_*) */ + int sys_err; /* copy of errno (E*) or zlib error code */ + char *_Nullable str; /* string representation or NULL */ +}; + +#define ZIP_STAT_NAME 0x0001u +#define ZIP_STAT_INDEX 0x0002u +#define ZIP_STAT_SIZE 0x0004u +#define ZIP_STAT_COMP_SIZE 0x0008u +#define ZIP_STAT_MTIME 0x0010u +#define ZIP_STAT_CRC 0x0020u +#define ZIP_STAT_COMP_METHOD 0x0040u +#define ZIP_STAT_ENCRYPTION_METHOD 0x0080u +#define ZIP_STAT_FLAGS 0x0100u struct zip_stat { - const char *name; /* name of the file */ - int index; /* index within archive */ - unsigned int crc; /* crc of file data */ - time_t mtime; /* modification time */ - off_t size; /* size of file (uncompressed) */ - off_t comp_size; /* size of file (compressed) */ - unsigned short comp_method; /* compression method used */ - unsigned short encryption_method; /* encryption method used */ + zip_uint64_t valid; /* which fields have valid values */ + const char *_Nullable name; /* name of the file */ + zip_uint64_t index; /* index within archive */ + zip_uint64_t size; /* size of file (uncompressed) */ + zip_uint64_t comp_size; /* size of file (compressed) */ + time_t mtime; /* modification time */ + zip_uint32_t crc; /* crc of file data */ + zip_uint16_t comp_method; /* compression method used */ + zip_uint16_t encryption_method; /* encryption method used */ + zip_uint32_t flags; /* reserved for future use */ }; +struct zip_buffer_fragment { + zip_uint8_t *_Nonnull data; + zip_uint64_t length; +}; + +struct zip_file_attributes { + zip_uint64_t valid; /* which fields have valid values */ + zip_uint8_t version; /* version of this struct, currently 1 */ + zip_uint8_t host_system; /* host system on which file was created */ + zip_uint8_t ascii; /* flag whether file is ASCII text */ + zip_uint8_t version_needed; /* minimum version needed to extract file */ + zip_uint32_t external_file_attributes; /* external file attributes (host-system specific) */ + zip_uint16_t general_purpose_bit_flags; /* general purpose big flags, only some bits are honored */ + zip_uint16_t general_purpose_bit_mask; /* which bits in general_purpose_bit_flags are valid */ +}; + +#define ZIP_FILE_ATTRIBUTES_HOST_SYSTEM 0x0001u +#define ZIP_FILE_ATTRIBUTES_ASCII 0x0002u +#define ZIP_FILE_ATTRIBUTES_VERSION_NEEDED 0x0004u +#define ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES 0x0008u +#define ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS 0x0010u + struct zip; struct zip_file; struct zip_source; - +typedef struct zip zip_t; +typedef struct zip_error zip_error_t; +typedef struct zip_file zip_file_t; +typedef struct zip_file_attributes zip_file_attributes_t; +typedef struct zip_source zip_source_t; +typedef struct zip_stat zip_stat_t; +typedef struct zip_buffer_fragment zip_buffer_fragment_t; -ZIP_EXTERN int zip_add(struct zip *, const char *, struct zip_source *); -ZIP_EXTERN int zip_add_dir(struct zip *, const char *); -ZIP_EXTERN int zip_close(struct zip *); -ZIP_EXTERN int zip_delete(struct zip *, int); -ZIP_EXTERN void zip_error_clear(struct zip *); -ZIP_EXTERN void zip_error_get(struct zip *, int *, int *); -ZIP_EXTERN int zip_error_get_sys_type(int); -ZIP_EXTERN int zip_error_to_str(char *, size_t, int, int); -ZIP_EXTERN int zip_fclose(struct zip_file *); -ZIP_EXTERN void zip_file_error_clear(struct zip_file *); -ZIP_EXTERN void zip_file_error_get(struct zip_file *, int *, int *); -ZIP_EXTERN const char *zip_file_strerror(struct zip_file *); -ZIP_EXTERN struct zip_file *zip_fopen(struct zip *, const char *, int); -ZIP_EXTERN struct zip_file *zip_fopen_index(struct zip *, int, int); -ZIP_EXTERN ssize_t zip_fread(struct zip_file *, void *, size_t); -ZIP_EXTERN const char *zip_get_archive_comment(struct zip *, int *, int); -ZIP_EXTERN int zip_get_archive_flag(struct zip *, int, int); -ZIP_EXTERN const char *zip_get_file_comment(struct zip *, int, int *, int); -ZIP_EXTERN const char *zip_get_name(struct zip *, int, int); -ZIP_EXTERN int zip_get_num_files(struct zip *); -ZIP_EXTERN int zip_name_locate(struct zip *, const char *, int); -#ifdef UNICODE -ZIP_EXTERN struct zip *zip_open(const wchar_t *, int, int *); -#else -ZIP_EXTERN struct zip *zip_open(const char *, int, int *); +typedef zip_uint32_t zip_flags_t; + +typedef zip_int64_t (*zip_source_callback)(void *_Nullable, void *_Nullable, zip_uint64_t, zip_source_cmd_t); +typedef void (*zip_progress_callback)(zip_t *_Nonnull, double, void *_Nullable); +typedef int (*zip_cancel_callback)(zip_t *_Nonnull, void *_Nullable); + +#ifndef ZIP_DISABLE_DEPRECATED +typedef void (*zip_progress_callback_t)(double); +ZIP_EXTERN void zip_register_progress_callback(zip_t *_Nonnull, zip_progress_callback_t _Nullable); /* use zip_register_progress_callback_with_state */ + +ZIP_EXTERN zip_int64_t zip_add(zip_t *_Nonnull, const char *_Nonnull, zip_source_t *_Nonnull); /* use zip_file_add */ +ZIP_EXTERN zip_int64_t zip_add_dir(zip_t *_Nonnull, const char *_Nonnull); /* use zip_dir_add */ +ZIP_EXTERN const char *_Nullable zip_get_file_comment(zip_t *_Nonnull, zip_uint64_t, int *_Nullable, int); /* use zip_file_get_comment */ +ZIP_EXTERN int zip_get_num_files(zip_t *_Nonnull); /* use zip_get_num_entries instead */ +ZIP_EXTERN int zip_rename(zip_t *_Nonnull, zip_uint64_t, const char *_Nonnull); /* use zip_file_rename */ +ZIP_EXTERN int zip_replace(zip_t *_Nonnull, zip_uint64_t, zip_source_t *_Nonnull); /* use zip_file_replace */ +ZIP_EXTERN int zip_set_file_comment(zip_t *_Nonnull, zip_uint64_t, const char *_Nullable, int); /* use zip_file_set_comment */ +ZIP_EXTERN int zip_error_get_sys_type(int); /* use zip_error_system_type */ +ZIP_EXTERN void zip_error_get(zip_t *_Nonnull, int *_Nullable, int *_Nullable); /* use zip_get_error, zip_error_code_zip / zip_error_code_system */ +ZIP_EXTERN int zip_error_to_str(char *_Nonnull, zip_uint64_t, int, int); /* use zip_error_init_with_code / zip_error_strerror */ +ZIP_EXTERN void zip_file_error_get(zip_file_t *_Nonnull, int *_Nullable, int *_Nullable); /* use zip_file_get_error, zip_error_code_zip / zip_error_code_system */ #endif -ZIP_EXTERN int zip_rename(struct zip *, int, const char *); -ZIP_EXTERN int zip_replace(struct zip *, int, struct zip_source *); -ZIP_EXTERN int zip_set_archive_comment(struct zip *, const char *, int); -ZIP_EXTERN int zip_set_archive_flag(struct zip *, int, int); -ZIP_EXTERN int zip_set_file_comment(struct zip *, int, const char *, int); -ZIP_EXTERN struct zip_source *zip_source_buffer(struct zip *, const void *, - off_t, int); -ZIP_EXTERN struct zip_source *zip_source_file(struct zip *, const char *, - off_t, off_t); -ZIP_EXTERN struct zip_source *zip_source_filep(struct zip *, FILE *, - off_t, off_t); -ZIP_EXTERN void zip_source_free(struct zip_source *); -ZIP_EXTERN struct zip_source *zip_source_function(struct zip *, - zip_source_callback, void *); -ZIP_EXTERN struct zip_source *zip_source_zip(struct zip *, struct zip *, - int, int, off_t, off_t); -ZIP_EXTERN int zip_stat(struct zip *, const char *, int, struct zip_stat *); -ZIP_EXTERN int zip_stat_index(struct zip *, int, int, struct zip_stat *); -ZIP_EXTERN void zip_stat_init(struct zip_stat *); -ZIP_EXTERN const char *zip_strerror(struct zip *); -ZIP_EXTERN int zip_unchange(struct zip *, int); -ZIP_EXTERN int zip_unchange_all(struct zip *); -ZIP_EXTERN int zip_unchange_archive(struct zip *); + +ZIP_EXTERN int zip_close(zip_t *_Nonnull); +ZIP_EXTERN int zip_delete(zip_t *_Nonnull, zip_uint64_t); +ZIP_EXTERN zip_int64_t zip_dir_add(zip_t *_Nonnull, const char *_Nonnull, zip_flags_t); +ZIP_EXTERN void zip_discard(zip_t *_Nonnull); + +ZIP_EXTERN zip_error_t *_Nonnull zip_get_error(zip_t *_Nonnull); +ZIP_EXTERN void zip_error_clear(zip_t *_Nonnull); +ZIP_EXTERN int zip_error_code_zip(const zip_error_t *_Nonnull); +ZIP_EXTERN int zip_error_code_system(const zip_error_t *_Nonnull); +ZIP_EXTERN void zip_error_fini(zip_error_t *_Nonnull); +ZIP_EXTERN void zip_error_init(zip_error_t *_Nonnull); +ZIP_EXTERN void zip_error_init_with_code(zip_error_t *_Nonnull, int); +ZIP_EXTERN void zip_error_set(zip_error_t *_Nullable, int, int); +ZIP_EXTERN const char *_Nonnull zip_error_strerror(zip_error_t *_Nonnull); +ZIP_EXTERN int zip_error_system_type(const zip_error_t *_Nonnull); +ZIP_EXTERN zip_int64_t zip_error_to_data(const zip_error_t *_Nonnull, void *_Nonnull, zip_uint64_t); + +ZIP_EXTERN int zip_fclose(zip_file_t *_Nonnull); +ZIP_EXTERN zip_t *_Nullable zip_fdopen(int, int, int *_Nullable); +ZIP_EXTERN zip_int64_t zip_file_add(zip_t *_Nonnull, const char *_Nonnull, zip_source_t *_Nonnull, zip_flags_t); +ZIP_EXTERN void zip_file_attributes_init(zip_file_attributes_t *_Nonnull); +ZIP_EXTERN void zip_file_error_clear(zip_file_t *_Nonnull); +ZIP_EXTERN int zip_file_extra_field_delete(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, zip_flags_t); +ZIP_EXTERN int zip_file_extra_field_delete_by_id(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, zip_uint16_t, zip_flags_t); +ZIP_EXTERN int zip_file_extra_field_set(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, zip_uint16_t, const zip_uint8_t *_Nullable, zip_uint16_t, zip_flags_t); +ZIP_EXTERN zip_int16_t zip_file_extra_fields_count(zip_t *_Nonnull, zip_uint64_t, zip_flags_t); +ZIP_EXTERN zip_int16_t zip_file_extra_fields_count_by_id(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, zip_flags_t); +ZIP_EXTERN const zip_uint8_t *_Nullable zip_file_extra_field_get(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, zip_uint16_t *_Nullable, zip_uint16_t *_Nullable, zip_flags_t); +ZIP_EXTERN const zip_uint8_t *_Nullable zip_file_extra_field_get_by_id(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, zip_uint16_t, zip_uint16_t *_Nullable, zip_flags_t); +ZIP_EXTERN const char *_Nullable zip_file_get_comment(zip_t *_Nonnull, zip_uint64_t, zip_uint32_t *_Nullable, zip_flags_t); +ZIP_EXTERN zip_error_t *_Nonnull zip_file_get_error(zip_file_t *_Nonnull); +ZIP_EXTERN int zip_file_get_external_attributes(zip_t *_Nonnull, zip_uint64_t, zip_flags_t, zip_uint8_t *_Nullable, zip_uint32_t *_Nullable); +ZIP_EXTERN int zip_file_rename(zip_t *_Nonnull, zip_uint64_t, const char *_Nonnull, zip_flags_t); +ZIP_EXTERN int zip_file_replace(zip_t *_Nonnull, zip_uint64_t, zip_source_t *_Nonnull, zip_flags_t); +ZIP_EXTERN int zip_file_set_comment(zip_t *_Nonnull, zip_uint64_t, const char *_Nullable, zip_uint16_t, zip_flags_t); +ZIP_EXTERN int zip_file_set_dostime(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, zip_uint16_t, zip_flags_t); +ZIP_EXTERN int zip_file_set_encryption(zip_t *_Nonnull, zip_uint64_t, zip_uint16_t, const char *_Nullable); +ZIP_EXTERN int zip_file_set_external_attributes(zip_t *_Nonnull, zip_uint64_t, zip_flags_t, zip_uint8_t, zip_uint32_t); +ZIP_EXTERN int zip_file_set_mtime(zip_t *_Nonnull, zip_uint64_t, time_t, zip_flags_t); +ZIP_EXTERN const char *_Nonnull zip_file_strerror(zip_file_t *_Nonnull); +ZIP_EXTERN zip_file_t *_Nullable zip_fopen(zip_t *_Nonnull, const char *_Nonnull, zip_flags_t); +ZIP_EXTERN zip_file_t *_Nullable zip_fopen_encrypted(zip_t *_Nonnull, const char *_Nonnull, zip_flags_t, const char *_Nullable); +ZIP_EXTERN zip_file_t *_Nullable zip_fopen_index(zip_t *_Nonnull, zip_uint64_t, zip_flags_t); +ZIP_EXTERN zip_file_t *_Nullable zip_fopen_index_encrypted(zip_t *_Nonnull, zip_uint64_t, zip_flags_t, const char *_Nullable); +ZIP_EXTERN zip_int64_t zip_fread(zip_file_t *_Nonnull, void *_Nonnull, zip_uint64_t); +ZIP_EXTERN zip_int8_t zip_fseek(zip_file_t *_Nonnull, zip_int64_t, int); +ZIP_EXTERN zip_int64_t zip_ftell(zip_file_t *_Nonnull); +ZIP_EXTERN const char *_Nullable zip_get_archive_comment(zip_t *_Nonnull, int *_Nullable, zip_flags_t); +ZIP_EXTERN int zip_get_archive_flag(zip_t *_Nonnull, zip_flags_t, zip_flags_t); +ZIP_EXTERN const char *_Nullable zip_get_name(zip_t *_Nonnull, zip_uint64_t, zip_flags_t); +ZIP_EXTERN zip_int64_t zip_get_num_entries(zip_t *_Nonnull, zip_flags_t); +ZIP_EXTERN const char *_Nonnull zip_libzip_version(void); +ZIP_EXTERN zip_int64_t zip_name_locate(zip_t *_Nonnull, const char *_Nonnull, zip_flags_t); +ZIP_EXTERN zip_t *_Nullable zip_open(const char *_Nonnull, int, int *_Nullable); +ZIP_EXTERN zip_t *_Nullable zip_open_from_source(zip_source_t *_Nonnull, int, zip_error_t *_Nullable); +ZIP_EXTERN int zip_register_progress_callback_with_state(zip_t *_Nonnull, double, zip_progress_callback _Nullable, void (*_Nullable)(void *_Nullable), void *_Nullable); +ZIP_EXTERN int zip_register_cancel_callback_with_state(zip_t *_Nonnull, zip_cancel_callback _Nullable, void (*_Nullable)(void *_Nullable), void *_Nullable); +ZIP_EXTERN int zip_set_archive_comment(zip_t *_Nonnull, const char *_Nullable, zip_uint16_t); +ZIP_EXTERN int zip_set_archive_flag(zip_t *_Nonnull, zip_flags_t, int); +ZIP_EXTERN int zip_set_default_password(zip_t *_Nonnull, const char *_Nullable); +ZIP_EXTERN int zip_set_file_compression(zip_t *_Nonnull, zip_uint64_t, zip_int32_t, zip_uint32_t); +ZIP_EXTERN int zip_source_begin_write(zip_source_t *_Nonnull); +ZIP_EXTERN int zip_source_begin_write_cloning(zip_source_t *_Nonnull, zip_uint64_t); +ZIP_EXTERN zip_source_t *_Nullable zip_source_buffer(zip_t *_Nonnull, const void *_Nullable, zip_uint64_t, int); +ZIP_EXTERN zip_source_t *_Nullable zip_source_buffer_create(const void *_Nullable, zip_uint64_t, int, zip_error_t *_Nullable); +ZIP_EXTERN zip_source_t *_Nullable zip_source_buffer_fragment(zip_t *_Nonnull, const zip_buffer_fragment_t *_Nonnull, zip_uint64_t, int); +ZIP_EXTERN zip_source_t *_Nullable zip_source_buffer_fragment_create(const zip_buffer_fragment_t *_Nullable, zip_uint64_t, int, zip_error_t *_Nullable); +ZIP_EXTERN int zip_source_close(zip_source_t *_Nonnull); +ZIP_EXTERN int zip_source_commit_write(zip_source_t *_Nonnull); +ZIP_EXTERN zip_error_t *_Nonnull zip_source_error(zip_source_t *_Nonnull); +ZIP_EXTERN zip_source_t *_Nullable zip_source_file(zip_t *_Nonnull, const char *_Nonnull, zip_uint64_t, zip_int64_t); +ZIP_EXTERN zip_source_t *_Nullable zip_source_file_create(const char *_Nonnull, zip_uint64_t, zip_int64_t, zip_error_t *_Nullable); +ZIP_EXTERN zip_source_t *_Nullable zip_source_filep(zip_t *_Nonnull, FILE *_Nonnull, zip_uint64_t, zip_int64_t); +ZIP_EXTERN zip_source_t *_Nullable zip_source_filep_create(FILE *_Nonnull, zip_uint64_t, zip_int64_t, zip_error_t *_Nullable); +ZIP_EXTERN void zip_source_free(zip_source_t *_Nullable); +ZIP_EXTERN zip_source_t *_Nullable zip_source_function(zip_t *_Nonnull, zip_source_callback _Nonnull, void *_Nullable); +ZIP_EXTERN zip_source_t *_Nullable zip_source_function_create(zip_source_callback _Nonnull, void *_Nullable, zip_error_t *_Nullable); +ZIP_EXTERN int zip_source_get_file_attributes(zip_source_t *_Nonnull, zip_file_attributes_t *_Nonnull); +ZIP_EXTERN int zip_source_is_deleted(zip_source_t *_Nonnull); +ZIP_EXTERN void zip_source_keep(zip_source_t *_Nonnull); +ZIP_EXTERN zip_int64_t zip_source_make_command_bitmap(zip_source_cmd_t, ...); +ZIP_EXTERN int zip_source_open(zip_source_t *_Nonnull); +ZIP_EXTERN zip_int64_t zip_source_read(zip_source_t *_Nonnull, void *_Nonnull, zip_uint64_t); +ZIP_EXTERN void zip_source_rollback_write(zip_source_t *_Nonnull); +ZIP_EXTERN int zip_source_seek(zip_source_t *_Nonnull, zip_int64_t, int); +ZIP_EXTERN zip_int64_t zip_source_seek_compute_offset(zip_uint64_t, zip_uint64_t, void *_Nonnull, zip_uint64_t, zip_error_t *_Nullable); +ZIP_EXTERN int zip_source_seek_write(zip_source_t *_Nonnull, zip_int64_t, int); +ZIP_EXTERN int zip_source_stat(zip_source_t *_Nonnull, zip_stat_t *_Nonnull); +ZIP_EXTERN zip_int64_t zip_source_tell(zip_source_t *_Nonnull); +ZIP_EXTERN zip_int64_t zip_source_tell_write(zip_source_t *_Nonnull); +#ifdef _WIN32 +ZIP_EXTERN zip_source_t *zip_source_win32a(zip_t *, const char *, zip_uint64_t, zip_int64_t); +ZIP_EXTERN zip_source_t *zip_source_win32a_create(const char *, zip_uint64_t, zip_int64_t, zip_error_t *); +ZIP_EXTERN zip_source_t *zip_source_win32handle(zip_t *, void *, zip_uint64_t, zip_int64_t); +ZIP_EXTERN zip_source_t *zip_source_win32handle_create(void *, zip_uint64_t, zip_int64_t, zip_error_t *); +ZIP_EXTERN zip_source_t *zip_source_win32w(zip_t *, const wchar_t *, zip_uint64_t, zip_int64_t); +ZIP_EXTERN zip_source_t *zip_source_win32w_create(const wchar_t *, zip_uint64_t, zip_int64_t, zip_error_t *); +#endif +ZIP_EXTERN zip_int64_t zip_source_write(zip_source_t *_Nonnull, const void *_Nullable, zip_uint64_t); +ZIP_EXTERN zip_source_t *_Nullable zip_source_zip(zip_t *_Nonnull, zip_t *_Nonnull, zip_uint64_t, zip_flags_t, zip_uint64_t, zip_int64_t); +ZIP_EXTERN int zip_stat(zip_t *_Nonnull, const char *_Nonnull, zip_flags_t, zip_stat_t *_Nonnull); +ZIP_EXTERN int zip_stat_index(zip_t *_Nonnull, zip_uint64_t, zip_flags_t, zip_stat_t *_Nonnull); +ZIP_EXTERN void zip_stat_init(zip_stat_t *_Nonnull); +ZIP_EXTERN const char *_Nonnull zip_strerror(zip_t *_Nonnull); +ZIP_EXTERN int zip_unchange(zip_t *_Nonnull, zip_uint64_t); +ZIP_EXTERN int zip_unchange_all(zip_t *_Nonnull); +ZIP_EXTERN int zip_unchange_archive(zip_t *_Nonnull); +ZIP_EXTERN int zip_compression_method_supported(zip_int32_t method, int compress); +ZIP_EXTERN int zip_encryption_method_supported(zip_uint16_t method, int encode); #ifdef __cplusplus } diff --git a/ext/libzip/zip_add.c b/ext/libzip/zip_add.c index f6bdb2ab92..1954235708 100644 --- a/ext/libzip/zip_add.c +++ b/ext/libzip/zip_add.c @@ -1,6 +1,6 @@ /* zip_add.c -- add file via callback function - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,19 +31,19 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - +#define _ZIP_COMPILING_DEPRECATED #include "zipint.h" - -ZIP_EXTERN int -zip_add(struct zip *za, const char *name, struct zip_source *source) -{ - if (name == NULL || source == NULL) { - _zip_error_set(&za->error, ZIP_ER_INVAL, 0); - return -1; - } - - return _zip_replace(za, -1, name, source); +/* + NOTE: Return type is signed so we can return -1 on error. + The index can not be larger than ZIP_INT64_MAX since the size + of the central directory cannot be larger than + ZIP_UINT64_MAX, and each entry is larger than 2 bytes. +*/ + +ZIP_EXTERN zip_int64_t +zip_add(zip_t *za, const char *name, zip_source_t *source) { + return zip_file_add(za, name, source, 0); } diff --git a/ext/libzip/zip_add_dir.c b/ext/libzip/zip_add_dir.c index a058640c1e..88fcf53a43 100644 --- a/ext/libzip/zip_add_dir.c +++ b/ext/libzip/zip_add_dir.c @@ -1,6 +1,6 @@ /* zip_add_dir.c -- add directory - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,53 +31,14 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef _MSC_VER -#pragma warning (disable:4996) -#pragma warning (disable:4244) -#endif - -#include -#include +#define _ZIP_COMPILING_DEPRECATED #include "zipint.h" - -ZIP_EXTERN int -zip_add_dir(struct zip *za, const char *name) -{ - int len, ret; - char *s; - struct zip_source *source; +/* NOTE: Signed due to -1 on error. See zip_add.c for more details. */ - if (name == NULL) { - _zip_error_set(&za->error, ZIP_ER_INVAL, 0); - return -1; - } - - s = NULL; - len = (int)strlen(name); - - if (name[len-1] != '/') { - if ((s=(char *)malloc(len+2)) == NULL) { - _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); - return -1; - } - strcpy(s, name); - s[len] = '/'; - s[len+1] = '\0'; - } - - if ((source=zip_source_buffer(za, NULL, 0, 0)) == NULL) { - free(s); - return -1; - } - - ret = _zip_replace(za, -1, s ? s : name, source); - - free(s); - if (ret < 0) - zip_source_free(source); - - return ret; +ZIP_EXTERN zip_int64_t +zip_add_dir(zip_t *za, const char *name) { + return zip_dir_add(za, name, 0); } diff --git a/ext/libzip/zip_add_entry.c b/ext/libzip/zip_add_entry.c new file mode 100644 index 0000000000..f52e0b5ca2 --- /dev/null +++ b/ext/libzip/zip_add_entry.c @@ -0,0 +1,80 @@ +/* + zip_add_entry.c -- create and init struct zip_entry + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include + +#include "zipint.h" + + +/* NOTE: Signed due to -1 on error. See zip_add.c for more details. */ + +zip_int64_t +_zip_add_entry(zip_t *za) { + zip_uint64_t idx; + + if (za->nentry + 1 >= za->nentry_alloc) { + zip_entry_t *rentries; + zip_uint64_t nalloc = za->nentry_alloc; + zip_uint64_t additional_entries = 2 * nalloc; + zip_uint64_t realloc_size; + + if (additional_entries < 16) { + additional_entries = 16; + } + else if (additional_entries > 1024) { + additional_entries = 1024; + } + /* neither + nor * overflows can happen: nentry_alloc * sizeof(struct zip_entry) < UINT64_MAX */ + nalloc += additional_entries; + realloc_size = sizeof(struct zip_entry) * (size_t)nalloc; + + if (sizeof(struct zip_entry) * (size_t)za->nentry_alloc > realloc_size) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + rentries = (zip_entry_t *)realloc(za->entry, sizeof(struct zip_entry) * (size_t)nalloc); + if (!rentries) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + za->entry = rentries; + za->nentry_alloc = nalloc; + } + + idx = za->nentry++; + + _zip_entry_init(za->entry + idx); + + return (zip_int64_t)idx; +} diff --git a/ext/libzip/zip_algorithm_deflate.c b/ext/libzip/zip_algorithm_deflate.c new file mode 100644 index 0000000000..d28a1ac50a --- /dev/null +++ b/ext/libzip/zip_algorithm_deflate.c @@ -0,0 +1,250 @@ +/* + zip_algorithm_deflate.c -- deflate (de)compression routines + Copyright (C) 2017-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "zipint.h" + +#include +#include +#include + +struct ctx { + zip_error_t *error; + bool compress; + int compression_flags; + bool end_of_input; + z_stream zstr; +}; + + +static void * +allocate(bool compress, int compression_flags, zip_error_t *error) { + struct ctx *ctx; + + if ((ctx = (struct ctx *)malloc(sizeof(*ctx))) == NULL) { + zip_error_set(error, ZIP_ET_SYS, errno); + return NULL; + } + + ctx->error = error; + ctx->compress = compress; + ctx->compression_flags = compression_flags; + if (ctx->compression_flags < 1 || ctx->compression_flags > 9) { + ctx->compression_flags = Z_BEST_COMPRESSION; + } + ctx->end_of_input = false; + + ctx->zstr.zalloc = Z_NULL; + ctx->zstr.zfree = Z_NULL; + ctx->zstr.opaque = NULL; + + return ctx; +} + + +static void * +compress_allocate(zip_uint16_t method, int compression_flags, zip_error_t *error) { + return allocate(true, compression_flags, error); +} + + +static void * +decompress_allocate(zip_uint16_t method, int compression_flags, zip_error_t *error) { + return allocate(false, compression_flags, error); +} + + +static void +deallocate(void *ud) { + struct ctx *ctx = (struct ctx *)ud; + + free(ctx); +} + + +static zip_uint16_t +general_purpose_bit_flags(void *ud) { + struct ctx *ctx = (struct ctx *)ud; + + if (!ctx->compress) { + return 0; + } + + if (ctx->compression_flags < 3) { + return 2 << 1; + } + else if (ctx->compression_flags > 7) { + return 1 << 1; + } + return 0; +} + + +static bool +start(void *ud) { + struct ctx *ctx = (struct ctx *)ud; + int ret; + + ctx->zstr.avail_in = 0; + ctx->zstr.next_in = NULL; + ctx->zstr.avail_out = 0; + ctx->zstr.next_out = NULL; + + if (ctx->compress) { + /* negative value to tell zlib not to write a header */ + ret = deflateInit2(&ctx->zstr, ctx->compression_flags, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); + } + else { + ret = inflateInit2(&ctx->zstr, -MAX_WBITS); + } + + if (ret != Z_OK) { + zip_error_set(ctx->error, ZIP_ER_ZLIB, ret); + return false; + } + + + return true; +} + + +static bool +end(void *ud) { + struct ctx *ctx = (struct ctx *)ud; + int err; + + if (ctx->compress) { + err = deflateEnd(&ctx->zstr); + } + else { + err = inflateEnd(&ctx->zstr); + } + + if (err != Z_OK) { + zip_error_set(ctx->error, ZIP_ER_ZLIB, err); + return false; + } + + return true; +} + + +static bool +input(void *ud, zip_uint8_t *data, zip_uint64_t length) { + struct ctx *ctx = (struct ctx *)ud; + + if (length > UINT_MAX || ctx->zstr.avail_in > 0) { + zip_error_set(ctx->error, ZIP_ER_INVAL, 0); + return false; + } + + ctx->zstr.avail_in = (uInt)length; + ctx->zstr.next_in = (Bytef *)data; + + return true; +} + + +static void +end_of_input(void *ud) { + struct ctx *ctx = (struct ctx *)ud; + + ctx->end_of_input = true; +} + + +static zip_compression_status_t +process(void *ud, zip_uint8_t *data, zip_uint64_t *length) { + struct ctx *ctx = (struct ctx *)ud; + + int ret; + + ctx->zstr.avail_out = (uInt)ZIP_MIN(UINT_MAX, *length); + ctx->zstr.next_out = (Bytef *)data; + + if (ctx->compress) { + ret = deflate(&ctx->zstr, ctx->end_of_input ? Z_FINISH : 0); + } + else { + ret = inflate(&ctx->zstr, Z_SYNC_FLUSH); + } + + *length = *length - ctx->zstr.avail_out; + + switch (ret) { + case Z_OK: + return ZIP_COMPRESSION_OK; + + case Z_STREAM_END: + return ZIP_COMPRESSION_END; + + case Z_BUF_ERROR: + if (ctx->zstr.avail_in == 0) { + return ZIP_COMPRESSION_NEED_DATA; + } + + /* fallthrough */ + + default: + zip_error_set(ctx->error, ZIP_ER_ZLIB, ret); + return ZIP_COMPRESSION_ERROR; + } +} + +/* clang-format off */ + +zip_compression_algorithm_t zip_algorithm_deflate_compress = { + compress_allocate, + deallocate, + general_purpose_bit_flags, + 20, + start, + end, + input, + end_of_input, + process +}; + + +zip_compression_algorithm_t zip_algorithm_deflate_decompress = { + decompress_allocate, + deallocate, + general_purpose_bit_flags, + 20, + start, + end, + input, + end_of_input, + process +}; + +/* clang-format on */ diff --git a/ext/libzip/zip_buffer.c b/ext/libzip/zip_buffer.c new file mode 100644 index 0000000000..8c24b27e0d --- /dev/null +++ b/ext/libzip/zip_buffer.c @@ -0,0 +1,324 @@ +/* + zip_buffer.c -- bounds checked access to memory buffer + Copyright (C) 2014-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "zipint.h" + +zip_uint8_t * +_zip_buffer_data(zip_buffer_t *buffer) { + return buffer->data; +} + + +void +_zip_buffer_free(zip_buffer_t *buffer) { + if (buffer == NULL) { + return; + } + + if (buffer->free_data) { + free(buffer->data); + } + + free(buffer); +} + + +bool +_zip_buffer_eof(zip_buffer_t *buffer) { + return buffer->ok && buffer->offset == buffer->size; +} + + +zip_uint8_t * +_zip_buffer_get(zip_buffer_t *buffer, zip_uint64_t length) { + zip_uint8_t *data; + + data = _zip_buffer_peek(buffer, length); + + if (data != NULL) { + buffer->offset += length; + } + + return data; +} + + +zip_uint16_t +_zip_buffer_get_16(zip_buffer_t *buffer) { + zip_uint8_t *data = _zip_buffer_get(buffer, 2); + + if (data == NULL) { + return 0; + } + + return (zip_uint16_t)(data[0] + (data[1] << 8)); +} + + +zip_uint32_t +_zip_buffer_get_32(zip_buffer_t *buffer) { + zip_uint8_t *data = _zip_buffer_get(buffer, 4); + + if (data == NULL) { + return 0; + } + + return ((((((zip_uint32_t)data[3] << 8) + data[2]) << 8) + data[1]) << 8) + data[0]; +} + + +zip_uint64_t +_zip_buffer_get_64(zip_buffer_t *buffer) { + zip_uint8_t *data = _zip_buffer_get(buffer, 8); + + if (data == NULL) { + return 0; + } + + return ((zip_uint64_t)data[7] << 56) + ((zip_uint64_t)data[6] << 48) + ((zip_uint64_t)data[5] << 40) + ((zip_uint64_t)data[4] << 32) + ((zip_uint64_t)data[3] << 24) + ((zip_uint64_t)data[2] << 16) + ((zip_uint64_t)data[1] << 8) + (zip_uint64_t)data[0]; +} + + +zip_uint8_t +_zip_buffer_get_8(zip_buffer_t *buffer) { + zip_uint8_t *data = _zip_buffer_get(buffer, 1); + + if (data == NULL) { + return 0; + } + + return data[0]; +} + + +zip_uint64_t +_zip_buffer_left(zip_buffer_t *buffer) { + return buffer->ok ? buffer->size - buffer->offset : 0; +} + + +zip_uint64_t +_zip_buffer_read(zip_buffer_t *buffer, zip_uint8_t *data, zip_uint64_t length) { + if (_zip_buffer_left(buffer) < length) { + length = _zip_buffer_left(buffer); + } + + memcpy(data, _zip_buffer_get(buffer, length), length); + + return length; +} + + +zip_buffer_t * +_zip_buffer_new(zip_uint8_t *data, zip_uint64_t size) { + bool free_data = (data == NULL); + zip_buffer_t *buffer; + + if (data == NULL) { + if ((data = (zip_uint8_t *)malloc(size)) == NULL) { + return NULL; + } + } + + if ((buffer = (zip_buffer_t *)malloc(sizeof(*buffer))) == NULL) { + if (free_data) { + free(data); + } + return NULL; + } + + buffer->ok = true; + buffer->data = data; + buffer->size = size; + buffer->offset = 0; + buffer->free_data = free_data; + + return buffer; +} + + +zip_buffer_t * +_zip_buffer_new_from_source(zip_source_t *src, zip_uint64_t size, zip_uint8_t *buf, zip_error_t *error) { + zip_buffer_t *buffer; + + if ((buffer = _zip_buffer_new(buf, size)) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + if (_zip_read(src, buffer->data, size, error) < 0) { + _zip_buffer_free(buffer); + return NULL; + } + + return buffer; +} + + +zip_uint64_t +_zip_buffer_offset(zip_buffer_t *buffer) { + return buffer->ok ? buffer->offset : 0; +} + + +bool +_zip_buffer_ok(zip_buffer_t *buffer) { + return buffer->ok; +} + + +zip_uint8_t * +_zip_buffer_peek(zip_buffer_t *buffer, zip_uint64_t length) { + zip_uint8_t *data; + + if (!buffer->ok || buffer->offset + length < length || buffer->offset + length > buffer->size) { + buffer->ok = false; + return NULL; + } + + data = buffer->data + buffer->offset; + return data; +} + +int +_zip_buffer_put(zip_buffer_t *buffer, const void *src, size_t length) { + zip_uint8_t *dst = _zip_buffer_get(buffer, length); + + if (dst == NULL) { + return -1; + } + + memcpy(dst, src, length); + return 0; +} + + +int +_zip_buffer_put_16(zip_buffer_t *buffer, zip_uint16_t i) { + zip_uint8_t *data = _zip_buffer_get(buffer, 2); + + if (data == NULL) { + return -1; + } + + data[0] = (zip_uint8_t)(i & 0xff); + data[1] = (zip_uint8_t)((i >> 8) & 0xff); + + return 0; +} + + +int +_zip_buffer_put_32(zip_buffer_t *buffer, zip_uint32_t i) { + zip_uint8_t *data = _zip_buffer_get(buffer, 4); + + if (data == NULL) { + return -1; + } + + data[0] = (zip_uint8_t)(i & 0xff); + data[1] = (zip_uint8_t)((i >> 8) & 0xff); + data[2] = (zip_uint8_t)((i >> 16) & 0xff); + data[3] = (zip_uint8_t)((i >> 24) & 0xff); + + return 0; +} + + +int +_zip_buffer_put_64(zip_buffer_t *buffer, zip_uint64_t i) { + zip_uint8_t *data = _zip_buffer_get(buffer, 8); + + if (data == NULL) { + return -1; + } + + data[0] = (zip_uint8_t)(i & 0xff); + data[1] = (zip_uint8_t)((i >> 8) & 0xff); + data[2] = (zip_uint8_t)((i >> 16) & 0xff); + data[3] = (zip_uint8_t)((i >> 24) & 0xff); + data[4] = (zip_uint8_t)((i >> 32) & 0xff); + data[5] = (zip_uint8_t)((i >> 40) & 0xff); + data[6] = (zip_uint8_t)((i >> 48) & 0xff); + data[7] = (zip_uint8_t)((i >> 56) & 0xff); + + return 0; +} + + +int +_zip_buffer_put_8(zip_buffer_t *buffer, zip_uint8_t i) { + zip_uint8_t *data = _zip_buffer_get(buffer, 1); + + if (data == NULL) { + return -1; + } + + data[0] = i; + + return 0; +} + + +int +_zip_buffer_set_offset(zip_buffer_t *buffer, zip_uint64_t offset) { + if (offset > buffer->size) { + buffer->ok = false; + return -1; + } + + buffer->ok = true; + buffer->offset = offset; + + return 0; +} + + +int +_zip_buffer_skip(zip_buffer_t *buffer, zip_uint64_t length) { + zip_uint64_t offset = buffer->offset + length; + + if (offset < buffer->offset) { + buffer->ok = false; + return -1; + } + return _zip_buffer_set_offset(buffer, offset); +} + +zip_uint64_t +_zip_buffer_size(zip_buffer_t *buffer) { + return buffer->size; +} diff --git a/ext/libzip/zip_close.c b/ext/libzip/zip_close.c index f7f3b9f7f3..816fcefa3b 100644 --- a/ext/libzip/zip_close.c +++ b/ext/libzip/zip_close.c @@ -1,6 +1,6 @@ /* zip_close.c -- close zip archive and update changes - Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,708 +31,673 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef _MSC_VER -#pragma warning (disable:4996) -#pragma warning (disable:4244) -#else -#include -#endif - -#include -#include -#include -#include -#include -#include #include "zipint.h" -#ifdef HAVE_STRINGS_H -#include +#include +#include +#ifdef _WIN32 +#include +#include #endif -static int add_data(struct zip *, struct zip_source *, struct zip_dirent *, - FILE *); -static int add_data_comp(zip_source_callback, void *, struct zip_stat *, - FILE *, struct zip_error *); -static int add_data_uncomp(struct zip *, zip_source_callback, void *, - struct zip_stat *, FILE *); -static void ch_set_error(struct zip_error *, zip_source_callback, void *); -static int copy_data(FILE *, off_t, FILE *, struct zip_error *); -static int write_cdir(struct zip *, struct zip_cdir *, FILE *); -static int _zip_cdir_set_comment(struct zip_cdir *, struct zip *); -static int _zip_changed(struct zip *, int *); -#ifdef UNICODE -static wchar_t * -#else -static char * -#endif - _zip_create_temp_output(struct zip *, FILE **); -static int _zip_torrentzip_cmp(const void *, const void *); - - -struct filelist { - int idx; - const char *name; -}; - - +static int add_data(zip_t *, zip_source_t *, zip_dirent_t *, zip_uint32_t); +static int copy_data(zip_t *, zip_uint64_t); +static int copy_source(zip_t *, zip_source_t *, zip_int64_t); +static int write_cdir(zip_t *, const zip_filelist_t *, zip_uint64_t); +static int write_data_descriptor(zip_t *za, const zip_dirent_t *dirent, int is_zip64); ZIP_EXTERN int -zip_close(struct zip *za) -{ - int survivors; - int i, j, error; -#ifdef UNICODE - wchar_t *temp; -#else - char *temp; -#endif - FILE *out; - mode_t mask; - struct zip_cdir *cd; - struct zip_dirent de; - struct filelist *filelist; - int reopen_on_error; - int new_torrentzip; - - reopen_on_error = 0; +zip_close(zip_t *za) { + zip_uint64_t i, j, survivors, unchanged_offset; + zip_int64_t off; + int error; + zip_filelist_t *filelist; + int changed; if (za == NULL) return -1; - if (!_zip_changed(za, &survivors)) { - _zip_free(za); - return 0; - } + changed = _zip_changed(za, &survivors); /* don't create zip files with no entries */ if (survivors == 0) { - if (za->zn && za->zp) { -#ifdef UNICODE - if (_wremove(za->zn) != 0) { -#else - if (remove(za->zn) != 0) { -#endif - _zip_error_set(&za->error, ZIP_ER_REMOVE, errno); - return -1; + if ((za->open_flags & ZIP_TRUNCATE) || changed) { + if (zip_source_remove(za->src) < 0) { + if (!((zip_error_code_zip(zip_source_error(za->src)) == ZIP_ER_REMOVE) && (zip_error_code_system(zip_source_error(za->src)) == ENOENT))) { + _zip_error_set_from_source(&za->error, za->src); + return -1; + } } } - _zip_free(za); + zip_discard(za); return 0; - } + } - if ((filelist=(struct filelist *)malloc(sizeof(filelist[0])*survivors)) - == NULL) - return -1; + if (!changed) { + zip_discard(za); + return 0; + } - if ((cd=_zip_cdir_new(survivors, &za->error)) == NULL) { - free(filelist); + if (survivors > za->nentry) { + zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); return -1; } - for (i=0; ientry[i]); - - /* archive comment is special for torrentzip */ - if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) { - cd->comment = (char *)_zip_memdup(TORRENT_SIG "XXXXXXXX", - TORRENT_SIG_LEN + TORRENT_CRC_LEN, - &za->error); - if (cd->comment == NULL) { - _zip_cdir_free(cd); - free(filelist); - return -1; - } - cd->comment_len = TORRENT_SIG_LEN + TORRENT_CRC_LEN; - } - else if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, ZIP_FL_UNCHANGED) == 0) { - if (_zip_cdir_set_comment(cd, za) == -1) { - _zip_cdir_free(cd); - free(filelist); - return -1; - } - } - - if ((temp = _zip_create_temp_output(za, &out)) == NULL) { - _zip_cdir_free(cd); - free(filelist); + if ((filelist = (zip_filelist_t *)malloc(sizeof(filelist[0]) * (size_t)survivors)) == NULL) return -1; - } - + unchanged_offset = ZIP_UINT64_MAX; /* create list of files with index into original archive */ - for (i=j=0; inentry; i++) { - if (za->entry[i].state == ZIP_ST_DELETED) + for (i = j = 0; i < za->nentry; i++) { + if (za->entry[i].orig != NULL && ZIP_ENTRY_HAS_CHANGES(&za->entry[i])) { + unchanged_offset = ZIP_MIN(unchanged_offset, za->entry[i].orig->offset); + } + if (za->entry[i].deleted) { continue; + } + + if (j >= survivors) { + free(filelist); + zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); + return -1; + } filelist[j].idx = i; - filelist[j].name = zip_get_name(za, i, 0); j++; } - if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) - qsort(filelist, survivors, sizeof(filelist[0]), - _zip_torrentzip_cmp); + if (j < survivors) { + free(filelist); + zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); + return -1; + } - new_torrentzip = (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 1 - && zip_get_archive_flag(za, ZIP_AFL_TORRENT, - ZIP_FL_UNCHANGED) == 0); + if ((zip_source_supports(za->src) & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE_CLONING)) == 0) { + unchanged_offset = 0; + } + else { + if (unchanged_offset == ZIP_UINT64_MAX) { + /* we're keeping all file data, find the end of the last one */ + zip_uint64_t last_index = ZIP_UINT64_MAX; + unchanged_offset = 0; + + for (i = 0; i < za->nentry; i++) { + if (za->entry[i].orig != NULL) { + if (za->entry[i].orig->offset >= unchanged_offset) { + unchanged_offset = za->entry[i].orig->offset; + last_index = i; + } + } + } + if (last_index != ZIP_UINT64_MAX) { + if ((unchanged_offset = _zip_file_get_end(za, last_index, &za->error)) == 0) { + free(filelist); + return -1; + } + } + } + if (unchanged_offset > 0) { + if (zip_source_begin_write_cloning(za->src, unchanged_offset) < 0) { + /* cloning not supported, need to copy everything */ + unchanged_offset = 0; + } + } + } + if (unchanged_offset == 0) { + if (zip_source_begin_write(za->src) < 0) { + _zip_error_set_from_source(&za->error, za->src); + free(filelist); + return -1; + } + } + + if (_zip_progress_start(za->progress) != 0) { + zip_error_set(&za->error, ZIP_ER_CANCELLED, 0); + zip_source_rollback_write(za->src); + free(filelist); + return -1; + } error = 0; - for (j=0; jprogress, (double)j / (double)survivors, (double)(j + 1) / (double)survivors) != 0) { + zip_error_set(&za->error, ZIP_ER_CANCELLED, 0); + error = 1; + break; + } + i = filelist[j].idx; + entry = za->entry + i; + + if (entry->orig != NULL && entry->orig->offset < unchanged_offset) { + /* already implicitly copied by cloning */ + continue; + } + + new_data = (ZIP_ENTRY_DATA_CHANGED(entry) || ZIP_ENTRY_CHANGED(entry, ZIP_DIRENT_COMP_METHOD) || ZIP_ENTRY_CHANGED(entry, ZIP_DIRENT_ENCRYPTION_METHOD)); /* create new local directory entry */ - if (ZIP_ENTRY_DATA_CHANGED(za->entry+i) || new_torrentzip) { - _zip_dirent_init(&de); - - if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) - _zip_dirent_torrent_normalize(&de); - - /* use it as central directory entry */ - memcpy(cd->entry+j, &de, sizeof(cd->entry[j])); - - /* set/update file name */ - if (za->entry[i].ch_filename == NULL) { - if (za->entry[i].state == ZIP_ST_ADDED) { - de.filename = strdup("-"); - de.filename_len = 1; - cd->entry[j].filename = "-"; - cd->entry[j].filename_len = 1; - } - else { - de.filename = strdup(za->cdir->entry[i].filename); - de.filename_len = (unsigned short)strlen(de.filename); - cd->entry[j].filename = za->cdir->entry[i].filename; - cd->entry[j].filename_len = de.filename_len; - } - } - } - else { - /* copy existing directory entries */ - if (fseeko(za->zp, za->cdir->entry[i].offset, SEEK_SET) != 0) { - _zip_error_set(&za->error, ZIP_ER_SEEK, errno); + if (entry->changes == NULL) { + if ((entry->changes = _zip_dirent_clone(entry->orig)) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); error = 1; break; } - if (_zip_dirent_read(&de, za->zp, NULL, NULL, 1, - &za->error) != 0) { - error = 1; - break; - } - memcpy(cd->entry+j, za->cdir->entry+i, sizeof(cd->entry[j])); - if (de.bitflags & ZIP_GPBF_DATA_DESCRIPTOR) { - de.crc = za->cdir->entry[i].crc; - de.comp_size = za->cdir->entry[i].comp_size; - de.uncomp_size = za->cdir->entry[i].uncomp_size; - de.bitflags &= ~ZIP_GPBF_DATA_DESCRIPTOR; - cd->entry[j].bitflags &= ~ZIP_GPBF_DATA_DESCRIPTOR; - } + } + de = entry->changes; + + if (_zip_read_local_ef(za, i) < 0) { + error = 1; + break; } - if (za->entry[i].ch_filename) { - free(de.filename); - if ((de.filename=strdup(za->entry[i].ch_filename)) == NULL) { - error = 1; - break; - } - de.filename_len = (unsigned short)strlen(de.filename); - cd->entry[j].filename = za->entry[i].ch_filename; - cd->entry[j].filename_len = de.filename_len; + if ((off = zip_source_tell_write(za->src)) < 0) { + _zip_error_set_from_source(&za->error, za->src); + error = 1; + break; } + de->offset = (zip_uint64_t)off; - if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 0 - && za->entry[i].ch_comment_len != -1) { - /* as the rest of cd entries, its malloc/free is done by za */ - cd->entry[j].comment = za->entry[i].ch_comment; - cd->entry[j].comment_len = za->entry[i].ch_comment_len; - } - - cd->entry[j].offset = ftello(out); - - if (ZIP_ENTRY_DATA_CHANGED(za->entry+i) || new_torrentzip) { - struct zip_source *zs; + if (new_data) { + zip_source_t *zs; zs = NULL; - if (!ZIP_ENTRY_DATA_CHANGED(za->entry+i)) { - if ((zs=zip_source_zip(za, za, i, ZIP_FL_RECOMPRESS, 0, -1)) - == NULL) { + if (!ZIP_ENTRY_DATA_CHANGED(entry)) { + if ((zs = _zip_source_zip_new(za, za, i, ZIP_FL_UNCHANGED, 0, 0, NULL)) == NULL) { error = 1; break; } } - if (add_data(za, zs ? zs : za->entry[i].source, &de, out) < 0) { + /* add_data writes dirent */ + if (add_data(za, zs ? zs : entry->source, de, entry->changes ? entry->changes->changed : 0) < 0) { error = 1; + if (zs) + zip_source_free(zs); break; } - cd->entry[j].last_mod = de.last_mod; - cd->entry[j].comp_method = de.comp_method; - cd->entry[j].comp_size = de.comp_size; - cd->entry[j].uncomp_size = de.uncomp_size; - cd->entry[j].crc = de.crc; + if (zs) + zip_source_free(zs); } else { - if (_zip_dirent_write(&de, out, 1, &za->error) < 0) { + zip_uint64_t offset; + + if (de->encryption_method != ZIP_EM_TRAD_PKWARE) { + /* when copying data, all sizes are known -> no data descriptor needed */ + /* except for PKWare encryption, where removing the data descriptor breaks password validation */ + de->bitflags &= (zip_uint16_t)~ZIP_GPBF_DATA_DESCRIPTOR; + } + if (_zip_dirent_write(za, de, ZIP_FL_LOCAL) < 0) { error = 1; break; } - /* we just read the local dirent, file is at correct position */ - if (copy_data(za->zp, cd->entry[j].comp_size, out, - &za->error) < 0) { + if ((offset = _zip_file_get_offset(za, i, &za->error)) == 0) { error = 1; break; } + if (zip_source_seek(za->src, (zip_int64_t)offset, SEEK_SET) < 0) { + _zip_error_set_from_source(&za->error, za->src); + error = 1; + break; + } + if (copy_data(za, de->comp_size) < 0) { + error = 1; + break; + } + + if (de->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) { + if (write_data_descriptor(za, de, _zip_dirent_needs_zip64(de, 0)) < 0) { + error = 1; + break; + } + } } + } - _zip_dirent_finalize(&de); + if (!error) { + if (write_cdir(za, filelist, survivors) < 0) + error = 1; } free(filelist); if (!error) { - if (write_cdir(za, cd, out) < 0) + if (zip_source_commit_write(za->src) != 0) { + _zip_error_set_from_source(&za->error, za->src); error = 1; + } + _zip_progress_end(za->progress); } - - /* pointers in cd entries are owned by za */ - cd->nentry = 0; - _zip_cdir_free(cd); if (error) { - _zip_dirent_finalize(&de); - fclose(out); -#ifdef UNICODE - _wremove(temp); -#else - remove(temp); -#endif - free(temp); + zip_source_rollback_write(za->src); return -1; } - if (fclose(out) != 0) { - _zip_error_set(&za->error, ZIP_ER_CLOSE, errno); -#ifdef UNICODE - _wremove(temp); -#else - remove(temp); -#endif - free(temp); - return -1; - } - - if (za->zp) { - fclose(za->zp); - za->zp = NULL; - reopen_on_error = 1; - } - if (_zip_rename(temp, za->zn) != 0) { - _zip_error_set(&za->error, ZIP_ER_RENAME, errno); -#ifdef UNICODE - _wremove(temp); -#else - remove(temp); -#endif - free(temp); - if (reopen_on_error) { - /* ignore errors, since we're already in an error case */ -#ifdef UNICODE - za->zp = _wfopen(za->zn, L"rb"); -#else - za->zp = fopen(za->zn, "rb"); -#endif - } - return -1; - } - mask = umask(0); - umask(mask); - chmod(za->zn, 0666&~mask); + zip_discard(za); - _zip_free(za); - free(temp); - return 0; } - static int -add_data(struct zip *za, struct zip_source *zs, struct zip_dirent *de, FILE *ft) -{ - off_t offstart, offend; - zip_source_callback cb; - void *ud; - struct zip_stat st; - - cb = zs->f; - ud = zs->ud; +add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, zip_uint32_t changed) { + zip_int64_t offstart, offdata, offend, data_length; + zip_stat_t st; + zip_file_attributes_t attributes; + zip_source_t *src_final, *src_tmp; + int ret; + int is_zip64; + zip_flags_t flags; + bool needs_recompress, needs_decompress, needs_crc, needs_compress, needs_reencrypt, needs_decrypt, needs_encrypt; - if (cb(ud, &st, sizeof(st), ZIP_SOURCE_STAT) < (ssize_t)sizeof(st)) { - ch_set_error(&za->error, cb, ud); + if (zip_source_stat(src, &st) < 0) { + _zip_error_set_from_source(&za->error, src); return -1; } - if (cb(ud, NULL, 0, ZIP_SOURCE_OPEN) < 0) { - ch_set_error(&za->error, cb, ud); - return -1; + if ((st.valid & ZIP_STAT_COMP_METHOD) == 0) { + st.valid |= ZIP_STAT_COMP_METHOD; + st.comp_method = ZIP_CM_STORE; } - offstart = ftello(ft); - - if (_zip_dirent_write(de, ft, 1, &za->error) < 0) - return -1; - - if (st.comp_method != ZIP_CM_STORE) { - if (add_data_comp(cb, ud, &st, ft, &za->error) < 0) - return -1; + if (ZIP_CM_IS_DEFAULT(de->comp_method) && st.comp_method != ZIP_CM_STORE) + de->comp_method = st.comp_method; + else if (de->comp_method == ZIP_CM_STORE && (st.valid & ZIP_STAT_SIZE)) { + st.valid |= ZIP_STAT_COMP_SIZE; + st.comp_size = st.size; } else { - if (add_data_uncomp(za, cb, ud, &st, ft) < 0) + /* we'll recompress */ + st.valid &= ~ZIP_STAT_COMP_SIZE; + } + + if ((st.valid & ZIP_STAT_ENCRYPTION_METHOD) == 0) { + st.valid |= ZIP_STAT_ENCRYPTION_METHOD; + st.encryption_method = ZIP_EM_NONE; + } + + flags = ZIP_EF_LOCAL; + + if ((st.valid & ZIP_STAT_SIZE) == 0) { + flags |= ZIP_FL_FORCE_ZIP64; + data_length = -1; + } + else { + de->uncomp_size = st.size; + /* this is technically incorrect (copy_source counts compressed data), but it's the best we have */ + data_length = (zip_int64_t)st.size; + + if ((st.valid & ZIP_STAT_COMP_SIZE) == 0) { + zip_uint64_t max_size; + + switch (ZIP_CM_ACTUAL(de->comp_method)) { + case ZIP_CM_BZIP2: + /* computed by looking at increase of 10 random files of size 1MB when + * compressed with bzip2, rounded up: 1.006 */ + max_size = 4269351188u; + break; + + case ZIP_CM_DEFLATE: + /* max deflate size increase: size + ceil(size/16k)*5+6 */ + max_size = 4293656963u; + break; + + case ZIP_CM_STORE: + max_size = 0xffffffffu; + break; + + default: + max_size = 0; + } + + if (st.size > max_size) { + flags |= ZIP_FL_FORCE_ZIP64; + } + } + else + de->comp_size = st.comp_size; + } + + if ((offstart = zip_source_tell_write(za->src)) < 0) { + _zip_error_set_from_source(&za->error, za->src); + return -1; + } + + /* as long as we don't support non-seekable output, clear data descriptor bit */ + de->bitflags &= (zip_uint16_t)~ZIP_GPBF_DATA_DESCRIPTOR; + if ((is_zip64 = _zip_dirent_write(za, de, flags)) < 0) { + return -1; + } + + needs_recompress = st.comp_method != ZIP_CM_ACTUAL(de->comp_method); + needs_decompress = needs_recompress && (st.comp_method != ZIP_CM_STORE); + needs_crc = (st.comp_method == ZIP_CM_STORE) || needs_decompress; + needs_compress = needs_recompress && (de->comp_method != ZIP_CM_STORE); + + needs_reencrypt = needs_recompress || (de->changed & ZIP_DIRENT_PASSWORD) || (de->encryption_method != st.encryption_method); + needs_decrypt = needs_reencrypt && (st.encryption_method != ZIP_EM_NONE); + needs_encrypt = needs_reencrypt && (de->encryption_method != ZIP_EM_NONE); + + src_final = src; + zip_source_keep(src_final); + + if (needs_decrypt) { + zip_encryption_implementation impl; + + if ((impl = _zip_get_encryption_implementation(st.encryption_method, ZIP_CODEC_DECODE)) == NULL) { + zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0); + zip_source_free(src_final); return -1; + } + if ((src_tmp = impl(za, src_final, st.encryption_method, ZIP_CODEC_DECODE, za->default_password)) == NULL) { + /* error set by impl */ + zip_source_free(src_final); + return -1; + } + + zip_source_free(src_final); + src_final = src_tmp; } - if (cb(ud, NULL, 0, ZIP_SOURCE_CLOSE) < 0) { - ch_set_error(&za->error, cb, ud); + if (needs_decompress) { + if ((src_tmp = zip_source_decompress(za, src_final, st.comp_method)) == NULL) { + zip_source_free(src_final); + return -1; + } + + zip_source_free(src_final); + src_final = src_tmp; + } + + if (needs_crc) { + if ((src_tmp = zip_source_crc(za, src_final, 0)) == NULL) { + zip_source_free(src_final); + return -1; + } + + zip_source_free(src_final); + src_final = src_tmp; + } + + if (needs_compress) { + if ((src_tmp = zip_source_compress(za, src_final, de->comp_method, de->compression_level)) == NULL) { + zip_source_free(src_final); + return -1; + } + + zip_source_free(src_final); + src_final = src_tmp; + } + + + if (needs_encrypt) { + zip_encryption_implementation impl; + const char *password = NULL; + + if (de->password) { + password = de->password; + } + else if (za->default_password) { + password = za->default_password; + } + + if ((impl = _zip_get_encryption_implementation(de->encryption_method, ZIP_CODEC_ENCODE)) == NULL) { + zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0); + zip_source_free(src_final); + return -1; + } + if ((src_tmp = impl(za, src_final, de->encryption_method, ZIP_CODEC_ENCODE, password)) == NULL) { + /* error set by impl */ + zip_source_free(src_final); + return -1; + } + if (de->encryption_method == ZIP_EM_TRAD_PKWARE) { + de->bitflags |= ZIP_GPBF_DATA_DESCRIPTOR; + } + + zip_source_free(src_final); + src_final = src_tmp; + } + + + if ((offdata = zip_source_tell_write(za->src)) < 0) { + _zip_error_set_from_source(&za->error, za->src); return -1; } - offend = ftello(ft); + ret = copy_source(za, src_final, data_length); - if (fseeko(ft, offstart, SEEK_SET) < 0) { - _zip_error_set(&za->error, ZIP_ER_SEEK, errno); + if (zip_source_stat(src_final, &st) < 0) { + _zip_error_set_from_source(&za->error, src_final); + ret = -1; + } + + if (zip_source_get_file_attributes(src_final, &attributes) != 0) { + _zip_error_set_from_source(&za->error, src_final); + ret = -1; + } + + zip_source_free(src_final); + + if (ret < 0) { return -1; } - - de->last_mod = st.mtime; + if ((offend = zip_source_tell_write(za->src)) < 0) { + _zip_error_set_from_source(&za->error, za->src); + return -1; + } + + if (zip_source_seek_write(za->src, offstart, SEEK_SET) < 0) { + _zip_error_set_from_source(&za->error, za->src); + return -1; + } + + if ((st.valid & (ZIP_STAT_COMP_METHOD | ZIP_STAT_CRC | ZIP_STAT_SIZE)) != (ZIP_STAT_COMP_METHOD | ZIP_STAT_CRC | ZIP_STAT_SIZE)) { + zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); + return -1; + } + + if ((de->changed & ZIP_DIRENT_LAST_MOD) == 0) { + if (st.valid & ZIP_STAT_MTIME) + de->last_mod = st.mtime; + else + time(&de->last_mod); + } de->comp_method = st.comp_method; de->crc = st.crc; de->uncomp_size = st.size; - de->comp_size = st.comp_size; + de->comp_size = (zip_uint64_t)(offend - offdata); + _zip_dirent_apply_attributes(de, &attributes, (flags & ZIP_FL_FORCE_ZIP64) != 0, changed); - if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) - _zip_dirent_torrent_normalize(de); - - if (_zip_dirent_write(de, ft, 1, &za->error) < 0) + if ((ret = _zip_dirent_write(za, de, flags)) < 0) return -1; - - if (fseeko(ft, offend, SEEK_SET) < 0) { - _zip_error_set(&za->error, ZIP_ER_SEEK, errno); + + if (is_zip64 != ret) { + /* Zip64 mismatch between preliminary file header written before data and final file header written afterwards */ + zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); return -1; } - return 0; -} + if (zip_source_seek_write(za->src, offend, SEEK_SET) < 0) { + _zip_error_set_from_source(&za->error, za->src); + return -1; + } - - -static int -add_data_comp(zip_source_callback cb, void *ud, struct zip_stat *st,FILE *ft, - struct zip_error *error) -{ - char buf[BUFSIZE]; - ssize_t n; - - st->comp_size = 0; - while ((n=cb(ud, buf, sizeof(buf), ZIP_SOURCE_READ)) > 0) { - if (fwrite(buf, 1, n, ft) != (size_t)n) { - _zip_error_set(error, ZIP_ER_WRITE, errno); + if (de->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) { + if (write_data_descriptor(za, de, is_zip64) < 0) { return -1; } - - st->comp_size += n; } - if (n < 0) { - ch_set_error(error, cb, ud); + + return 0; +} + + +static int +copy_data(zip_t *za, zip_uint64_t len) { + DEFINE_BYTE_ARRAY(buf, BUFSIZE); + size_t n; + double total = (double)len; + + if (!byte_array_init(buf, BUFSIZE)) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); return -1; - } - - return 0; -} - - - -static int -add_data_uncomp(struct zip *za, zip_source_callback cb, void *ud, - struct zip_stat *st, FILE *ft) -{ - char b1[BUFSIZE], b2[BUFSIZE]; - int end, flush, ret; - ssize_t n; - size_t n2; - z_stream zstr; - int mem_level; - - st->comp_method = ZIP_CM_DEFLATE; - st->comp_size = st->size = 0; - st->crc = crc32(0, NULL, 0); - - zstr.zalloc = Z_NULL; - zstr.zfree = Z_NULL; - zstr.opaque = NULL; - zstr.avail_in = 0; - zstr.avail_out = 0; - - if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) - mem_level = TORRENT_MEM_LEVEL; - else - mem_level = MAX_MEM_LEVEL; - - /* -MAX_WBITS: undocumented feature of zlib to _not_ write a zlib header */ - deflateInit2(&zstr, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, mem_level, - Z_DEFAULT_STRATEGY); - - zstr.next_out = (Bytef *)b2; - zstr.avail_out = sizeof(b2); - zstr.next_in = NULL; - zstr.avail_in = 0; - - flush = 0; - end = 0; - while (!end) { - if (zstr.avail_in == 0 && !flush) { - if ((n=cb(ud, b1, sizeof(b1), ZIP_SOURCE_READ)) < 0) { - ch_set_error(&za->error, cb, ud); - deflateEnd(&zstr); - return -1; - } - if (n > 0) { - zstr.avail_in = n; - zstr.next_in = (Bytef *)b1; - st->size += n; - st->crc = crc32(st->crc, (Bytef *)b1, n); - } - else - flush = Z_FINISH; - } - - ret = deflate(&zstr, flush); - if (ret != Z_OK && ret != Z_STREAM_END) { - _zip_error_set(&za->error, ZIP_ER_ZLIB, ret); - return -1; - } - - if (zstr.avail_out != sizeof(b2)) { - n2 = sizeof(b2) - zstr.avail_out; - - if (fwrite(b2, 1, n2, ft) != n2) { - _zip_error_set(&za->error, ZIP_ER_WRITE, errno); - return -1; - } - - zstr.next_out = (Bytef *)b2; - zstr.avail_out = sizeof(b2); - st->comp_size += (off_t)n2; - } - - if (ret == Z_STREAM_END) { - deflateEnd(&zstr); - end = 1; - } } - return 0; -} - - - -static void -ch_set_error(struct zip_error *error, zip_source_callback cb, void *ud) -{ - int e[2]; - - if ((cb(ud, e, sizeof(e), ZIP_SOURCE_ERROR)) < (ssize_t)sizeof(e)) { - error->zip_err = ZIP_ER_INTERNAL; - error->sys_err = 0; - } - else { - error->zip_err = e[0]; - error->sys_err = e[1]; - } -} - - - -static int -copy_data(FILE *fs, off_t len, FILE *ft, struct zip_error *error) -{ - char buf[BUFSIZE]; - ssize_t n, nn; - - if (len == 0) - return 0; - while (len > 0) { - nn = len > sizeof(buf) ? sizeof(buf) : len; - if ((n=fread(buf, 1, nn, fs)) < 0) { - _zip_error_set(error, ZIP_ER_READ, errno); - return -1; - } - else if (n == 0) { - _zip_error_set(error, ZIP_ER_EOF, 0); + n = len > BUFSIZE ? BUFSIZE : len; + if (_zip_read(za->src, buf, n, &za->error) < 0) { + byte_array_fini(buf); return -1; } - if (fwrite(buf, 1, n, ft) != n) { - _zip_error_set(error, ZIP_ER_WRITE, errno); + if (_zip_write(za, buf, n) < 0) { + byte_array_fini(buf); + return -1; + } + + len -= n; + + if (_zip_progress_update(za->progress, (total - (double)len) / total) != 0) { + zip_error_set(&za->error, ZIP_ER_CANCELLED, 0); return -1; } - - len -= (off_t)n; } + byte_array_fini(buf); return 0; } - static int -write_cdir(struct zip *za, struct zip_cdir *cd, FILE *out) -{ - off_t offset; - uLong crc; - char buf[TORRENT_CRC_LEN+1]; - - if (_zip_cdir_write(cd, out, &za->error) < 0) - return -1; - - if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 0) - return 0; +copy_source(zip_t *za, zip_source_t *src, zip_int64_t data_length) { + DEFINE_BYTE_ARRAY(buf, BUFSIZE); + zip_int64_t n, current; + int ret; - - /* fix up torrentzip comment */ - - offset = ftello(out); - - if (_zip_filerange_crc(out, cd->offset, cd->size, &crc, &za->error) < 0) - return -1; - - snprintf(buf, sizeof(buf), "%08lX", (long)crc); - - if (fseeko(out, offset-TORRENT_CRC_LEN, SEEK_SET) < 0) { - _zip_error_set(&za->error, ZIP_ER_SEEK, errno); + if (zip_source_open(src) < 0) { + _zip_error_set_from_source(&za->error, src); return -1; } - if (fwrite(buf, TORRENT_CRC_LEN, 1, out) != 1) { - _zip_error_set(&za->error, ZIP_ER_WRITE, errno); + if (!byte_array_init(buf, BUFSIZE)) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + + ret = 0; + current = 0; + while ((n = zip_source_read(src, buf, BUFSIZE)) > 0) { + if (_zip_write(za, buf, (zip_uint64_t)n) < 0) { + ret = -1; + break; + } + if (n == BUFSIZE && za->progress && data_length > 0) { + current += n; + if (_zip_progress_update(za->progress, (double)current / (double)data_length) != 0) { + zip_error_set(&za->error, ZIP_ER_CANCELLED, 0); + ret = -1; + break; + } + } + } + + if (n < 0) { + _zip_error_set_from_source(&za->error, src); + ret = -1; + } + + byte_array_fini(buf); + + zip_source_close(src); + + return ret; +} + +static int +write_cdir(zip_t *za, const zip_filelist_t *filelist, zip_uint64_t survivors) { + zip_int64_t cd_start, end, size; + + if ((cd_start = zip_source_tell_write(za->src)) < 0) { + return -1; + } + + if ((size = _zip_cdir_write(za, filelist, survivors)) < 0) { + return -1; + } + + if ((end = zip_source_tell_write(za->src)) < 0) { return -1; } return 0; } - -static int -_zip_cdir_set_comment(struct zip_cdir *dest, struct zip *src) -{ - if (src->ch_comment_len != -1) { - dest->comment = (char *)_zip_memdup(src->ch_comment, - src->ch_comment_len, &src->error); - if (dest->comment == NULL) - return -1; - dest->comment_len = src->ch_comment_len; - } else { - if (src->cdir && src->cdir->comment) { - dest->comment = (char *)_zip_memdup(src->cdir->comment, - src->cdir->comment_len, &src->error); - if (dest->comment == NULL) - return -1; - dest->comment_len = src->cdir->comment_len; - } - } +int +_zip_changed(const zip_t *za, zip_uint64_t *survivorsp) { + int changed; + zip_uint64_t i, survivors; - return 0; -} + changed = 0; + survivors = 0; - - -static int -_zip_changed(struct zip *za, int *survivorsp) -{ - int changed, i, survivors; - - changed = survivors = 0; - - if (za->ch_comment_len != -1 - || za->ch_flags != za->flags) + if (za->comment_changed || za->ch_flags != za->flags) { changed = 1; - - for (i=0; inentry; i++) { - if ((za->entry[i].state != ZIP_ST_UNCHANGED) - || (za->entry[i].ch_comment_len != -1)) - changed = 1; - if (za->entry[i].state != ZIP_ST_DELETED) - survivors++; } - *survivorsp = survivors; + for (i = 0; i < za->nentry; i++) { + if (ZIP_ENTRY_HAS_CHANGES(&za->entry[i])) { + changed = 1; + } + if (!za->entry[i].deleted) { + survivors++; + } + } + + if (survivorsp) { + *survivorsp = survivors; + } return changed; } - -#ifdef UNICODE -static wchar_t * -#else -static char * -#endif -_zip_create_temp_output(struct zip *za, FILE **outp) -{ -#ifdef UNICODE - wchar_t *temp; -#else - char *temp; -#endif - int tfd; - FILE *tfp; - -#ifdef UNICODE - if ((temp=(wchar_t *)malloc((wcslen(za->zn)+8)*2)) == NULL) { -#else - if ((temp=(char *)malloc(strlen(za->zn)+8)) == NULL) { -#endif - _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); - return NULL; - } - -#ifdef UNICODE - swprintf(temp, 256, L"%s.XXXXXX", za->zn); -#else - sprintf(temp, "%s.XXXXXX", za->zn); -#endif - - if ((tfd=mkstemp(temp)) == -1) { - _zip_error_set(&za->error, ZIP_ER_TMPOPEN, errno); - free(temp); - return NULL; - } - - if ((tfp=fdopen(tfd, "r+b")) == NULL) { - _zip_error_set(&za->error, ZIP_ER_TMPOPEN, errno); -#ifndef _WIN32 - close(tfd); -#endif -#ifdef UNICODE - _wremove(temp); -#else - remove(temp); -#endif - free(temp); - return NULL; - } - - *outp = tfp; - return temp; -} - - - static int -_zip_torrentzip_cmp(const void *a, const void *b) -{ - return strcasecmp(((const struct filelist *)a)->name, - ((const struct filelist *)b)->name); +write_data_descriptor(zip_t *za, const zip_dirent_t *de, int is_zip64) { + zip_buffer_t *buffer = _zip_buffer_new(NULL, MAX_DATA_DESCRIPTOR_LENGTH); + int ret = 0; + + if (buffer == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + + _zip_buffer_put(buffer, DATADES_MAGIC, 4); + _zip_buffer_put_32(buffer, de->crc); + if (is_zip64) { + _zip_buffer_put_64(buffer, de->comp_size); + _zip_buffer_put_64(buffer, de->uncomp_size); + } + else { + _zip_buffer_put_32(buffer, (zip_uint32_t)de->comp_size); + _zip_buffer_put_32(buffer, (zip_uint32_t)de->uncomp_size); + } + + if (!_zip_buffer_ok(buffer)) { + zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); + ret = -1; + } + else { + ret = _zip_write(za, _zip_buffer_data(buffer), _zip_buffer_offset(buffer)); + } + + _zip_buffer_free(buffer); + + return ret; } diff --git a/ext/libzip/zip_delete.c b/ext/libzip/zip_delete.c index a0545e4b9e..97701a01d6 100644 --- a/ext/libzip/zip_delete.c +++ b/ext/libzip/zip_delete.c @@ -1,6 +1,6 @@ /* zip_delete.c -- delete file from zip archive - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,17 +31,29 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "zipint.h" - ZIP_EXTERN int -zip_delete(struct zip *za, int idx) -{ - if (idx < 0 || idx >= za->nentry) { - _zip_error_set(&za->error, ZIP_ER_INVAL, 0); +zip_delete(zip_t *za, zip_uint64_t idx) { + const char *name; + + if (idx >= za->nentry) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (ZIP_IS_RDONLY(za)) { + zip_error_set(&za->error, ZIP_ER_RDONLY, 0); + return -1; + } + + if ((name = _zip_get_name(za, idx, 0, &za->error)) == NULL) { + return -1; + } + + if (!_zip_hash_delete(za->names, (const zip_uint8_t *)name, &za->error)) { return -1; } @@ -50,9 +62,7 @@ zip_delete(struct zip *za, int idx) if (_zip_unchange(za, idx, 1) != 0) return -1; - za->entry[idx].state = ZIP_ST_DELETED; + za->entry[idx].deleted = 1; return 0; } - - diff --git a/ext/libzip/zip_dir_add.c b/ext/libzip/zip_dir_add.c new file mode 100644 index 0000000000..c40b1cb9af --- /dev/null +++ b/ext/libzip/zip_dir_add.c @@ -0,0 +1,92 @@ +/* + zip_dir_add.c -- add directory + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include +#include + +#include "zipint.h" + + +/* NOTE: Signed due to -1 on error. See zip_add.c for more details. */ + +ZIP_EXTERN zip_int64_t +zip_dir_add(zip_t *za, const char *name, zip_flags_t flags) { + size_t len; + zip_int64_t idx; + char *s; + zip_source_t *source; + + if (ZIP_IS_RDONLY(za)) { + zip_error_set(&za->error, ZIP_ER_RDONLY, 0); + return -1; + } + + if (name == NULL) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + s = NULL; + len = strlen(name); + + if (name[len - 1] != '/') { + if ((s = (char *)malloc(len + 2)) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + strcpy(s, name); + s[len] = '/'; + s[len + 1] = '\0'; + } + + if ((source = zip_source_buffer(za, NULL, 0, 0)) == NULL) { + free(s); + return -1; + } + + idx = _zip_file_replace(za, ZIP_UINT64_MAX, s ? s : name, source, flags); + + free(s); + + if (idx < 0) + zip_source_free(source); + else { + if (zip_file_set_external_attributes(za, (zip_uint64_t)idx, 0, ZIP_OPSYS_DEFAULT, ZIP_EXT_ATTRIB_DEFAULT_DIR) < 0) { + zip_delete(za, (zip_uint64_t)idx); + return -1; + } + } + + return idx; +} diff --git a/ext/libzip/zip_dirent.c b/ext/libzip/zip_dirent.c index d0ae6ad1cb..1691071fed 100644 --- a/ext/libzip/zip_dirent.c +++ b/ext/libzip/zip_dirent.c @@ -1,6 +1,6 @@ /* zip_dirent.c -- read directory entry (local or central), clean dirent - Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,588 +31,1133 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef _MSC_VER -#pragma warning (disable:4996) -#pragma warning (disable:4244) -#endif #include #include #include -#include #include -#include +#include #include "zipint.h" -static time_t _zip_d2u_time(int, int); -static char *_zip_readfpstr(FILE *, unsigned int, int, struct zip_error *); -static char *_zip_readstr(unsigned char **, int, int, struct zip_error *); -static void _zip_u2d_time(time_t, unsigned short *, unsigned short *); -static void _zip_write2(unsigned short, FILE *); -static void _zip_write4(unsigned int, FILE *); +static zip_string_t *_zip_dirent_process_ef_utf_8(const zip_dirent_t *de, zip_uint16_t id, zip_string_t *str); +static zip_extra_field_t *_zip_ef_utf8(zip_uint16_t, zip_string_t *, zip_error_t *); +static bool _zip_dirent_process_winzip_aes(zip_dirent_t *de, zip_error_t *error); - void -_zip_cdir_free(struct zip_cdir *cd) -{ - int i; +_zip_cdir_free(zip_cdir_t *cd) { + zip_uint64_t i; if (!cd) return; - for (i=0; inentry; i++) - _zip_dirent_finalize(cd->entry+i); - free(cd->comment); + for (i = 0; i < cd->nentry; i++) + _zip_entry_finalize(cd->entry + i); free(cd->entry); + _zip_string_free(cd->comment); free(cd); } - -int -_zip_cdir_grow(struct zip_cdir *cd, int nentry, struct zip_error *error) -{ - struct zip_dirent *entry; +zip_cdir_t * +_zip_cdir_new(zip_uint64_t nentry, zip_error_t *error) { + zip_cdir_t *cd; - if (nentry < cd->nentry) { - _zip_error_set(error, ZIP_ER_INTERNAL, 0); - return -1; - } - - if ((entry=((struct zip_dirent *) - realloc(cd->entry, sizeof(*(cd->entry))*nentry))) == NULL) { - _zip_error_set(error, ZIP_ER_MEMORY, 0); - return -1; - } - - cd->nentry = nentry; - cd->entry = entry; - - return 0; -} - - - -struct zip_cdir * -_zip_cdir_new(int nentry, struct zip_error *error) -{ - struct zip_cdir *cd; - - if ((cd=(struct zip_cdir *)malloc(sizeof(*cd))) == NULL) { - _zip_error_set(error, ZIP_ER_MEMORY, 0); + if ((cd = (zip_cdir_t *)malloc(sizeof(*cd))) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); return NULL; } - if ((cd->entry=(struct zip_dirent *)malloc(sizeof(*(cd->entry))*nentry)) - == NULL) { - _zip_error_set(error, ZIP_ER_MEMORY, 0); - free(cd); - return NULL; - } - - /* entries must be initialized by caller */ - - cd->nentry = nentry; + cd->entry = NULL; + cd->nentry = cd->nentry_alloc = 0; cd->size = cd->offset = 0; cd->comment = NULL; - cd->comment_len = 0; + cd->is_zip64 = false; + + if (!_zip_cdir_grow(cd, nentry, error)) { + _zip_cdir_free(cd); + return NULL; + } return cd; } - -int -_zip_cdir_write(struct zip_cdir *cd, FILE *fp, struct zip_error *error) -{ - int i; +bool +_zip_cdir_grow(zip_cdir_t *cd, zip_uint64_t additional_entries, zip_error_t *error) { + zip_uint64_t i, new_alloc; + zip_entry_t *new_entry; - cd->offset = ftello(fp); - - for (i=0; inentry; i++) { - if (_zip_dirent_write(cd->entry+i, fp, 0, error) != 0) - return -1; + if (additional_entries == 0) { + return true; } - cd->size = ftello(fp) - cd->offset; - - /* clearerr(fp); */ - fwrite(EOCD_MAGIC, 1, 4, fp); - _zip_write4(0, fp); - _zip_write2((unsigned short)cd->nentry, fp); - _zip_write2((unsigned short)cd->nentry, fp); - _zip_write4(cd->size, fp); - _zip_write4(cd->offset, fp); - _zip_write2(cd->comment_len, fp); - fwrite(cd->comment, 1, cd->comment_len, fp); + new_alloc = cd->nentry_alloc + additional_entries; - if (ferror(fp)) { - _zip_error_set(error, ZIP_ER_WRITE, errno); + if (new_alloc < additional_entries || new_alloc > SIZE_MAX / sizeof(*(cd->entry))) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return false; + } + + if ((new_entry = (zip_entry_t *)realloc(cd->entry, sizeof(*(cd->entry)) * (size_t)new_alloc)) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return false; + } + + cd->entry = new_entry; + + for (i = cd->nentry; i < new_alloc; i++) { + _zip_entry_init(cd->entry + i); + } + + cd->nentry = cd->nentry_alloc = new_alloc; + + return true; +} + + +zip_int64_t +_zip_cdir_write(zip_t *za, const zip_filelist_t *filelist, zip_uint64_t survivors) { + zip_uint64_t offset, size; + zip_string_t *comment; + zip_uint8_t buf[EOCDLEN + EOCD64LEN + EOCD64LOCLEN]; + zip_buffer_t *buffer; + zip_int64_t off; + zip_uint64_t i; + bool is_zip64; + int ret; + + if ((off = zip_source_tell_write(za->src)) < 0) { + _zip_error_set_from_source(&za->error, za->src); + return -1; + } + offset = (zip_uint64_t)off; + + is_zip64 = false; + + for (i = 0; i < survivors; i++) { + zip_entry_t *entry = za->entry + filelist[i].idx; + + if ((ret = _zip_dirent_write(za, entry->changes ? entry->changes : entry->orig, ZIP_FL_CENTRAL)) < 0) + return -1; + if (ret) + is_zip64 = true; + } + + if ((off = zip_source_tell_write(za->src)) < 0) { + _zip_error_set_from_source(&za->error, za->src); + return -1; + } + size = (zip_uint64_t)off - offset; + + if (offset > ZIP_UINT32_MAX || survivors > ZIP_UINT16_MAX) + is_zip64 = true; + + + if ((buffer = _zip_buffer_new(buf, sizeof(buf))) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); return -1; } - return 0; + if (is_zip64) { + _zip_buffer_put(buffer, EOCD64_MAGIC, 4); + _zip_buffer_put_64(buffer, EOCD64LEN - 12); + _zip_buffer_put_16(buffer, 45); + _zip_buffer_put_16(buffer, 45); + _zip_buffer_put_32(buffer, 0); + _zip_buffer_put_32(buffer, 0); + _zip_buffer_put_64(buffer, survivors); + _zip_buffer_put_64(buffer, survivors); + _zip_buffer_put_64(buffer, size); + _zip_buffer_put_64(buffer, offset); + _zip_buffer_put(buffer, EOCD64LOC_MAGIC, 4); + _zip_buffer_put_32(buffer, 0); + _zip_buffer_put_64(buffer, offset + size); + _zip_buffer_put_32(buffer, 1); + } + + _zip_buffer_put(buffer, EOCD_MAGIC, 4); + _zip_buffer_put_32(buffer, 0); + _zip_buffer_put_16(buffer, (zip_uint16_t)(survivors >= ZIP_UINT16_MAX ? ZIP_UINT16_MAX : survivors)); + _zip_buffer_put_16(buffer, (zip_uint16_t)(survivors >= ZIP_UINT16_MAX ? ZIP_UINT16_MAX : survivors)); + _zip_buffer_put_32(buffer, size >= ZIP_UINT32_MAX ? ZIP_UINT32_MAX : (zip_uint32_t)size); + _zip_buffer_put_32(buffer, offset >= ZIP_UINT32_MAX ? ZIP_UINT32_MAX : (zip_uint32_t)offset); + + comment = za->comment_changed ? za->comment_changes : za->comment_orig; + + _zip_buffer_put_16(buffer, (zip_uint16_t)(comment ? comment->length : 0)); + + if (!_zip_buffer_ok(buffer)) { + zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); + _zip_buffer_free(buffer); + return -1; + } + + if (_zip_write(za, _zip_buffer_data(buffer), _zip_buffer_offset(buffer)) < 0) { + _zip_buffer_free(buffer); + return -1; + } + + _zip_buffer_free(buffer); + + if (comment) { + if (_zip_write(za, comment->raw, comment->length) < 0) { + return -1; + } + } + + return (zip_int64_t)size; } - -void -_zip_dirent_finalize(struct zip_dirent *zde) -{ - free(zde->filename); - zde->filename = NULL; - free(zde->extrafield); - zde->extrafield = NULL; - free(zde->comment); - zde->comment = NULL; +zip_dirent_t * +_zip_dirent_clone(const zip_dirent_t *sde) { + zip_dirent_t *tde; + + if ((tde = (zip_dirent_t *)malloc(sizeof(*tde))) == NULL) + return NULL; + + if (sde) + memcpy(tde, sde, sizeof(*sde)); + else + _zip_dirent_init(tde); + + tde->changed = 0; + tde->cloned = 1; + + return tde; } - void -_zip_dirent_init(struct zip_dirent *de) -{ - de->version_madeby = 0; - de->version_needed = 20; /* 2.0 */ +_zip_dirent_finalize(zip_dirent_t *zde) { + if (!zde->cloned || zde->changed & ZIP_DIRENT_FILENAME) { + _zip_string_free(zde->filename); + zde->filename = NULL; + } + if (!zde->cloned || zde->changed & ZIP_DIRENT_EXTRA_FIELD) { + _zip_ef_free(zde->extra_fields); + zde->extra_fields = NULL; + } + if (!zde->cloned || zde->changed & ZIP_DIRENT_COMMENT) { + _zip_string_free(zde->comment); + zde->comment = NULL; + } + if (!zde->cloned || zde->changed & ZIP_DIRENT_PASSWORD) { + if (zde->password) { + _zip_crypto_clear(zde->password, strlen(zde->password)); + } + free(zde->password); + zde->password = NULL; + } +} + + +void +_zip_dirent_free(zip_dirent_t *zde) { + if (zde == NULL) + return; + + _zip_dirent_finalize(zde); + free(zde); +} + + +void +_zip_dirent_init(zip_dirent_t *de) { + de->changed = 0; + de->local_extra_fields_read = 0; + de->cloned = 0; + + de->crc_valid = true; + de->version_madeby = 63 | (ZIP_OPSYS_DEFAULT << 8); + de->version_needed = 10; /* 1.0 */ de->bitflags = 0; - de->comp_method = 0; + de->comp_method = ZIP_CM_DEFAULT; de->last_mod = 0; de->crc = 0; de->comp_size = 0; de->uncomp_size = 0; de->filename = NULL; - de->filename_len = 0; - de->extrafield = NULL; - de->extrafield_len = 0; + de->extra_fields = NULL; de->comment = NULL; - de->comment_len = 0; de->disk_number = 0; de->int_attrib = 0; - de->ext_attrib = 0; + de->ext_attrib = ZIP_EXT_ATTRIB_DEFAULT; de->offset = 0; + de->compression_level = 0; + de->encryption_method = ZIP_EM_NONE; + de->password = NULL; +} + + +bool +_zip_dirent_needs_zip64(const zip_dirent_t *de, zip_flags_t flags) { + if (de->uncomp_size >= ZIP_UINT32_MAX || de->comp_size >= ZIP_UINT32_MAX || ((flags & ZIP_FL_CENTRAL) && de->offset >= ZIP_UINT32_MAX)) + return true; + + return false; +} + + +zip_dirent_t * +_zip_dirent_new(void) { + zip_dirent_t *de; + + if ((de = (zip_dirent_t *)malloc(sizeof(*de))) == NULL) + return NULL; + + _zip_dirent_init(de); + return de; } - /* _zip_dirent_read(zde, fp, bufp, left, localp, error): Fills the zip directory entry zde. - If bufp is non-NULL, data is taken from there and bufp is advanced - by the amount of data used; otherwise data is read from fp as needed. - - if leftp is non-NULL, no more bytes than specified by it are used, - and *leftp is reduced by the number of bytes used. + If buffer is non-NULL, data is taken from there; otherwise data is read from fp as needed. - If local != 0, it reads a local header instead of a central - directory entry. + If local is true, it reads a local header instead of a central directory entry. - Returns 0 if successful. On error, error is filled in and -1 is - returned. - - XXX: leftp and file position undefined on error. + Returns size of dirent read if successful. On error, error is filled in and -1 is returned. */ -int -_zip_dirent_read(struct zip_dirent *zde, FILE *fp, - unsigned char **bufp, unsigned int *leftp, int local, - struct zip_error *error) -{ - unsigned char buf[CDENTRYSIZE]; - unsigned char *cur; - unsigned short dostime, dosdate; - unsigned int size; +zip_int64_t +_zip_dirent_read(zip_dirent_t *zde, zip_source_t *src, zip_buffer_t *buffer, bool local, zip_error_t *error) { + zip_uint8_t buf[CDENTRYSIZE]; + zip_uint16_t dostime, dosdate; + zip_uint32_t size, variable_size; + zip_uint16_t filename_len, comment_len, ef_len; - if (local) - size = LENTRYSIZE; - else - size = CDENTRYSIZE; + bool from_buffer = (buffer != NULL); - if (leftp && (*leftp < size)) { - _zip_error_set(error, ZIP_ER_NOZIP, 0); - return -1; - } + size = local ? LENTRYSIZE : CDENTRYSIZE; - if (bufp) { - /* use data from buffer */ - cur = *bufp; + if (buffer) { + if (_zip_buffer_left(buffer) < size) { + zip_error_set(error, ZIP_ER_NOZIP, 0); + return -1; + } } else { - /* read entry from disk */ - if ((fread(buf, 1, size, fp)version_madeby = _zip_read2(&cur); + zde->version_madeby = _zip_buffer_get_16(buffer); else zde->version_madeby = 0; - zde->version_needed = _zip_read2(&cur); - zde->bitflags = _zip_read2(&cur); - zde->comp_method = _zip_read2(&cur); - + zde->version_needed = _zip_buffer_get_16(buffer); + zde->bitflags = _zip_buffer_get_16(buffer); + zde->comp_method = _zip_buffer_get_16(buffer); + /* convert to time_t */ - dostime = _zip_read2(&cur); - dosdate = _zip_read2(&cur); + dostime = _zip_buffer_get_16(buffer); + dosdate = _zip_buffer_get_16(buffer); zde->last_mod = _zip_d2u_time(dostime, dosdate); - - zde->crc = _zip_read4(&cur); - zde->comp_size = _zip_read4(&cur); - zde->uncomp_size = _zip_read4(&cur); - - zde->filename_len = _zip_read2(&cur); - zde->extrafield_len = _zip_read2(&cur); - + + zde->crc = _zip_buffer_get_32(buffer); + zde->comp_size = _zip_buffer_get_32(buffer); + zde->uncomp_size = _zip_buffer_get_32(buffer); + + filename_len = _zip_buffer_get_16(buffer); + ef_len = _zip_buffer_get_16(buffer); + if (local) { - zde->comment_len = 0; + comment_len = 0; zde->disk_number = 0; zde->int_attrib = 0; zde->ext_attrib = 0; zde->offset = 0; - } else { - zde->comment_len = _zip_read2(&cur); - zde->disk_number = _zip_read2(&cur); - zde->int_attrib = _zip_read2(&cur); - zde->ext_attrib = _zip_read4(&cur); - zde->offset = _zip_read4(&cur); + } + else { + comment_len = _zip_buffer_get_16(buffer); + zde->disk_number = _zip_buffer_get_16(buffer); + zde->int_attrib = _zip_buffer_get_16(buffer); + zde->ext_attrib = _zip_buffer_get_32(buffer); + zde->offset = _zip_buffer_get_32(buffer); } - zde->filename = NULL; - zde->extrafield = NULL; - zde->comment = NULL; - - size += zde->filename_len+zde->extrafield_len+zde->comment_len; - - if (leftp && (*leftp < size)) { - _zip_error_set(error, ZIP_ER_NOZIP, 0); + if (!_zip_buffer_ok(buffer)) { + zip_error_set(error, ZIP_ER_INTERNAL, 0); + if (!from_buffer) { + _zip_buffer_free(buffer); + } return -1; } - if (bufp) { - if (zde->filename_len) { - zde->filename = _zip_readstr(&cur, zde->filename_len, 1, error); - if (!zde->filename) - return -1; + if (zde->bitflags & ZIP_GPBF_ENCRYPTED) { + if (zde->bitflags & ZIP_GPBF_STRONG_ENCRYPTION) { + /* TODO */ + zde->encryption_method = ZIP_EM_UNKNOWN; } - - if (zde->extrafield_len) { - zde->extrafield = _zip_readstr(&cur, zde->extrafield_len, 0, - error); - if (!zde->extrafield) - return -1; - } - - if (zde->comment_len) { - zde->comment = _zip_readstr(&cur, zde->comment_len, 0, error); - if (!zde->comment) - return -1; + else { + zde->encryption_method = ZIP_EM_TRAD_PKWARE; } } else { - if (zde->filename_len) { - zde->filename = _zip_readfpstr(fp, zde->filename_len, 1, error); - if (!zde->filename) - return -1; - } + zde->encryption_method = ZIP_EM_NONE; + } - if (zde->extrafield_len) { - zde->extrafield = _zip_readfpstr(fp, zde->extrafield_len, 0, - error); - if (!zde->extrafield) - return -1; - } + zde->filename = NULL; + zde->extra_fields = NULL; + zde->comment = NULL; - if (zde->comment_len) { - zde->comment = _zip_readfpstr(fp, zde->comment_len, 0, error); - if (!zde->comment) - return -1; + variable_size = (zip_uint32_t)filename_len + (zip_uint32_t)ef_len + (zip_uint32_t)comment_len; + + if (from_buffer) { + if (_zip_buffer_left(buffer) < variable_size) { + zip_error_set(error, ZIP_ER_INCONS, 0); + return -1; + } + } + else { + _zip_buffer_free(buffer); + + if ((buffer = _zip_buffer_new_from_source(src, variable_size, NULL, error)) == NULL) { + return -1; } } - if (bufp) - *bufp = cur; - if (leftp) - *leftp -= size; + if (filename_len) { + zde->filename = _zip_read_string(buffer, src, filename_len, 1, error); + if (!zde->filename) { + if (zip_error_code_zip(error) == ZIP_ER_EOF) { + zip_error_set(error, ZIP_ER_INCONS, 0); + } + if (!from_buffer) { + _zip_buffer_free(buffer); + } + return -1; + } - return 0; -} - - - -/* _zip_dirent_torrent_normalize(de); - Set values suitable for torrentzip. -*/ - -void -_zip_dirent_torrent_normalize(struct zip_dirent *de) -{ - static struct tm torrenttime; - static time_t last_mod = 0; - - if (last_mod == 0) { -#ifdef HAVE_STRUCT_TM_TM_ZONE - time_t now; - struct tm *l; -#endif - - torrenttime.tm_sec = 0; - torrenttime.tm_min = 32; - torrenttime.tm_hour = 23; - torrenttime.tm_mday = 24; - torrenttime.tm_mon = 11; - torrenttime.tm_year = 96; - torrenttime.tm_wday = 0; - torrenttime.tm_yday = 0; - torrenttime.tm_isdst = 0; - -#ifdef HAVE_STRUCT_TM_TM_ZONE - time(&now); - l = localtime(&now); - torrenttime.tm_gmtoff = l->tm_gmtoff; - torrenttime.tm_zone = l->tm_zone; -#endif - - last_mod = mktime(&torrenttime); + if (zde->bitflags & ZIP_GPBF_ENCODING_UTF_8) { + if (_zip_guess_encoding(zde->filename, ZIP_ENCODING_UTF8_KNOWN) == ZIP_ENCODING_ERROR) { + zip_error_set(error, ZIP_ER_INCONS, 0); + if (!from_buffer) { + _zip_buffer_free(buffer); + } + return -1; + } + } } - - de->version_madeby = 0; - de->version_needed = 20; /* 2.0 */ - de->bitflags = 2; /* maximum compression */ - de->comp_method = ZIP_CM_DEFLATE; - de->last_mod = last_mod; - de->disk_number = 0; - de->int_attrib = 0; - de->ext_attrib = 0; - de->offset = 0; + if (ef_len) { + zip_uint8_t *ef = _zip_read_data(buffer, src, ef_len, 0, error); - free(de->extrafield); - de->extrafield = NULL; - de->extrafield_len = 0; - free(de->comment); - de->comment = NULL; - de->comment_len = 0; + if (ef == NULL) { + if (!from_buffer) { + _zip_buffer_free(buffer); + } + return -1; + } + if (!_zip_ef_parse(ef, ef_len, local ? ZIP_EF_LOCAL : ZIP_EF_CENTRAL, &zde->extra_fields, error)) { + free(ef); + if (!from_buffer) { + _zip_buffer_free(buffer); + } + return -1; + } + free(ef); + if (local) + zde->local_extra_fields_read = 1; + } + + if (comment_len) { + zde->comment = _zip_read_string(buffer, src, comment_len, 0, error); + if (!zde->comment) { + if (!from_buffer) { + _zip_buffer_free(buffer); + } + return -1; + } + if (zde->bitflags & ZIP_GPBF_ENCODING_UTF_8) { + if (_zip_guess_encoding(zde->comment, ZIP_ENCODING_UTF8_KNOWN) == ZIP_ENCODING_ERROR) { + zip_error_set(error, ZIP_ER_INCONS, 0); + if (!from_buffer) { + _zip_buffer_free(buffer); + } + return -1; + } + } + } + + zde->filename = _zip_dirent_process_ef_utf_8(zde, ZIP_EF_UTF_8_NAME, zde->filename); + zde->comment = _zip_dirent_process_ef_utf_8(zde, ZIP_EF_UTF_8_COMMENT, zde->comment); + + /* Zip64 */ + + if (zde->uncomp_size == ZIP_UINT32_MAX || zde->comp_size == ZIP_UINT32_MAX || zde->offset == ZIP_UINT32_MAX) { + zip_uint16_t got_len; + zip_buffer_t *ef_buffer; + const zip_uint8_t *ef = _zip_ef_get_by_id(zde->extra_fields, &got_len, ZIP_EF_ZIP64, 0, local ? ZIP_EF_LOCAL : ZIP_EF_CENTRAL, error); + /* TODO: if got_len == 0 && !ZIP64_EOCD: no error, 0xffffffff is valid value */ + if (ef == NULL) { + if (!from_buffer) { + _zip_buffer_free(buffer); + } + return -1; + } + + if ((ef_buffer = _zip_buffer_new((zip_uint8_t *)ef, got_len)) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + if (!from_buffer) { + _zip_buffer_free(buffer); + } + return -1; + } + + if (zde->uncomp_size == ZIP_UINT32_MAX) { + zde->uncomp_size = _zip_buffer_get_64(ef_buffer); + } + else if (local) { + /* From appnote.txt: This entry in the Local header MUST + include BOTH original and compressed file size fields. */ + (void)_zip_buffer_skip(ef_buffer, 8); /* error is caught by _zip_buffer_eof() call */ + } + if (zde->comp_size == ZIP_UINT32_MAX) { + zde->comp_size = _zip_buffer_get_64(ef_buffer); + } + if (!local) { + if (zde->offset == ZIP_UINT32_MAX) { + zde->offset = _zip_buffer_get_64(ef_buffer); + } + if (zde->disk_number == ZIP_UINT16_MAX) { + zde->disk_number = _zip_buffer_get_32(ef_buffer); + } + } + + if (!_zip_buffer_eof(ef_buffer)) { + /* accept additional fields if values match */ + bool ok = true; + switch (got_len) { + case 28: + _zip_buffer_set_offset(ef_buffer, 24); + if (zde->disk_number != _zip_buffer_get_32(ef_buffer)) { + ok = false; + } + /* fallthrough */ + case 24: + _zip_buffer_set_offset(ef_buffer, 0); + if ((zde->uncomp_size != _zip_buffer_get_64(ef_buffer)) || (zde->comp_size != _zip_buffer_get_64(ef_buffer)) || (zde->offset != _zip_buffer_get_64(ef_buffer))) { + ok = false; + } + break; + + default: + ok = false; + } + if (!ok) { + zip_error_set(error, ZIP_ER_INCONS, 0); + _zip_buffer_free(ef_buffer); + if (!from_buffer) { + _zip_buffer_free(buffer); + } + return -1; + } + } + _zip_buffer_free(ef_buffer); + } + + if (!_zip_buffer_ok(buffer)) { + zip_error_set(error, ZIP_ER_INTERNAL, 0); + if (!from_buffer) { + _zip_buffer_free(buffer); + } + return -1; + } + if (!from_buffer) { + _zip_buffer_free(buffer); + } + + /* zip_source_seek / zip_source_tell don't support values > ZIP_INT64_MAX */ + if (zde->offset > ZIP_INT64_MAX) { + zip_error_set(error, ZIP_ER_SEEK, EFBIG); + return -1; + } + + if (!_zip_dirent_process_winzip_aes(zde, error)) { + return -1; + } + + zde->extra_fields = _zip_ef_remove_internal(zde->extra_fields); + + return (zip_int64_t)size + (zip_int64_t)variable_size; } - -/* _zip_dirent_write(zde, fp, localp, error): - Writes zip directory entry zde to file fp. +static zip_string_t * +_zip_dirent_process_ef_utf_8(const zip_dirent_t *de, zip_uint16_t id, zip_string_t *str) { + zip_uint16_t ef_len; + zip_uint32_t ef_crc; + zip_buffer_t *buffer; - If localp != 0, it writes a local header instead of a central - directory entry. + const zip_uint8_t *ef = _zip_ef_get_by_id(de->extra_fields, &ef_len, id, 0, ZIP_EF_BOTH, NULL); - Returns 0 if successful. On error, error is filled in and -1 is + if (ef == NULL || ef_len < 5 || ef[0] != 1) { + return str; + } + + if ((buffer = _zip_buffer_new((zip_uint8_t *)ef, ef_len)) == NULL) { + return str; + } + + _zip_buffer_get_8(buffer); + ef_crc = _zip_buffer_get_32(buffer); + + if (_zip_string_crc32(str) == ef_crc) { + zip_uint16_t len = (zip_uint16_t)_zip_buffer_left(buffer); + zip_string_t *ef_str = _zip_string_new(_zip_buffer_get(buffer, len), len, ZIP_FL_ENC_UTF_8, NULL); + + if (ef_str != NULL) { + _zip_string_free(str); + str = ef_str; + } + } + + _zip_buffer_free(buffer); + + return str; +} + + +static bool +_zip_dirent_process_winzip_aes(zip_dirent_t *de, zip_error_t *error) { + zip_uint16_t ef_len; + zip_buffer_t *buffer; + const zip_uint8_t *ef; + bool crc_valid; + zip_uint16_t enc_method; + + + if (de->comp_method != ZIP_CM_WINZIP_AES) { + return true; + } + + ef = _zip_ef_get_by_id(de->extra_fields, &ef_len, ZIP_EF_WINZIP_AES, 0, ZIP_EF_BOTH, NULL); + + if (ef == NULL || ef_len < 7) { + zip_error_set(error, ZIP_ER_INCONS, 0); + return false; + } + + if ((buffer = _zip_buffer_new((zip_uint8_t *)ef, ef_len)) == NULL) { + zip_error_set(error, ZIP_ER_INTERNAL, 0); + return false; + } + + /* version */ + + crc_valid = true; + switch (_zip_buffer_get_16(buffer)) { + case 1: + break; + + case 2: + if (de->uncomp_size < 20 /* TODO: constant */) { + crc_valid = false; + } + break; + + default: + zip_error_set(error, ZIP_ER_ENCRNOTSUPP, 0); + _zip_buffer_free(buffer); + return false; + } + + /* vendor */ + if (memcmp(_zip_buffer_get(buffer, 2), "AE", 2) != 0) { + zip_error_set(error, ZIP_ER_ENCRNOTSUPP, 0); + _zip_buffer_free(buffer); + return false; + } + + /* mode */ + switch (_zip_buffer_get_8(buffer)) { + case 1: + enc_method = ZIP_EM_AES_128; + break; + case 2: + enc_method = ZIP_EM_AES_192; + break; + case 3: + enc_method = ZIP_EM_AES_256; + break; + default: + zip_error_set(error, ZIP_ER_ENCRNOTSUPP, 0); + _zip_buffer_free(buffer); + return false; + } + + if (ef_len != 7) { + zip_error_set(error, ZIP_ER_INCONS, 0); + _zip_buffer_free(buffer); + return false; + } + + de->crc_valid = crc_valid; + de->encryption_method = enc_method; + de->comp_method = _zip_buffer_get_16(buffer); + + _zip_buffer_free(buffer); + return true; +} + + +zip_int32_t +_zip_dirent_size(zip_source_t *src, zip_uint16_t flags, zip_error_t *error) { + zip_int32_t size; + bool local = (flags & ZIP_EF_LOCAL) != 0; + int i; + zip_uint8_t b[6]; + zip_buffer_t *buffer; + + size = local ? LENTRYSIZE : CDENTRYSIZE; + + if (zip_source_seek(src, local ? 26 : 28, SEEK_CUR) < 0) { + _zip_error_set_from_source(error, src); + return -1; + } + + if ((buffer = _zip_buffer_new_from_source(src, local ? 4 : 6, b, error)) == NULL) { + return -1; + } + + for (i = 0; i < (local ? 2 : 3); i++) { + size += _zip_buffer_get_16(buffer); + } + + if (!_zip_buffer_eof(buffer)) { + zip_error_set(error, ZIP_ER_INTERNAL, 0); + _zip_buffer_free(buffer); + return -1; + } + + _zip_buffer_free(buffer); + return size; +} + + +/* _zip_dirent_write + Writes zip directory entry. + + If flags & ZIP_EF_LOCAL, it writes a local header instead of a central + directory entry. If flags & ZIP_EF_FORCE_ZIP64, a ZIP64 extra field is written, even if not needed. + + Returns 0 if successful, 1 if successful and wrote ZIP64 extra field. On error, error is filled in and -1 is returned. */ int -_zip_dirent_write(struct zip_dirent *zde, FILE *fp, int localp, - struct zip_error *error) -{ - unsigned short dostime, dosdate; +_zip_dirent_write(zip_t *za, zip_dirent_t *de, zip_flags_t flags) { + zip_uint16_t dostime, dosdate; + zip_encoding_type_t com_enc, name_enc; + zip_extra_field_t *ef; + zip_extra_field_t *ef64; + zip_uint32_t ef_total_size; + bool is_zip64; + bool is_really_zip64; + bool is_winzip_aes; + zip_uint8_t buf[CDENTRYSIZE]; + zip_buffer_t *buffer; - fwrite(localp ? LOCAL_MAGIC : CENTRAL_MAGIC, 1, 4, fp); + ef = NULL; - if (!localp) - _zip_write2(zde->version_madeby, fp); - _zip_write2(zde->version_needed, fp); - _zip_write2(zde->bitflags, fp); - _zip_write2(zde->comp_method, fp); + name_enc = _zip_guess_encoding(de->filename, ZIP_ENCODING_UNKNOWN); + com_enc = _zip_guess_encoding(de->comment, ZIP_ENCODING_UNKNOWN); - _zip_u2d_time(zde->last_mod, &dostime, &dosdate); - _zip_write2(dostime, fp); - _zip_write2(dosdate, fp); - - _zip_write4(zde->crc, fp); - _zip_write4(zde->comp_size, fp); - _zip_write4(zde->uncomp_size, fp); - - _zip_write2(zde->filename_len, fp); - _zip_write2(zde->extrafield_len, fp); - - if (!localp) { - _zip_write2(zde->comment_len, fp); - _zip_write2(zde->disk_number, fp); - _zip_write2(zde->int_attrib, fp); - _zip_write4(zde->ext_attrib, fp); - _zip_write4(zde->offset, fp); + if ((name_enc == ZIP_ENCODING_UTF8_KNOWN && com_enc == ZIP_ENCODING_ASCII) || (name_enc == ZIP_ENCODING_ASCII && com_enc == ZIP_ENCODING_UTF8_KNOWN) || (name_enc == ZIP_ENCODING_UTF8_KNOWN && com_enc == ZIP_ENCODING_UTF8_KNOWN)) + de->bitflags |= ZIP_GPBF_ENCODING_UTF_8; + else { + de->bitflags &= (zip_uint16_t)~ZIP_GPBF_ENCODING_UTF_8; + if (name_enc == ZIP_ENCODING_UTF8_KNOWN) { + ef = _zip_ef_utf8(ZIP_EF_UTF_8_NAME, de->filename, &za->error); + if (ef == NULL) + return -1; + } + if ((flags & ZIP_FL_LOCAL) == 0 && com_enc == ZIP_ENCODING_UTF8_KNOWN) { + zip_extra_field_t *ef2 = _zip_ef_utf8(ZIP_EF_UTF_8_COMMENT, de->comment, &za->error); + if (ef2 == NULL) { + _zip_ef_free(ef); + return -1; + } + ef2->next = ef; + ef = ef2; + } } - if (zde->filename_len) - fwrite(zde->filename, 1, zde->filename_len, fp); - - if (zde->extrafield_len) - fwrite(zde->extrafield, 1, zde->extrafield_len, fp); - - if (!localp) { - if (zde->comment_len) - fwrite(zde->comment, 1, zde->comment_len, fp); + if (de->encryption_method == ZIP_EM_NONE) { + de->bitflags &= (zip_uint16_t)~ZIP_GPBF_ENCRYPTED; + } + else { + de->bitflags |= (zip_uint16_t)ZIP_GPBF_ENCRYPTED; } - if (ferror(fp)) { - _zip_error_set(error, ZIP_ER_WRITE, errno); + is_really_zip64 = _zip_dirent_needs_zip64(de, flags); + is_zip64 = (flags & (ZIP_FL_LOCAL | ZIP_FL_FORCE_ZIP64)) == (ZIP_FL_LOCAL | ZIP_FL_FORCE_ZIP64) || is_really_zip64; + is_winzip_aes = de->encryption_method == ZIP_EM_AES_128 || de->encryption_method == ZIP_EM_AES_192 || de->encryption_method == ZIP_EM_AES_256; + + if (is_zip64) { + zip_uint8_t ef_zip64[EFZIP64SIZE]; + zip_buffer_t *ef_buffer = _zip_buffer_new(ef_zip64, sizeof(ef_zip64)); + if (ef_buffer == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + _zip_ef_free(ef); + return -1; + } + + if (flags & ZIP_FL_LOCAL) { + if ((flags & ZIP_FL_FORCE_ZIP64) || de->comp_size > ZIP_UINT32_MAX || de->uncomp_size > ZIP_UINT32_MAX) { + _zip_buffer_put_64(ef_buffer, de->uncomp_size); + _zip_buffer_put_64(ef_buffer, de->comp_size); + } + } + else { + if ((flags & ZIP_FL_FORCE_ZIP64) || de->comp_size > ZIP_UINT32_MAX || de->uncomp_size > ZIP_UINT32_MAX || de->offset > ZIP_UINT32_MAX) { + if (de->uncomp_size >= ZIP_UINT32_MAX) { + _zip_buffer_put_64(ef_buffer, de->uncomp_size); + } + if (de->comp_size >= ZIP_UINT32_MAX) { + _zip_buffer_put_64(ef_buffer, de->comp_size); + } + if (de->offset >= ZIP_UINT32_MAX) { + _zip_buffer_put_64(ef_buffer, de->offset); + } + } + } + + if (!_zip_buffer_ok(ef_buffer)) { + zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); + _zip_buffer_free(ef_buffer); + _zip_ef_free(ef); + return -1; + } + + ef64 = _zip_ef_new(ZIP_EF_ZIP64, (zip_uint16_t)(_zip_buffer_offset(ef_buffer)), ef_zip64, ZIP_EF_BOTH); + _zip_buffer_free(ef_buffer); + ef64->next = ef; + ef = ef64; + } + + if (is_winzip_aes) { + zip_uint8_t data[EF_WINZIP_AES_SIZE]; + zip_buffer_t *ef_buffer = _zip_buffer_new(data, sizeof(data)); + zip_extra_field_t *ef_winzip; + + if (ef_buffer == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + _zip_ef_free(ef); + return -1; + } + + _zip_buffer_put_16(ef_buffer, 2); + _zip_buffer_put(ef_buffer, "AE", 2); + _zip_buffer_put_8(ef_buffer, (zip_uint8_t)(de->encryption_method & 0xff)); + _zip_buffer_put_16(ef_buffer, (zip_uint16_t)de->comp_method); + + if (!_zip_buffer_ok(ef_buffer)) { + zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); + _zip_buffer_free(ef_buffer); + _zip_ef_free(ef); + return -1; + } + + ef_winzip = _zip_ef_new(ZIP_EF_WINZIP_AES, EF_WINZIP_AES_SIZE, data, ZIP_EF_BOTH); + _zip_buffer_free(ef_buffer); + ef_winzip->next = ef; + ef = ef_winzip; + } + + if ((buffer = _zip_buffer_new(buf, sizeof(buf))) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + _zip_ef_free(ef); return -1; } - return 0; + _zip_buffer_put(buffer, (flags & ZIP_FL_LOCAL) ? LOCAL_MAGIC : CENTRAL_MAGIC, 4); + + if ((flags & ZIP_FL_LOCAL) == 0) { + _zip_buffer_put_16(buffer, de->version_madeby); + } + _zip_buffer_put_16(buffer, ZIP_MAX(is_really_zip64 ? 45 : 0, de->version_needed)); + _zip_buffer_put_16(buffer, de->bitflags); + if (is_winzip_aes) { + _zip_buffer_put_16(buffer, ZIP_CM_WINZIP_AES); + } + else { + _zip_buffer_put_16(buffer, (zip_uint16_t)de->comp_method); + } + + _zip_u2d_time(de->last_mod, &dostime, &dosdate); + _zip_buffer_put_16(buffer, dostime); + _zip_buffer_put_16(buffer, dosdate); + + if (is_winzip_aes && de->uncomp_size < 20) { + _zip_buffer_put_32(buffer, 0); + } + else { + _zip_buffer_put_32(buffer, de->crc); + } + + if (((flags & ZIP_FL_LOCAL) == ZIP_FL_LOCAL) && ((de->comp_size >= ZIP_UINT32_MAX) || (de->uncomp_size >= ZIP_UINT32_MAX))) { + /* In local headers, if a ZIP64 EF is written, it MUST contain + * both compressed and uncompressed sizes (even if one of the + * two is smaller than 0xFFFFFFFF); on the other hand, those + * may only appear when the corresponding standard entry is + * 0xFFFFFFFF. (appnote.txt 4.5.3) */ + _zip_buffer_put_32(buffer, ZIP_UINT32_MAX); + _zip_buffer_put_32(buffer, ZIP_UINT32_MAX); + } + else { + if (de->comp_size < ZIP_UINT32_MAX) { + _zip_buffer_put_32(buffer, (zip_uint32_t)de->comp_size); + } + else { + _zip_buffer_put_32(buffer, ZIP_UINT32_MAX); + } + if (de->uncomp_size < ZIP_UINT32_MAX) { + _zip_buffer_put_32(buffer, (zip_uint32_t)de->uncomp_size); + } + else { + _zip_buffer_put_32(buffer, ZIP_UINT32_MAX); + } + } + + _zip_buffer_put_16(buffer, _zip_string_length(de->filename)); + /* TODO: check for overflow */ + ef_total_size = (zip_uint32_t)_zip_ef_size(de->extra_fields, flags) + (zip_uint32_t)_zip_ef_size(ef, ZIP_EF_BOTH); + _zip_buffer_put_16(buffer, (zip_uint16_t)ef_total_size); + + if ((flags & ZIP_FL_LOCAL) == 0) { + _zip_buffer_put_16(buffer, _zip_string_length(de->comment)); + _zip_buffer_put_16(buffer, (zip_uint16_t)de->disk_number); + _zip_buffer_put_16(buffer, de->int_attrib); + _zip_buffer_put_32(buffer, de->ext_attrib); + if (de->offset < ZIP_UINT32_MAX) + _zip_buffer_put_32(buffer, (zip_uint32_t)de->offset); + else + _zip_buffer_put_32(buffer, ZIP_UINT32_MAX); + } + + if (!_zip_buffer_ok(buffer)) { + zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); + _zip_buffer_free(buffer); + _zip_ef_free(ef); + return -1; + } + + if (_zip_write(za, buf, _zip_buffer_offset(buffer)) < 0) { + _zip_buffer_free(buffer); + _zip_ef_free(ef); + return -1; + } + + _zip_buffer_free(buffer); + + if (de->filename) { + if (_zip_string_write(za, de->filename) < 0) { + _zip_ef_free(ef); + return -1; + } + } + + if (ef) { + if (_zip_ef_write(za, ef, ZIP_EF_BOTH) < 0) { + _zip_ef_free(ef); + return -1; + } + } + _zip_ef_free(ef); + if (de->extra_fields) { + if (_zip_ef_write(za, de->extra_fields, flags) < 0) { + return -1; + } + } + + if ((flags & ZIP_FL_LOCAL) == 0) { + if (de->comment) { + if (_zip_string_write(za, de->comment) < 0) { + return -1; + } + } + } + + + return is_zip64; } - -static time_t -_zip_d2u_time(int dtime, int ddate) -{ +time_t +_zip_d2u_time(zip_uint16_t dtime, zip_uint16_t ddate) { struct tm tm; memset(&tm, 0, sizeof(tm)); - + /* let mktime decide if DST is in effect */ tm.tm_isdst = -1; - - tm.tm_year = ((ddate>>9)&127) + 1980 - 1900; - tm.tm_mon = ((ddate>>5)&15) - 1; - tm.tm_mday = ddate&31; - tm.tm_hour = (dtime>>11)&31; - tm.tm_min = (dtime>>5)&63; - tm.tm_sec = (dtime<<1)&62; + tm.tm_year = ((ddate >> 9) & 127) + 1980 - 1900; + tm.tm_mon = ((ddate >> 5) & 15) - 1; + tm.tm_mday = ddate & 31; + + tm.tm_hour = (dtime >> 11) & 31; + tm.tm_min = (dtime >> 5) & 63; + tm.tm_sec = (dtime << 1) & 62; return mktime(&tm); } - -unsigned short -_zip_read2(unsigned char **a) -{ - unsigned short ret; +static zip_extra_field_t * +_zip_ef_utf8(zip_uint16_t id, zip_string_t *str, zip_error_t *error) { + const zip_uint8_t *raw; + zip_uint32_t len; + zip_buffer_t *buffer; + zip_extra_field_t *ef; - ret = (*a)[0]+((*a)[1]<<8); - *a += 2; - - return ret; -} - - - -unsigned int -_zip_read4(unsigned char **a) -{ - unsigned int ret; - - ret = ((((((*a)[3]<<8)+(*a)[2])<<8)+(*a)[1])<<8)+(*a)[0]; - *a += 4; - - return ret; -} - - - -static char * -_zip_readfpstr(FILE *fp, unsigned int len, int nulp, struct zip_error *error) -{ - char *r, *o; - - r = (char *)malloc(nulp ? len+1 : len); - if (!r) { - _zip_error_set(error, ZIP_ER_MEMORY, 0); + if ((raw = _zip_string_get(str, &len, ZIP_FL_ENC_RAW, NULL)) == NULL) { + /* error already set */ return NULL; } - if (fread(r, 1, len, fp) ZIP_UINT16_MAX) { + zip_error_set(error, ZIP_ER_INVAL, 0); /* TODO: better error code? */ return NULL; } - if (nulp) { - /* replace any in-string NUL characters with spaces */ - r[len] = 0; - for (o=r; o>8)&0xff, fp); +zip_dirent_t * +_zip_get_dirent(zip_t *za, zip_uint64_t idx, zip_flags_t flags, zip_error_t *error) { + if (error == NULL) + error = &za->error; + + if (idx >= za->nentry) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((flags & ZIP_FL_UNCHANGED) || za->entry[idx].changes == NULL) { + if (za->entry[idx].orig == NULL) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + if (za->entry[idx].deleted && (flags & ZIP_FL_UNCHANGED) == 0) { + zip_error_set(error, ZIP_ER_DELETED, 0); + return NULL; + } + return za->entry[idx].orig; + } + else + return za->entry[idx].changes; +} + + +void +_zip_u2d_time(time_t intime, zip_uint16_t *dtime, zip_uint16_t *ddate) { + struct tm *tpm; + +#ifdef HAVE_LOCALTIME_R + struct tm tm; + tpm = localtime_r(&intime, &tm); +#else + tpm = localtime(&intime); +#endif + if (tpm == NULL) { + /* if localtime() fails, return an arbitrary date (1980-01-01 00:00:00) */ + *ddate = (1 << 5) + 1; + *dtime = 0; + return; + } + if (tpm->tm_year < 80) { + tpm->tm_year = 80; + } + + *ddate = (zip_uint16_t)(((tpm->tm_year + 1900 - 1980) << 9) + ((tpm->tm_mon + 1) << 5) + tpm->tm_mday); + *dtime = (zip_uint16_t)(((tpm->tm_hour) << 11) + ((tpm->tm_min) << 5) + ((tpm->tm_sec) >> 1)); return; } - -static void -_zip_write4(unsigned int i, FILE *fp) -{ - putc(i&0xff, fp); - putc((i>>8)&0xff, fp); - putc((i>>16)&0xff, fp); - putc((i>>24)&0xff, fp); - - return; -} +void +_zip_dirent_apply_attributes(zip_dirent_t *de, zip_file_attributes_t *attributes, bool force_zip64, zip_uint32_t changed) { + zip_uint16_t length; - + if (attributes->valid & ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS) { + zip_uint16_t mask = attributes->general_purpose_bit_mask & ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK; + de->bitflags = (de->bitflags & ~mask) | (attributes->general_purpose_bit_flags & mask); + } + if (attributes->valid & ZIP_FILE_ATTRIBUTES_ASCII) { + de->int_attrib = (de->int_attrib & ~0x1) | (attributes->ascii ? 1 : 0); + } + /* manually set attributes are preferred over attributes provided by source */ + if ((changed & ZIP_DIRENT_ATTRIBUTES) == 0 && (attributes->valid & ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES)) { + de->ext_attrib = attributes->external_file_attributes; + } -static void -_zip_u2d_time(time_t time, unsigned short *dtime, unsigned short *ddate) -{ - struct tm *tm; + if (de->comp_method == ZIP_CM_LZMA) { + de->version_needed = 63; + } + else if (de->encryption_method == ZIP_EM_AES_128 || de->encryption_method == ZIP_EM_AES_192 || de->encryption_method == ZIP_EM_AES_256) { + de->version_needed = 51; + } + else if (de->comp_method == ZIP_CM_BZIP2) { + de->version_needed = 46; + } + else if (force_zip64 || _zip_dirent_needs_zip64(de, 0)) { + de->version_needed = 45; + } + else if (de->comp_method == ZIP_CM_DEFLATE || de->encryption_method == ZIP_EM_TRAD_PKWARE) { + de->version_needed = 20; + } + else if ((length = _zip_string_length(de->filename)) > 0 && de->filename->raw[length - 1] == '/') { + de->version_needed = 20; + } + else { + de->version_needed = 10; + } - tm = localtime(&time); - *ddate = ((tm->tm_year+1900-1980)<<9) + ((tm->tm_mon+1)<<5) - + tm->tm_mday; - *dtime = ((tm->tm_hour)<<11) + ((tm->tm_min)<<5) - + ((tm->tm_sec)>>1); + if (attributes->valid & ZIP_FILE_ATTRIBUTES_VERSION_NEEDED) { + de->version_needed = ZIP_MAX(de->version_needed, attributes->version_needed); + } - return; + de->version_madeby = 63 | (de->version_madeby & 0xff00); + if ((changed & ZIP_DIRENT_ATTRIBUTES) == 0 && (attributes->valid & ZIP_FILE_ATTRIBUTES_HOST_SYSTEM)) { + de->version_madeby = (de->version_madeby & 0xff) | (zip_uint16_t)(attributes->host_system << 8); + } } diff --git a/ext/libzip/zip_free.c b/ext/libzip/zip_discard.c similarity index 72% rename from ext/libzip/zip_free.c rename to ext/libzip/zip_discard.c index 76c3a9673f..12f0636724 100644 --- a/ext/libzip/zip_free.c +++ b/ext/libzip/zip_discard.c @@ -1,6 +1,6 @@ /* - zip_free.c -- free struct zip - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + zip_discard.c -- discard and free struct zip + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,50 +31,49 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include "zipint.h" - -/* _zip_free: +/* zip_discard: frees the space allocated to a zipfile struct, and closes the corresponding file. */ void -_zip_free(struct zip *za) -{ - int i; +zip_discard(zip_t *za) { + zip_uint64_t i; if (za == NULL) return; - if (za->zn) - free(za->zn); + if (za->src) { + zip_source_close(za->src); + zip_source_free(za->src); + } - if (za->zp) - fclose(za->zp); + free(za->default_password); + _zip_string_free(za->comment_orig); + _zip_string_free(za->comment_changes); - _zip_cdir_free(za->cdir); + _zip_hash_free(za->names); if (za->entry) { - for (i=0; inentry; i++) { - _zip_entry_free(za->entry+i); - } + for (i = 0; i < za->nentry; i++) + _zip_entry_finalize(za->entry + i); free(za->entry); } - for (i=0; infile; i++) { - if (za->file[i]->error.zip_err == ZIP_ER_OK) { - _zip_error_set(&za->file[i]->error, ZIP_ER_ZIPCLOSED, 0); - za->file[i]->za = NULL; - } + for (i = 0; i < za->nopen_source; i++) { + _zip_source_invalidate(za->open_source[i]); } + free(za->open_source); + + _zip_progress_free(za->progress); + + zip_error_fini(&za->error); - free(za->file); - free(za); return; diff --git a/ext/libzip/zip_entry.c b/ext/libzip/zip_entry.c new file mode 100644 index 0000000000..52ecaef916 --- /dev/null +++ b/ext/libzip/zip_entry.c @@ -0,0 +1,51 @@ +/* + zip_entry.c -- struct zip_entry helper functions + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + +void +_zip_entry_finalize(zip_entry_t *e) { + _zip_unchange_data(e); + _zip_dirent_free(e->orig); + _zip_dirent_free(e->changes); +} + + +void +_zip_entry_init(zip_entry_t *e) { + e->orig = NULL; + e->changes = NULL; + e->source = NULL; + e->deleted = 0; +} diff --git a/ext/libzip/zip_err_str.c b/ext/libzip/zip_err_str.c index 3fcdf1738a..c46258b4ac 100644 --- a/ext/libzip/zip_err_str.c +++ b/ext/libzip/zip_err_str.c @@ -1,12 +1,10 @@ /* - This file was generated automatically by ./make_zip_err_str.sh - from ./zip.h; make changes there. - */ + This file was generated automatically by CMake + from zip.h; make changes there. +*/ #include "zipint.h" - - const char * const _zip_err_str[] = { "No error", "Multi-disk zip archives not supported", @@ -25,13 +23,22 @@ const char * const _zip_err_str[] = { "Malloc failure", "Entry has been changed", "Compression method not supported", - "Premature EOF", + "Premature end of file", "Invalid argument", "Not a zip archive", "Internal error", "Zip archive inconsistent", "Can't remove file", "Entry has been deleted", + "Encryption method not supported", + "Read-only archive", + "No password provided", + "Wrong password provided", + "Operation not supported", + "Resource still in use", + "Tell error", + "Compressed data invalid", + "Operation cancelled", }; const int _zip_nerr_str = sizeof(_zip_err_str)/sizeof(_zip_err_str[0]); @@ -65,4 +72,13 @@ const int _zip_err_type[] = { N, S, N, + N, + N, + N, + N, + N, + N, + S, + N, + N, }; diff --git a/ext/libzip/zip_error.c b/ext/libzip/zip_error.c index aab7079456..a0f74f022d 100644 --- a/ext/libzip/zip_error.c +++ b/ext/libzip/zip_error.c @@ -1,6 +1,6 @@ /* - zip_error.c -- struct zip_error helper functions - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + zip_error.c -- zip_error_t helper functions + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,71 +31,120 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - - #include #include "zipint.h" - -void -_zip_error_clear(struct zip_error *err) -{ - err->zip_err = ZIP_ER_OK; - err->sys_err = 0; +ZIP_EXTERN int +zip_error_code_system(const zip_error_t *error) { + return error->sys_err; } - -void -_zip_error_copy(struct zip_error *dst, struct zip_error *src) -{ - dst->zip_err = src->zip_err; - dst->sys_err = src->sys_err; +ZIP_EXTERN int +zip_error_code_zip(const zip_error_t *error) { + return error->zip_err; } - -void -_zip_error_fini(struct zip_error *err) -{ +ZIP_EXTERN void +zip_error_fini(zip_error_t *err) { free(err->str); err->str = NULL; } - + +ZIP_EXTERN void +zip_error_init(zip_error_t *err) { + err->zip_err = ZIP_ER_OK; + err->sys_err = 0; + err->str = NULL; +} + +ZIP_EXTERN void +zip_error_init_with_code(zip_error_t *error, int ze) { + zip_error_init(error); + error->zip_err = ze; + switch (zip_error_system_type(error)) { + case ZIP_ET_SYS: + error->sys_err = errno; + break; + + default: + error->sys_err = 0; + break; + } +} + + +ZIP_EXTERN int +zip_error_system_type(const zip_error_t *error) { + if (error->zip_err < 0 || error->zip_err >= _zip_nerr_str) + return ZIP_ET_NONE; + + return _zip_err_type[error->zip_err]; +} + void -_zip_error_get(struct zip_error *err, int *zep, int *sep) -{ +_zip_error_clear(zip_error_t *err) { + if (err == NULL) + return; + + err->zip_err = ZIP_ER_OK; + err->sys_err = 0; +} + + +void +_zip_error_copy(zip_error_t *dst, const zip_error_t *src) { + if (dst == NULL) { + return; + } + + dst->zip_err = src->zip_err; + dst->sys_err = src->sys_err; +} + + +void +_zip_error_get(const zip_error_t *err, int *zep, int *sep) { if (zep) *zep = err->zip_err; if (sep) { - if (zip_error_get_sys_type(err->zip_err) != ZIP_ET_NONE) + if (zip_error_system_type(err) != ZIP_ET_NONE) *sep = err->sys_err; else *sep = 0; } } - void -_zip_error_init(struct zip_error *err) -{ - err->zip_err = ZIP_ER_OK; - err->sys_err = 0; - err->str = NULL; -} - - - -void -_zip_error_set(struct zip_error *err, int ze, int se) -{ +zip_error_set(zip_error_t *err, int ze, int se) { if (err) { err->zip_err = ze; err->sys_err = se; } } + + +void +_zip_error_set_from_source(zip_error_t *err, zip_source_t *src) { + _zip_error_copy(err, zip_source_error(src)); +} + + +zip_int64_t +zip_error_to_data(const zip_error_t *error, void *data, zip_uint64_t length) { + int *e = (int *)data; + + if (length < sizeof(int) * 2) { + return -1; + } + + e[0] = zip_error_code_zip(error); + e[1] = zip_error_code_system(error); + return sizeof(int) * 2; +} diff --git a/ext/libzip/zip_error_clear.c b/ext/libzip/zip_error_clear.c index 364383ebe9..eec1568ff2 100644 --- a/ext/libzip/zip_error_clear.c +++ b/ext/libzip/zip_error_clear.c @@ -1,6 +1,6 @@ /* zip_error_clear.c -- clear zip error - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,14 +31,14 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "zipint.h" - ZIP_EXTERN void -zip_error_clear(struct zip *za) -{ +zip_error_clear(zip_t *za) { + if (za == NULL) + return; + _zip_error_clear(&za->error); } diff --git a/ext/libzip/zip_error_get.c b/ext/libzip/zip_error_get.c index 6d1c958c17..9588b4c4a5 100644 --- a/ext/libzip/zip_error_get.c +++ b/ext/libzip/zip_error_get.c @@ -1,6 +1,6 @@ /* zip_error_get.c -- get zip error - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,14 +31,24 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - +#define _ZIP_COMPILING_DEPRECATED #include "zipint.h" - ZIP_EXTERN void -zip_error_get(struct zip *za, int *zep, int *sep) -{ +zip_error_get(zip_t *za, int *zep, int *sep) { _zip_error_get(&za->error, zep, sep); } + + +ZIP_EXTERN zip_error_t * +zip_get_error(zip_t *za) { + return &za->error; +} + + +ZIP_EXTERN zip_error_t * +zip_file_get_error(zip_file_t *f) { + return &f->error; +} diff --git a/ext/libzip/zip_error_get_sys_type.c b/ext/libzip/zip_error_get_sys_type.c index 6c6f380740..3dfe5c26d4 100644 --- a/ext/libzip/zip_error_get_sys_type.c +++ b/ext/libzip/zip_error_get_sys_type.c @@ -1,6 +1,6 @@ /* zip_error_get_sys_type.c -- return type of system error code - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,15 +31,12 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - - +#define _ZIP_COMPILING_DEPRECATED #include "zipint.h" - ZIP_EXTERN int -zip_error_get_sys_type(int ze) -{ +zip_error_get_sys_type(int ze) { if (ze < 0 || ze >= _zip_nerr_str) return 0; diff --git a/ext/libzip/zip_error_strerror.c b/ext/libzip/zip_error_strerror.c index d7f7d0ea9a..93b24eab17 100644 --- a/ext/libzip/zip_error_strerror.c +++ b/ext/libzip/zip_error_strerror.c @@ -1,6 +1,6 @@ /* zip_error_sterror.c -- get string representation of struct zip_error - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,27 +31,21 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef _MSC_VER -#pragma warning (disable:4996) -#pragma warning (disable:4244) -#endif -#include #include #include #include +#include #include "zipint.h" - -const char * -_zip_error_strerror(struct zip_error *err) -{ +ZIP_EXTERN const char * +zip_error_strerror(zip_error_t *err) { const char *zs, *ss; char buf[128], *s; - _zip_error_fini(err); + zip_error_fini(err); if (err->zip_err < 0 || err->zip_err >= _zip_nerr_str) { sprintf(buf, "Unknown error %d", err->zip_err); @@ -60,7 +54,7 @@ _zip_error_strerror(struct zip_error *err) } else { zs = _zip_err_str[err->zip_err]; - + switch (_zip_err_type[err->zip_err]) { case ZIP_ET_SYS: ss = strerror(err->sys_err); @@ -78,14 +72,10 @@ _zip_error_strerror(struct zip_error *err) if (ss == NULL) return zs; else { - if ((s=(char *)malloc(strlen(ss) - + (zs ? strlen(zs)+2 : 0) + 1)) == NULL) + if ((s = (char *)malloc(strlen(ss) + (zs ? strlen(zs) + 2 : 0) + 1)) == NULL) return _zip_err_str[ZIP_ER_MEMORY]; - - sprintf(s, "%s%s%s", - (zs ? zs : ""), - (zs ? ": " : ""), - ss); + + sprintf(s, "%s%s%s", (zs ? zs : ""), (zs ? ": " : ""), ss); err->str = s; return s; diff --git a/ext/libzip/zip_error_to_str.c b/ext/libzip/zip_error_to_str.c index 4eea045662..34a3331fb8 100644 --- a/ext/libzip/zip_error_to_str.c +++ b/ext/libzip/zip_error_to_str.c @@ -1,6 +1,6 @@ /* zip_error_to_str.c -- get string representation of zip error code - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,43 +31,36 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef _MSC_VER -#pragma warning (disable:4996) -#pragma warning (disable:4244) -#endif -#include #include -#include #include +#include +#define _ZIP_COMPILING_DEPRECATED #include "zipint.h" - ZIP_EXTERN int -zip_error_to_str(char *buf, size_t len, int ze, int se) -{ +zip_error_to_str(char *buf, zip_uint64_t len, int ze, int se) { const char *zs, *ss; if (ze < 0 || ze >= _zip_nerr_str) return snprintf(buf, len, "Unknown error %d", ze); zs = _zip_err_str[ze]; - + switch (_zip_err_type[ze]) { case ZIP_ET_SYS: ss = strerror(se); break; - + case ZIP_ET_ZLIB: ss = zError(se); break; - + default: ss = NULL; } - return snprintf(buf, len, "%s%s%s", - zs, (ss ? ": " : ""), (ss ? ss : "")); + return snprintf(buf, len, "%s%s%s", zs, (ss ? ": " : ""), (ss ? ss : "")); } diff --git a/ext/libzip/zip_extra_field.c b/ext/libzip/zip_extra_field.c new file mode 100644 index 0000000000..7114a2510f --- /dev/null +++ b/ext/libzip/zip_extra_field.c @@ -0,0 +1,426 @@ +/* + zip_extra_field.c -- manipulate extra fields + Copyright (C) 2012-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "zipint.h" + + +zip_extra_field_t * +_zip_ef_clone(const zip_extra_field_t *ef, zip_error_t *error) { + zip_extra_field_t *head, *prev, *def; + + head = prev = NULL; + + while (ef) { + if ((def = _zip_ef_new(ef->id, ef->size, ef->data, ef->flags)) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + _zip_ef_free(head); + return NULL; + } + + if (head == NULL) + head = def; + if (prev) + prev->next = def; + prev = def; + + ef = ef->next; + } + + return head; +} + + +zip_extra_field_t * +_zip_ef_delete_by_id(zip_extra_field_t *ef, zip_uint16_t id, zip_uint16_t id_idx, zip_flags_t flags) { + zip_extra_field_t *head, *prev; + int i; + + i = 0; + head = ef; + prev = NULL; + for (; ef; ef = (prev ? prev->next : head)) { + if ((ef->flags & flags & ZIP_EF_BOTH) && ((ef->id == id) || (id == ZIP_EXTRA_FIELD_ALL))) { + if (id_idx == ZIP_EXTRA_FIELD_ALL || i == id_idx) { + ef->flags &= ~(flags & ZIP_EF_BOTH); + if ((ef->flags & ZIP_EF_BOTH) == 0) { + if (prev) + prev->next = ef->next; + else + head = ef->next; + ef->next = NULL; + _zip_ef_free(ef); + + if (id_idx == ZIP_EXTRA_FIELD_ALL) + continue; + } + } + + i++; + if (i > id_idx) + break; + } + prev = ef; + } + + return head; +} + + +void +_zip_ef_free(zip_extra_field_t *ef) { + zip_extra_field_t *ef2; + + while (ef) { + ef2 = ef->next; + free(ef->data); + free(ef); + ef = ef2; + } +} + + +const zip_uint8_t * +_zip_ef_get_by_id(const zip_extra_field_t *ef, zip_uint16_t *lenp, zip_uint16_t id, zip_uint16_t id_idx, zip_flags_t flags, zip_error_t *error) { + static const zip_uint8_t empty[1] = {'\0'}; + + int i; + + i = 0; + for (; ef; ef = ef->next) { + if (ef->id == id && (ef->flags & flags & ZIP_EF_BOTH)) { + if (i < id_idx) { + i++; + continue; + } + + if (lenp) + *lenp = ef->size; + if (ef->size > 0) + return ef->data; + else + return empty; + } + } + + zip_error_set(error, ZIP_ER_NOENT, 0); + return NULL; +} + + +zip_extra_field_t * +_zip_ef_merge(zip_extra_field_t *to, zip_extra_field_t *from) { + zip_extra_field_t *ef2, *tt, *tail; + int duplicate; + + if (to == NULL) + return from; + + for (tail = to; tail->next; tail = tail->next) + ; + + for (; from; from = ef2) { + ef2 = from->next; + + duplicate = 0; + for (tt = to; tt; tt = tt->next) { + if (tt->id == from->id && tt->size == from->size && (tt->size == 0 || memcmp(tt->data, from->data, tt->size) == 0)) { + tt->flags |= (from->flags & ZIP_EF_BOTH); + duplicate = 1; + break; + } + } + + from->next = NULL; + if (duplicate) + _zip_ef_free(from); + else + tail = tail->next = from; + } + + return to; +} + + +zip_extra_field_t * +_zip_ef_new(zip_uint16_t id, zip_uint16_t size, const zip_uint8_t *data, zip_flags_t flags) { + zip_extra_field_t *ef; + + if ((ef = (zip_extra_field_t *)malloc(sizeof(*ef))) == NULL) + return NULL; + + ef->next = NULL; + ef->flags = flags; + ef->id = id; + ef->size = size; + if (size > 0) { + if ((ef->data = (zip_uint8_t *)_zip_memdup(data, size, NULL)) == NULL) { + free(ef); + return NULL; + } + } + else + ef->data = NULL; + + return ef; +} + + +bool +_zip_ef_parse(const zip_uint8_t *data, zip_uint16_t len, zip_flags_t flags, zip_extra_field_t **ef_head_p, zip_error_t *error) { + zip_buffer_t *buffer; + zip_extra_field_t *ef, *ef2, *ef_head; + + if ((buffer = _zip_buffer_new((zip_uint8_t *)data, len)) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return false; + } + + ef_head = ef = NULL; + + while (_zip_buffer_ok(buffer) && _zip_buffer_left(buffer) >= 4) { + zip_uint16_t fid, flen; + zip_uint8_t *ef_data; + + fid = _zip_buffer_get_16(buffer); + flen = _zip_buffer_get_16(buffer); + ef_data = _zip_buffer_get(buffer, flen); + + if (ef_data == NULL) { + zip_error_set(error, ZIP_ER_INCONS, 0); + _zip_buffer_free(buffer); + _zip_ef_free(ef_head); + return false; + } + + if ((ef2 = _zip_ef_new(fid, flen, ef_data, flags)) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + _zip_buffer_free(buffer); + _zip_ef_free(ef_head); + return false; + } + + if (ef_head) { + ef->next = ef2; + ef = ef2; + } + else + ef_head = ef = ef2; + } + + if (!_zip_buffer_eof(buffer)) { + /* Android APK files align stored file data with padding in extra fields; ignore. */ + /* see https://android.googlesource.com/platform/build/+/master/tools/zipalign/ZipAlign.cpp */ + size_t glen = _zip_buffer_left(buffer); + zip_uint8_t *garbage; + garbage = _zip_buffer_get(buffer, glen); + if (glen >= 4 || garbage == NULL || memcmp(garbage, "\0\0\0", glen) != 0) { + zip_error_set(error, ZIP_ER_INCONS, 0); + _zip_buffer_free(buffer); + _zip_ef_free(ef_head); + return false; + } + } + + _zip_buffer_free(buffer); + + if (ef_head_p) { + *ef_head_p = ef_head; + } + else { + _zip_ef_free(ef_head); + } + + return true; +} + + +zip_extra_field_t * +_zip_ef_remove_internal(zip_extra_field_t *ef) { + zip_extra_field_t *ef_head; + zip_extra_field_t *prev, *next; + + ef_head = ef; + prev = NULL; + + while (ef) { + if (ZIP_EF_IS_INTERNAL(ef->id)) { + next = ef->next; + if (ef_head == ef) + ef_head = next; + ef->next = NULL; + _zip_ef_free(ef); + if (prev) + prev->next = next; + ef = next; + } + else { + prev = ef; + ef = ef->next; + } + } + + return ef_head; +} + + +zip_uint16_t +_zip_ef_size(const zip_extra_field_t *ef, zip_flags_t flags) { + zip_uint16_t size; + + size = 0; + for (; ef; ef = ef->next) { + if (ef->flags & flags & ZIP_EF_BOTH) + size = (zip_uint16_t)(size + 4 + ef->size); + } + + return size; +} + + +int +_zip_ef_write(zip_t *za, const zip_extra_field_t *ef, zip_flags_t flags) { + zip_uint8_t b[4]; + zip_buffer_t *buffer = _zip_buffer_new(b, sizeof(b)); + + if (buffer == NULL) { + return -1; + } + + for (; ef; ef = ef->next) { + if (ef->flags & flags & ZIP_EF_BOTH) { + _zip_buffer_set_offset(buffer, 0); + _zip_buffer_put_16(buffer, ef->id); + _zip_buffer_put_16(buffer, ef->size); + if (!_zip_buffer_ok(buffer)) { + zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); + _zip_buffer_free(buffer); + return -1; + } + if (_zip_write(za, b, 4) < 0) { + _zip_buffer_free(buffer); + return -1; + } + if (ef->size > 0) { + if (_zip_write(za, ef->data, ef->size) < 0) { + _zip_buffer_free(buffer); + return -1; + } + } + } + } + + _zip_buffer_free(buffer); + return 0; +} + + +int +_zip_read_local_ef(zip_t *za, zip_uint64_t idx) { + zip_entry_t *e; + unsigned char b[4]; + zip_buffer_t *buffer; + zip_uint16_t fname_len, ef_len; + + if (idx >= za->nentry) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + e = za->entry + idx; + + if (e->orig == NULL || e->orig->local_extra_fields_read) + return 0; + + if (e->orig->offset + 26 > ZIP_INT64_MAX) { + zip_error_set(&za->error, ZIP_ER_SEEK, EFBIG); + return -1; + } + + if (zip_source_seek(za->src, (zip_int64_t)(e->orig->offset + 26), SEEK_SET) < 0) { + _zip_error_set_from_source(&za->error, za->src); + return -1; + } + + if ((buffer = _zip_buffer_new_from_source(za->src, sizeof(b), b, &za->error)) == NULL) { + return -1; + } + + fname_len = _zip_buffer_get_16(buffer); + ef_len = _zip_buffer_get_16(buffer); + + if (!_zip_buffer_eof(buffer)) { + _zip_buffer_free(buffer); + zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); + return -1; + } + + _zip_buffer_free(buffer); + + if (ef_len > 0) { + zip_extra_field_t *ef; + zip_uint8_t *ef_raw; + + if (zip_source_seek(za->src, fname_len, SEEK_CUR) < 0) { + zip_error_set(&za->error, ZIP_ER_SEEK, errno); + return -1; + } + + ef_raw = _zip_read_data(NULL, za->src, ef_len, 0, &za->error); + + if (ef_raw == NULL) + return -1; + + if (!_zip_ef_parse(ef_raw, ef_len, ZIP_EF_LOCAL, &ef, &za->error)) { + free(ef_raw); + return -1; + } + free(ef_raw); + + if (ef) { + ef = _zip_ef_remove_internal(ef); + e->orig->extra_fields = _zip_ef_merge(e->orig->extra_fields, ef); + } + } + + e->orig->local_extra_fields_read = 1; + + if (e->changes && e->changes->local_extra_fields_read == 0) { + e->changes->extra_fields = e->orig->extra_fields; + e->changes->local_extra_fields_read = 1; + } + + return 0; +} diff --git a/ext/libzip/zip_extra_field_api.c b/ext/libzip/zip_extra_field_api.c new file mode 100644 index 0000000000..605f95d0f1 --- /dev/null +++ b/ext/libzip/zip_extra_field_api.c @@ -0,0 +1,355 @@ +/* + zip_extra_field_api.c -- public extra fields API functions + Copyright (C) 2012-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + + +ZIP_EXTERN int +zip_file_extra_field_delete(zip_t *za, zip_uint64_t idx, zip_uint16_t ef_idx, zip_flags_t flags) { + zip_dirent_t *de; + + if ((flags & ZIP_EF_BOTH) == 0) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (((flags & ZIP_EF_BOTH) == ZIP_EF_BOTH) && (ef_idx != ZIP_EXTRA_FIELD_ALL)) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (_zip_get_dirent(za, idx, 0, NULL) == NULL) + return -1; + + if (ZIP_IS_RDONLY(za)) { + zip_error_set(&za->error, ZIP_ER_RDONLY, 0); + return -1; + } + + if (_zip_file_extra_field_prepare_for_change(za, idx) < 0) + return -1; + + de = za->entry[idx].changes; + + de->extra_fields = _zip_ef_delete_by_id(de->extra_fields, ZIP_EXTRA_FIELD_ALL, ef_idx, flags); + return 0; +} + + +ZIP_EXTERN int +zip_file_extra_field_delete_by_id(zip_t *za, zip_uint64_t idx, zip_uint16_t ef_id, zip_uint16_t ef_idx, zip_flags_t flags) { + zip_dirent_t *de; + + if ((flags & ZIP_EF_BOTH) == 0) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (((flags & ZIP_EF_BOTH) == ZIP_EF_BOTH) && (ef_idx != ZIP_EXTRA_FIELD_ALL)) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (_zip_get_dirent(za, idx, 0, NULL) == NULL) + return -1; + + if (ZIP_IS_RDONLY(za)) { + zip_error_set(&za->error, ZIP_ER_RDONLY, 0); + return -1; + } + + if (_zip_file_extra_field_prepare_for_change(za, idx) < 0) + return -1; + + de = za->entry[idx].changes; + + de->extra_fields = _zip_ef_delete_by_id(de->extra_fields, ef_id, ef_idx, flags); + return 0; +} + + +ZIP_EXTERN const zip_uint8_t * +zip_file_extra_field_get(zip_t *za, zip_uint64_t idx, zip_uint16_t ef_idx, zip_uint16_t *idp, zip_uint16_t *lenp, zip_flags_t flags) { + static const zip_uint8_t empty[1] = {'\0'}; + + zip_dirent_t *de; + zip_extra_field_t *ef; + int i; + + if ((flags & ZIP_EF_BOTH) == 0) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((de = _zip_get_dirent(za, idx, flags, &za->error)) == NULL) + return NULL; + + if (flags & ZIP_FL_LOCAL) + if (_zip_read_local_ef(za, idx) < 0) + return NULL; + + i = 0; + for (ef = de->extra_fields; ef; ef = ef->next) { + if (ef->flags & flags & ZIP_EF_BOTH) { + if (i < ef_idx) { + i++; + continue; + } + + if (idp) + *idp = ef->id; + if (lenp) + *lenp = ef->size; + if (ef->size > 0) + return ef->data; + else + return empty; + } + } + + zip_error_set(&za->error, ZIP_ER_NOENT, 0); + return NULL; +} + + +ZIP_EXTERN const zip_uint8_t * +zip_file_extra_field_get_by_id(zip_t *za, zip_uint64_t idx, zip_uint16_t ef_id, zip_uint16_t ef_idx, zip_uint16_t *lenp, zip_flags_t flags) { + zip_dirent_t *de; + + if ((flags & ZIP_EF_BOTH) == 0) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((de = _zip_get_dirent(za, idx, flags, &za->error)) == NULL) + return NULL; + + if (flags & ZIP_FL_LOCAL) + if (_zip_read_local_ef(za, idx) < 0) + return NULL; + + return _zip_ef_get_by_id(de->extra_fields, lenp, ef_id, ef_idx, flags, &za->error); +} + + +ZIP_EXTERN zip_int16_t +zip_file_extra_fields_count(zip_t *za, zip_uint64_t idx, zip_flags_t flags) { + zip_dirent_t *de; + zip_extra_field_t *ef; + zip_uint16_t n; + + if ((flags & ZIP_EF_BOTH) == 0) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if ((de = _zip_get_dirent(za, idx, flags, &za->error)) == NULL) + return -1; + + if (flags & ZIP_FL_LOCAL) + if (_zip_read_local_ef(za, idx) < 0) + return -1; + + n = 0; + for (ef = de->extra_fields; ef; ef = ef->next) + if (ef->flags & flags & ZIP_EF_BOTH) + n++; + + return (zip_int16_t)n; +} + + +ZIP_EXTERN zip_int16_t +zip_file_extra_fields_count_by_id(zip_t *za, zip_uint64_t idx, zip_uint16_t ef_id, zip_flags_t flags) { + zip_dirent_t *de; + zip_extra_field_t *ef; + zip_uint16_t n; + + if ((flags & ZIP_EF_BOTH) == 0) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if ((de = _zip_get_dirent(za, idx, flags, &za->error)) == NULL) + return -1; + + if (flags & ZIP_FL_LOCAL) + if (_zip_read_local_ef(za, idx) < 0) + return -1; + + n = 0; + for (ef = de->extra_fields; ef; ef = ef->next) + if (ef->id == ef_id && (ef->flags & flags & ZIP_EF_BOTH)) + n++; + + return (zip_int16_t)n; +} + + +ZIP_EXTERN int +zip_file_extra_field_set(zip_t *za, zip_uint64_t idx, zip_uint16_t ef_id, zip_uint16_t ef_idx, const zip_uint8_t *data, zip_uint16_t len, zip_flags_t flags) { + zip_dirent_t *de; + zip_uint16_t ls, cs; + zip_extra_field_t *ef, *ef_prev, *ef_new; + int i, found, new_len; + + if ((flags & ZIP_EF_BOTH) == 0) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (_zip_get_dirent(za, idx, 0, NULL) == NULL) + return -1; + + if (ZIP_IS_RDONLY(za)) { + zip_error_set(&za->error, ZIP_ER_RDONLY, 0); + return -1; + } + + if (ZIP_EF_IS_INTERNAL(ef_id)) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (_zip_file_extra_field_prepare_for_change(za, idx) < 0) + return -1; + + de = za->entry[idx].changes; + + ef = de->extra_fields; + ef_prev = NULL; + i = 0; + found = 0; + + for (; ef; ef = ef->next) { + if (ef->id == ef_id && (ef->flags & flags & ZIP_EF_BOTH)) { + if (i == ef_idx) { + found = 1; + break; + } + i++; + } + ef_prev = ef; + } + + if (i < ef_idx && ef_idx != ZIP_EXTRA_FIELD_NEW) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (flags & ZIP_EF_LOCAL) + ls = _zip_ef_size(de->extra_fields, ZIP_EF_LOCAL); + else + ls = 0; + if (flags & ZIP_EF_CENTRAL) + cs = _zip_ef_size(de->extra_fields, ZIP_EF_CENTRAL); + else + cs = 0; + + new_len = ls > cs ? ls : cs; + if (found) + new_len -= ef->size + 4; + new_len += len + 4; + + if (new_len > ZIP_UINT16_MAX) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if ((ef_new = _zip_ef_new(ef_id, len, data, flags)) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + + if (found) { + if ((ef->flags & ZIP_EF_BOTH) == (flags & ZIP_EF_BOTH)) { + ef_new->next = ef->next; + ef->next = NULL; + _zip_ef_free(ef); + if (ef_prev) + ef_prev->next = ef_new; + else + de->extra_fields = ef_new; + } + else { + ef->flags &= ~(flags & ZIP_EF_BOTH); + ef_new->next = ef->next; + ef->next = ef_new; + } + } + else if (ef_prev) { + ef_new->next = ef_prev->next; + ef_prev->next = ef_new; + } + else + de->extra_fields = ef_new; + + return 0; +} + + +int +_zip_file_extra_field_prepare_for_change(zip_t *za, zip_uint64_t idx) { + zip_entry_t *e; + + if (idx >= za->nentry) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + e = za->entry + idx; + + if (e->changes && (e->changes->changed & ZIP_DIRENT_EXTRA_FIELD)) + return 0; + + if (e->orig) { + if (_zip_read_local_ef(za, idx) < 0) + return -1; + } + + if (e->changes == NULL) { + if ((e->changes = _zip_dirent_clone(e->orig)) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + } + + if (e->orig && e->orig->extra_fields) { + if ((e->changes->extra_fields = _zip_ef_clone(e->orig->extra_fields, &za->error)) == NULL) + return -1; + } + e->changes->changed |= ZIP_DIRENT_EXTRA_FIELD; + + return 0; +} diff --git a/ext/libzip/zip_fclose.c b/ext/libzip/zip_fclose.c index 8709362d6c..6eb9dc910e 100644 --- a/ext/libzip/zip_fclose.c +++ b/ext/libzip/zip_fclose.c @@ -1,6 +1,6 @@ /* zip_fclose.c -- close file in zip archive - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,41 +31,24 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include "zipint.h" - ZIP_EXTERN int -zip_fclose(struct zip_file *zf) -{ - int i, ret; - - if (zf->zstr) - inflateEnd(zf->zstr); - free(zf->buffer); - free(zf->zstr); +zip_fclose(zip_file_t *zf) { + int ret; - for (i=0; iza->nfile; i++) { - if (zf->za->file[i] == zf) { - zf->za->file[i] = zf->za->file[zf->za->nfile-1]; - zf->za->nfile--; - break; - } - } + if (zf->src) + zip_source_free(zf->src); ret = 0; if (zf->error.zip_err) ret = zf->error.zip_err; - else if ((zf->flags & ZIP_ZF_CRC) && (zf->flags & ZIP_ZF_EOF)) { - /* if EOF, compare CRC */ - if (zf->crc_orig != zf->crc) - ret = ZIP_ER_CRC; - } + zip_error_fini(&zf->error); free(zf); return ret; } diff --git a/ext/libzip/zip_fdopen.c b/ext/libzip/zip_fdopen.c new file mode 100644 index 0000000000..5a732cd41a --- /dev/null +++ b/ext/libzip/zip_fdopen.c @@ -0,0 +1,86 @@ +/* + zip_fdopen.c -- open read-only archive from file descriptor + Copyright (C) 2009-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" +#ifdef HAVE_UNISTD_H +#include +#endif + + +ZIP_EXTERN zip_t * +zip_fdopen(int fd_orig, int _flags, int *zep) { + int fd; + FILE *fp; + zip_t *za; + zip_source_t *src; + struct zip_error error; + + if (_flags < 0 || (_flags & ~(ZIP_CHECKCONS | ZIP_RDONLY))) { + _zip_set_open_error(zep, NULL, ZIP_ER_INVAL); + return NULL; + } + + /* We dup() here to avoid messing with the passed in fd. + We could not restore it to the original state in case of error. */ + + if ((fd = dup(fd_orig)) < 0) { + _zip_set_open_error(zep, NULL, ZIP_ER_OPEN); + return NULL; + } + + if ((fp = fdopen(fd, "rb")) == NULL) { + close(fd); + _zip_set_open_error(zep, NULL, ZIP_ER_OPEN); + return NULL; + } + + zip_error_init(&error); + if ((src = zip_source_filep_create(fp, 0, -1, &error)) == NULL) { + fclose(fp); + _zip_set_open_error(zep, &error, 0); + zip_error_fini(&error); + return NULL; + } + + if ((za = zip_open_from_source(src, _flags, &error)) == NULL) { + zip_source_free(src); + _zip_set_open_error(zep, &error, 0); + zip_error_fini(&error); + return NULL; + } + + zip_error_fini(&error); + close(fd_orig); + return za; +} diff --git a/ext/libzip/zip_file_add.c b/ext/libzip/zip_file_add.c new file mode 100644 index 0000000000..c019d8dd7d --- /dev/null +++ b/ext/libzip/zip_file_add.c @@ -0,0 +1,52 @@ +/* + zip_file_add.c -- add file via callback function + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + +/* + NOTE: Return type is signed so we can return -1 on error. + The index can not be larger than ZIP_INT64_MAX since the size + of the central directory cannot be larger than + ZIP_UINT64_MAX, and each entry is larger than 2 bytes. +*/ + +ZIP_EXTERN zip_int64_t +zip_file_add(zip_t *za, const char *name, zip_source_t *source, zip_flags_t flags) { + if (name == NULL || source == NULL) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + return _zip_file_replace(za, ZIP_UINT64_MAX, name, source, flags); +} diff --git a/ext/libzip/zip_file_error_clear.c b/ext/libzip/zip_file_error_clear.c index ef6fa10e4d..47e5da63ab 100644 --- a/ext/libzip/zip_file_error_clear.c +++ b/ext/libzip/zip_file_error_clear.c @@ -1,6 +1,6 @@ /* zip_file_error_clear.c -- clear zip file error - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,14 +31,14 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "zipint.h" - ZIP_EXTERN void -zip_file_error_clear(struct zip_file *zf) -{ +zip_file_error_clear(zip_file_t *zf) { + if (zf == NULL) + return; + _zip_error_clear(&zf->error); } diff --git a/ext/libzip/zip_file_error_get.c b/ext/libzip/zip_file_error_get.c index f20e011fc7..1ed68df195 100644 --- a/ext/libzip/zip_file_error_get.c +++ b/ext/libzip/zip_file_error_get.c @@ -1,6 +1,6 @@ /* zip_file_error_get.c -- get zip file error - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,14 +31,11 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - - +#define _ZIP_COMPILING_DEPRECATED #include "zipint.h" - ZIP_EXTERN void -zip_file_error_get(struct zip_file *zf, int *zep, int *sep) -{ +zip_file_error_get(zip_file_t *zf, int *zep, int *sep) { _zip_error_get(&zf->error, zep, sep); } diff --git a/ext/libzip/zip_file_get_comment.c b/ext/libzip/zip_file_get_comment.c new file mode 100644 index 0000000000..8f68141aea --- /dev/null +++ b/ext/libzip/zip_file_get_comment.c @@ -0,0 +1,55 @@ +/* + zip_file_get_comment.c -- get file comment + Copyright (C) 2006-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + +/* lenp is 32 bit because converted comment can be longer than ZIP_UINT16_MAX */ + +ZIP_EXTERN const char * +zip_file_get_comment(zip_t *za, zip_uint64_t idx, zip_uint32_t *lenp, zip_flags_t flags) { + zip_dirent_t *de; + zip_uint32_t len; + const zip_uint8_t *str; + + if ((de = _zip_get_dirent(za, idx, flags, NULL)) == NULL) + return NULL; + + if ((str = _zip_string_get(de->comment, &len, flags, &za->error)) == NULL) + return NULL; + + if (lenp) + *lenp = len; + + return (const char *)str; +} diff --git a/ext/libzip/zip_file_get_external_attributes.c b/ext/libzip/zip_file_get_external_attributes.c new file mode 100644 index 0000000000..ebc20580cb --- /dev/null +++ b/ext/libzip/zip_file_get_external_attributes.c @@ -0,0 +1,50 @@ +/* + zip_file_get_external_attributes.c -- get opsys/external attributes + Copyright (C) 2013-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "zipint.h" + +int +zip_file_get_external_attributes(zip_t *za, zip_uint64_t idx, zip_flags_t flags, zip_uint8_t *opsys, zip_uint32_t *attributes) { + zip_dirent_t *de; + + if ((de = _zip_get_dirent(za, idx, flags, NULL)) == NULL) + return -1; + + if (opsys) + *opsys = (zip_uint8_t)((de->version_madeby >> 8) & 0xff); + + if (attributes) + *attributes = de->ext_attrib; + + return 0; +} diff --git a/ext/libzip/zip_file_get_offset.c b/ext/libzip/zip_file_get_offset.c index b96fd5e480..2cf4480db7 100644 --- a/ext/libzip/zip_file_get_offset.c +++ b/ext/libzip/zip_file_get_offset.c @@ -1,6 +1,6 @@ /* zip_file_get_offset.c -- get offset of file data in archive. - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,18 +31,12 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include -#include #include -#include -#include -#include #include "zipint.h" - /* _zip_file_get_offset(za, ze): Returns the offset of the file data for entry ze. @@ -50,25 +44,74 @@ On error, fills in za->error and returns 0. */ -unsigned int -_zip_file_get_offset(struct zip *za, int idx) -{ - struct zip_dirent de; - unsigned int offset; +zip_uint64_t +_zip_file_get_offset(const zip_t *za, zip_uint64_t idx, zip_error_t *error) { + zip_uint64_t offset; + zip_int32_t size; - offset = za->cdir->entry[idx].offset; - - if (fseeko(za->zp, offset, SEEK_SET) != 0) { - _zip_error_set(&za->error, ZIP_ER_SEEK, errno); + if (za->entry[idx].orig == NULL) { + zip_error_set(error, ZIP_ER_INTERNAL, 0); return 0; } - if (_zip_dirent_read(&de, za->zp, NULL, NULL, 1, &za->error) != 0) + offset = za->entry[idx].orig->offset; + + if (zip_source_seek(za->src, (zip_int64_t)offset, SEEK_SET) < 0) { + _zip_error_set_from_source(error, za->src); + return 0; + } + + /* TODO: cache? */ + if ((size = _zip_dirent_size(za->src, ZIP_EF_LOCAL, error)) < 0) return 0; - offset += LENTRYSIZE + de.filename_len + de.extrafield_len; + if (offset + (zip_uint32_t)size > ZIP_INT64_MAX) { + zip_error_set(error, ZIP_ER_SEEK, EFBIG); + return 0; + } - _zip_dirent_finalize(&de); + return offset + (zip_uint32_t)size; +} + +zip_uint64_t +_zip_file_get_end(const zip_t *za, zip_uint64_t index, zip_error_t *error) { + zip_uint64_t offset; + zip_dirent_t *entry; + + if ((offset = _zip_file_get_offset(za, index, error)) == 0) { + return 0; + } + + entry = za->entry[index].orig; + + if (offset + entry->comp_size < offset || offset + entry->comp_size > ZIP_INT64_MAX) { + zip_error_set(error, ZIP_ER_SEEK, EFBIG); + return 0; + } + offset += entry->comp_size; + + if (entry->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) { + zip_uint8_t buf[4]; + if (zip_source_seek(za->src, (zip_int64_t)offset, SEEK_SET) < 0) { + _zip_error_set_from_source(error, za->src); + return 0; + } + if (zip_source_read(za->src, buf, 4) != 4) { + _zip_error_set_from_source(error, za->src); + return 0; + } + if (memcmp(buf, DATADES_MAGIC, 4) == 0) { + offset += 4; + } + offset += 12; + if (_zip_dirent_needs_zip64(entry, 0)) { + offset += 8; + } + if (offset > ZIP_INT64_MAX) { + zip_error_set(error, ZIP_ER_SEEK, EFBIG); + return 0; + } + } return offset; } diff --git a/ext/libzip/zip_file_rename.c b/ext/libzip/zip_file_rename.c new file mode 100644 index 0000000000..7ca428c499 --- /dev/null +++ b/ext/libzip/zip_file_rename.c @@ -0,0 +1,67 @@ +/* + zip_file_rename.c -- rename file in zip archive + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include + +#include "zipint.h" + + +ZIP_EXTERN int +zip_file_rename(zip_t *za, zip_uint64_t idx, const char *name, zip_flags_t flags) { + const char *old_name; + int old_is_dir, new_is_dir; + + if (idx >= za->nentry || (name != NULL && strlen(name) > ZIP_UINT16_MAX)) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (ZIP_IS_RDONLY(za)) { + zip_error_set(&za->error, ZIP_ER_RDONLY, 0); + return -1; + } + + if ((old_name = zip_get_name(za, idx, 0)) == NULL) + return -1; + + new_is_dir = (name != NULL && name[strlen(name) - 1] == '/'); + old_is_dir = (old_name[strlen(old_name) - 1] == '/'); + + if (new_is_dir != old_is_dir) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + return _zip_set_name(za, idx, name, flags); +} diff --git a/ext/libzip/zip_file_replace.c b/ext/libzip/zip_file_replace.c new file mode 100644 index 0000000000..87401f4a46 --- /dev/null +++ b/ext/libzip/zip_file_replace.c @@ -0,0 +1,105 @@ +/* + zip_file_replace.c -- replace file via callback function + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + + +ZIP_EXTERN int +zip_file_replace(zip_t *za, zip_uint64_t idx, zip_source_t *source, zip_flags_t flags) { + if (idx >= za->nentry || source == NULL) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (_zip_file_replace(za, idx, NULL, source, flags) == -1) + return -1; + + return 0; +} + + +/* NOTE: Signed due to -1 on error. See zip_add.c for more details. */ + +zip_int64_t +_zip_file_replace(zip_t *za, zip_uint64_t idx, const char *name, zip_source_t *source, zip_flags_t flags) { + zip_uint64_t za_nentry_prev; + + if (ZIP_IS_RDONLY(za)) { + zip_error_set(&za->error, ZIP_ER_RDONLY, 0); + return -1; + } + + za_nentry_prev = za->nentry; + if (idx == ZIP_UINT64_MAX) { + zip_int64_t i = -1; + + if (flags & ZIP_FL_OVERWRITE) + i = _zip_name_locate(za, name, flags, NULL); + + if (i == -1) { + /* create and use new entry, used by zip_add */ + if ((i = _zip_add_entry(za)) < 0) + return -1; + } + idx = (zip_uint64_t)i; + } + + if (name && _zip_set_name(za, idx, name, flags) != 0) { + if (za->nentry != za_nentry_prev) { + _zip_entry_finalize(za->entry + idx); + za->nentry = za_nentry_prev; + } + return -1; + } + + /* does not change any name related data, so we can do it here; + * needed for a double add of the same file name */ + _zip_unchange_data(za->entry + idx); + + if (za->entry[idx].orig != NULL && (za->entry[idx].changes == NULL || (za->entry[idx].changes->changed & ZIP_DIRENT_COMP_METHOD) == 0)) { + if (za->entry[idx].changes == NULL) { + if ((za->entry[idx].changes = _zip_dirent_clone(za->entry[idx].orig)) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + } + + za->entry[idx].changes->comp_method = ZIP_CM_REPLACED_DEFAULT; + za->entry[idx].changes->changed |= ZIP_DIRENT_COMP_METHOD; + } + + za->entry[idx].source = source; + + return (zip_int64_t)idx; +} diff --git a/ext/libzip/zip_file_set_comment.c b/ext/libzip/zip_file_set_comment.c new file mode 100644 index 0000000000..c309db8b1e --- /dev/null +++ b/ext/libzip/zip_file_set_comment.c @@ -0,0 +1,101 @@ +/* + zip_file_set_comment.c -- set comment for file in archive + Copyright (C) 2006-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include + +#include "zipint.h" + + +ZIP_EXTERN int +zip_file_set_comment(zip_t *za, zip_uint64_t idx, const char *comment, zip_uint16_t len, zip_flags_t flags) { + zip_entry_t *e; + zip_string_t *cstr; + int changed; + + if (_zip_get_dirent(za, idx, 0, NULL) == NULL) + return -1; + + if (ZIP_IS_RDONLY(za)) { + zip_error_set(&za->error, ZIP_ER_RDONLY, 0); + return -1; + } + + if (len > 0 && comment == NULL) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (len > 0) { + if ((cstr = _zip_string_new((const zip_uint8_t *)comment, len, flags, &za->error)) == NULL) + return -1; + if ((flags & ZIP_FL_ENCODING_ALL) == ZIP_FL_ENC_GUESS && _zip_guess_encoding(cstr, ZIP_ENCODING_UNKNOWN) == ZIP_ENCODING_UTF8_GUESSED) + cstr->encoding = ZIP_ENCODING_UTF8_KNOWN; + } + else + cstr = NULL; + + e = za->entry + idx; + + if (e->changes) { + _zip_string_free(e->changes->comment); + e->changes->comment = NULL; + e->changes->changed &= ~ZIP_DIRENT_COMMENT; + } + + if (e->orig && e->orig->comment) + changed = !_zip_string_equal(e->orig->comment, cstr); + else + changed = (cstr != NULL); + + if (changed) { + if (e->changes == NULL) { + if ((e->changes = _zip_dirent_clone(e->orig)) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + _zip_string_free(cstr); + return -1; + } + } + e->changes->comment = cstr; + e->changes->changed |= ZIP_DIRENT_COMMENT; + } + else { + _zip_string_free(cstr); + if (e->changes && e->changes->changed == 0) { + _zip_dirent_free(e->changes); + e->changes = NULL; + } + } + + return 0; +} diff --git a/ext/libzip/zip_file_set_encryption.c b/ext/libzip/zip_file_set_encryption.c new file mode 100644 index 0000000000..a66a1b5472 --- /dev/null +++ b/ext/libzip/zip_file_set_encryption.c @@ -0,0 +1,116 @@ +/* + zip_file_set_encryption.c -- set encryption for file in archive + Copyright (C) 2016-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + +#include +#include + +ZIP_EXTERN int +zip_file_set_encryption(zip_t *za, zip_uint64_t idx, zip_uint16_t method, const char *password) { + zip_entry_t *e; + zip_uint16_t old_method; + + if (idx >= za->nentry) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (ZIP_IS_RDONLY(za)) { + zip_error_set(&za->error, ZIP_ER_RDONLY, 0); + return -1; + } + + if (method != ZIP_EM_NONE && _zip_get_encryption_implementation(method, ZIP_CODEC_ENCODE) == NULL) { + zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0); + return -1; + } + + e = za->entry + idx; + + old_method = (e->orig == NULL ? ZIP_EM_NONE : e->orig->encryption_method); + + if (method == old_method && password == NULL) { + if (e->changes) { + if (e->changes->changed & ZIP_DIRENT_PASSWORD) { + _zip_crypto_clear(e->changes->password, strlen(e->changes->password)); + free(e->changes->password); + e->changes->password = (e->orig == NULL ? NULL : e->orig->password); + } + e->changes->changed &= ~(ZIP_DIRENT_ENCRYPTION_METHOD | ZIP_DIRENT_PASSWORD); + if (e->changes->changed == 0) { + _zip_dirent_free(e->changes); + e->changes = NULL; + } + } + } + else { + char *our_password = NULL; + + if (password) { + if ((our_password = strdup(password)) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + } + + if (e->changes == NULL) { + if ((e->changes = _zip_dirent_clone(e->orig)) == NULL) { + if (our_password) { + _zip_crypto_clear(our_password, strlen(our_password)); + } + free(our_password); + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + } + + e->changes->encryption_method = method; + e->changes->changed |= ZIP_DIRENT_ENCRYPTION_METHOD; + if (password) { + e->changes->password = our_password; + e->changes->changed |= ZIP_DIRENT_PASSWORD; + } + else { + if (e->changes->changed & ZIP_DIRENT_PASSWORD) { + _zip_crypto_clear(e->changes->password, strlen(e->changes->password)); + free(e->changes->password); + e->changes->password = e->orig ? e->orig->password : NULL; + e->changes->changed &= ~ZIP_DIRENT_PASSWORD; + } + } + } + + return 0; +} diff --git a/ext/libzip/zip_file_set_external_attributes.c b/ext/libzip/zip_file_set_external_attributes.c new file mode 100644 index 0000000000..bb2ea61466 --- /dev/null +++ b/ext/libzip/zip_file_set_external_attributes.c @@ -0,0 +1,82 @@ +/* + zip_file_set_external_attributes.c -- set external attributes for entry + Copyright (C) 2013-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "zipint.h" + +ZIP_EXTERN int +zip_file_set_external_attributes(zip_t *za, zip_uint64_t idx, zip_flags_t flags, zip_uint8_t opsys, zip_uint32_t attributes) { + zip_entry_t *e; + int changed; + zip_uint8_t unchanged_opsys; + zip_uint32_t unchanged_attributes; + + if (_zip_get_dirent(za, idx, 0, NULL) == NULL) + return -1; + + if (ZIP_IS_RDONLY(za)) { + zip_error_set(&za->error, ZIP_ER_RDONLY, 0); + return -1; + } + + e = za->entry + idx; + + unchanged_opsys = (e->orig ? (zip_uint8_t)(e->orig->version_madeby >> 8) : (zip_uint8_t)ZIP_OPSYS_DEFAULT); + unchanged_attributes = e->orig ? e->orig->ext_attrib : ZIP_EXT_ATTRIB_DEFAULT; + + changed = (opsys != unchanged_opsys || attributes != unchanged_attributes); + + if (changed) { + if (e->changes == NULL) { + if ((e->changes = _zip_dirent_clone(e->orig)) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + } + e->changes->version_madeby = (zip_uint16_t)((opsys << 8) | (e->changes->version_madeby & 0xff)); + e->changes->ext_attrib = attributes; + e->changes->changed |= ZIP_DIRENT_ATTRIBUTES; + } + else if (e->changes) { + e->changes->changed &= ~ZIP_DIRENT_ATTRIBUTES; + if (e->changes->changed == 0) { + _zip_dirent_free(e->changes); + e->changes = NULL; + } + else { + e->changes->version_madeby = (zip_uint16_t)((unchanged_opsys << 8) | (e->changes->version_madeby & 0xff)); + e->changes->ext_attrib = unchanged_attributes; + } + } + + return 0; +} diff --git a/ext/libzip/zip_file_set_mtime.c b/ext/libzip/zip_file_set_mtime.c new file mode 100644 index 0000000000..838548ef92 --- /dev/null +++ b/ext/libzip/zip_file_set_mtime.c @@ -0,0 +1,68 @@ +/* + zip_file_set_mtime.c -- set modification time of entry. + Copyright (C) 2014-2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "zipint.h" + +ZIP_EXTERN int +zip_file_set_dostime(zip_t *za, zip_uint64_t idx, zip_uint16_t dtime, zip_uint16_t ddate, zip_flags_t flags) { + time_t mtime; + mtime = _zip_d2u_time(dtime, ddate); + return zip_file_set_mtime(za, idx, mtime, flags); +} + +ZIP_EXTERN int +zip_file_set_mtime(zip_t *za, zip_uint64_t idx, time_t mtime, zip_flags_t flags) { + zip_entry_t *e; + + if (_zip_get_dirent(za, idx, 0, NULL) == NULL) + return -1; + + if (ZIP_IS_RDONLY(za)) { + zip_error_set(&za->error, ZIP_ER_RDONLY, 0); + return -1; + } + + e = za->entry + idx; + + if (e->changes == NULL) { + if ((e->changes = _zip_dirent_clone(e->orig)) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + } + + e->changes->last_mod = mtime; + e->changes->changed |= ZIP_DIRENT_LAST_MOD; + + return 0; +} diff --git a/ext/libzip/zip_file_strerror.c b/ext/libzip/zip_file_strerror.c index 9ba70f14ff..1fa7fffd63 100644 --- a/ext/libzip/zip_file_strerror.c +++ b/ext/libzip/zip_file_strerror.c @@ -1,6 +1,6 @@ /* zip_file_sterror.c -- get string representation of zip file error - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,14 +31,11 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "zipint.h" - ZIP_EXTERN const char * -zip_file_strerror(struct zip_file *zf) -{ - return _zip_error_strerror(&zf->error); +zip_file_strerror(zip_file_t *zf) { + return zip_error_strerror(&zf->error); } diff --git a/ext/libzip/zip_fopen.c b/ext/libzip/zip_fopen.c index 6e2f724127..4f1a051dbd 100644 --- a/ext/libzip/zip_fopen.c +++ b/ext/libzip/zip_fopen.c @@ -1,6 +1,6 @@ /* zip_fopen.c -- open file in zip archive for reading - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,19 +31,16 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "zipint.h" - -ZIP_EXTERN struct zip_file * -zip_fopen(struct zip *za, const char *fname, int flags) -{ - int idx; +ZIP_EXTERN zip_file_t * +zip_fopen(zip_t *za, const char *fname, zip_flags_t flags) { + zip_int64_t idx; - if ((idx=zip_name_locate(za, fname, flags)) < 0) + if ((idx = zip_name_locate(za, fname, flags)) < 0) return NULL; - return zip_fopen_index(za, idx, flags); + return zip_fopen_index_encrypted(za, (zip_uint64_t)idx, flags, za->default_password); } diff --git a/ext/libzip/zip_fopen_encrypted.c b/ext/libzip/zip_fopen_encrypted.c new file mode 100644 index 0000000000..9e9b13e53e --- /dev/null +++ b/ext/libzip/zip_fopen_encrypted.c @@ -0,0 +1,46 @@ +/* + zip_fopen_encrypted.c -- open file for reading with password + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + + +ZIP_EXTERN zip_file_t * +zip_fopen_encrypted(zip_t *za, const char *fname, zip_flags_t flags, const char *password) { + zip_int64_t idx; + + if ((idx = zip_name_locate(za, fname, flags)) < 0) + return NULL; + + return zip_fopen_index_encrypted(za, (zip_uint64_t)idx, flags, password); +} diff --git a/ext/libzip/zip_fopen_index.c b/ext/libzip/zip_fopen_index.c index ea4a68acf7..e5205b73ef 100644 --- a/ext/libzip/zip_fopen_index.c +++ b/ext/libzip/zip_fopen_index.c @@ -1,6 +1,6 @@ /* zip_fopen_index.c -- open file in zip archive for reading by index - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,191 +31,11 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef _MSC_VER -#pragma warning (disable:4996) -#pragma warning (disable:4244) -#endif - - -#include -#include -#include #include "zipint.h" -static struct zip_file *_zip_file_new(struct zip *za); - - -ZIP_EXTERN struct zip_file * -zip_fopen_index(struct zip *za, int fileno, int flags) -{ - int len, ret; - int zfflags; - struct zip_file *zf; - - if ((fileno < 0) || (fileno >= za->nentry)) { - _zip_error_set(&za->error, ZIP_ER_INVAL, 0); - return NULL; - } - - if ((flags & ZIP_FL_UNCHANGED) == 0 - && ZIP_ENTRY_DATA_CHANGED(za->entry+fileno)) { - _zip_error_set(&za->error, ZIP_ER_CHANGED, 0); - return NULL; - } - - if (fileno >= za->cdir->nentry) { - _zip_error_set(&za->error, ZIP_ER_INVAL, 0); - return NULL; - } - - zfflags = 0; - switch (za->cdir->entry[fileno].comp_method) { - case ZIP_CM_STORE: - zfflags |= ZIP_ZF_CRC; - break; - - case ZIP_CM_DEFLATE: - if ((flags & ZIP_FL_COMPRESSED) == 0) - zfflags |= ZIP_ZF_CRC | ZIP_ZF_DECOMP; - break; - default: - if ((flags & ZIP_FL_COMPRESSED) == 0) { - _zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0); - return NULL; - } - break; - } - - zf = _zip_file_new(za); - - zf->flags = zfflags; - /* zf->name = za->cdir->entry[fileno].filename; */ - zf->method = za->cdir->entry[fileno].comp_method; - zf->bytes_left = za->cdir->entry[fileno].uncomp_size; - zf->cbytes_left = za->cdir->entry[fileno].comp_size; - zf->crc_orig = za->cdir->entry[fileno].crc; - - if ((zf->fpos=_zip_file_get_offset(za, fileno)) == 0) { - zip_fclose(zf); - return NULL; - } - - if ((zf->flags & ZIP_ZF_DECOMP) == 0) - zf->bytes_left = zf->cbytes_left; - else { - if ((zf->buffer=(char *)malloc(BUFSIZE)) == NULL) { - _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); - zip_fclose(zf); - return NULL; - } - - len = _zip_file_fillbuf(zf->buffer, BUFSIZE, zf); - if (len <= 0) { - _zip_error_copy(&za->error, &zf->error); - zip_fclose(zf); - return NULL; - } - - if ((zf->zstr = (z_stream *)malloc(sizeof(z_stream))) == NULL) { - _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); - zip_fclose(zf); - return NULL; - } - zf->zstr->zalloc = Z_NULL; - zf->zstr->zfree = Z_NULL; - zf->zstr->opaque = NULL; - zf->zstr->next_in = (Bytef *)zf->buffer; - zf->zstr->avail_in = len; - - /* negative value to tell zlib that there is no header */ - if ((ret=inflateInit2(zf->zstr, -MAX_WBITS)) != Z_OK) { - _zip_error_set(&za->error, ZIP_ER_ZLIB, ret); - zip_fclose(zf); - return NULL; - } - } - - return zf; -} - - - -int -_zip_file_fillbuf(void *buf, size_t buflen, struct zip_file *zf) -{ - ssize_t i; - int j; - - if (zf->error.zip_err != ZIP_ER_OK) - return -1; - - if ((zf->flags & ZIP_ZF_EOF) || zf->cbytes_left <= 0 || buflen <= 0) - return 0; - - if (fseeko(zf->za->zp, zf->fpos, SEEK_SET) < 0) { - _zip_error_set(&zf->error, ZIP_ER_SEEK, errno); - return -1; - } - if (buflen < zf->cbytes_left) - i = buflen; - else - i = zf->cbytes_left; - - j = (int)fread(buf, 1, i, zf->za->zp); - if (j == 0) { - _zip_error_set(&zf->error, ZIP_ER_EOF, 0); - j = -1; - } - else if (j < 0) - _zip_error_set(&zf->error, ZIP_ER_READ, errno); - else { - zf->fpos += j; - zf->cbytes_left -= j; - } - - return j; -} - - - -static struct zip_file * -_zip_file_new(struct zip *za) -{ - struct zip_file *zf, **file; - int n; - - if ((zf=(struct zip_file *)malloc(sizeof(struct zip_file))) == NULL) { - _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); - return NULL; - } - - if (za->nfile >= za->nfile_alloc-1) { - n = za->nfile_alloc + 10; - file = (struct zip_file **)realloc(za->file, - n*sizeof(struct zip_file *)); - if (file == NULL) { - _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); - free(zf); - return NULL; - } - za->nfile_alloc = n; - za->file = file; - } - - za->file[za->nfile++] = zf; - - zf->za = za; - _zip_error_init(&zf->error); - zf->flags = 0; - zf->crc = crc32(0L, Z_NULL, 0); - zf->crc_orig = 0; - zf->method = -1; - zf->bytes_left = zf->cbytes_left = 0; - zf->fpos = 0; - zf->buffer = NULL; - zf->zstr = NULL; - - return zf; +ZIP_EXTERN zip_file_t * +zip_fopen_index(zip_t *za, zip_uint64_t index, zip_flags_t flags) { + return zip_fopen_index_encrypted(za, index, flags, za->default_password); } diff --git a/ext/libzip/zip_fopen_index_encrypted.c b/ext/libzip/zip_fopen_index_encrypted.c new file mode 100644 index 0000000000..6712c32a9f --- /dev/null +++ b/ext/libzip/zip_fopen_index_encrypted.c @@ -0,0 +1,83 @@ +/* + zip_fopen_index_encrypted.c -- open file for reading by index w/ password + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include +#include + +#include "zipint.h" + +static zip_file_t *_zip_file_new(zip_t *za); + + +ZIP_EXTERN zip_file_t * +zip_fopen_index_encrypted(zip_t *za, zip_uint64_t index, zip_flags_t flags, const char *password) { + zip_file_t *zf; + zip_source_t *src; + + if ((src = _zip_source_zip_new(za, za, index, flags, 0, 0, password)) == NULL) + return NULL; + + if (zip_source_open(src) < 0) { + _zip_error_set_from_source(&za->error, src); + zip_source_free(src); + return NULL; + } + + if ((zf = _zip_file_new(za)) == NULL) { + zip_source_free(src); + return NULL; + } + + zf->src = src; + + return zf; +} + + +static zip_file_t * +_zip_file_new(zip_t *za) { + zip_file_t *zf; + + if ((zf = (zip_file_t *)malloc(sizeof(struct zip_file))) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return NULL; + } + + zf->za = za; + zip_error_init(&zf->error); + zf->eof = 0; + zf->src = NULL; + + return zf; +} diff --git a/ext/libzip/zip_fread.c b/ext/libzip/zip_fread.c index 036e87c547..646d712408 100644 --- a/ext/libzip/zip_fread.c +++ b/ext/libzip/zip_fread.c @@ -1,6 +1,6 @@ /* zip_fread.c -- read from file - Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -32,21 +32,12 @@ */ -#ifdef _MSC_VER -#pragma warning (disable:4996) -#pragma warning (disable:4244) -#endif - #include "zipint.h" - -ZIP_EXTERN ssize_t -zip_fread(struct zip_file *zf, void *outbuf, size_t toread) -{ - int ret; - size_t out_before, len; - int i; +ZIP_EXTERN zip_int64_t +zip_fread(zip_file_t *zf, void *outbuf, zip_uint64_t toread) { + zip_int64_t n; if (!zf) return -1; @@ -54,81 +45,18 @@ zip_fread(struct zip_file *zf, void *outbuf, size_t toread) if (zf->error.zip_err != 0) return -1; - if ((zf->flags & ZIP_ZF_EOF) || (toread == 0)) + if (toread > ZIP_INT64_MAX) { + zip_error_set(&zf->error, ZIP_ER_INVAL, 0); + return -1; + } + + if ((zf->eof) || (toread == 0)) return 0; - if (zf->bytes_left == 0) { - zf->flags |= ZIP_ZF_EOF; - if (zf->flags & ZIP_ZF_CRC) { - if (zf->crc != zf->crc_orig) { - _zip_error_set(&zf->error, ZIP_ER_CRC, 0); - return -1; - } - } - return 0; + if ((n = zip_source_read(zf->src, outbuf, toread)) < 0) { + _zip_error_set_from_source(&zf->error, zf->src); + return -1; } - - if ((zf->flags & ZIP_ZF_DECOMP) == 0) { - ret = _zip_file_fillbuf(outbuf, toread, zf); - if (ret > 0) { - if (zf->flags & ZIP_ZF_CRC) - zf->crc = crc32(zf->crc, (Bytef *)outbuf, ret); - zf->bytes_left -= ret; - } - return ret; - } - - zf->zstr->next_out = (Bytef *)outbuf; - zf->zstr->avail_out = (uInt)toread; - out_before = zf->zstr->total_out; - - /* endless loop until something has been accomplished */ - for (;;) { - ret = inflate(zf->zstr, Z_SYNC_FLUSH); - switch (ret) { - case Z_STREAM_END: - if (zf->zstr->total_out == out_before) { - if (zf->crc != zf->crc_orig) { - _zip_error_set(&zf->error, ZIP_ER_CRC, 0); - return -1; - } - else - return 0; - } - - /* fallthrough */ - - case Z_OK: - len = zf->zstr->total_out - out_before; - if (len >= zf->bytes_left || len >= toread) { - if (zf->flags & ZIP_ZF_CRC) - zf->crc = crc32(zf->crc, (Bytef *)outbuf, (uInt)len); - zf->bytes_left -= (unsigned long)len; - return len; - } - break; - - case Z_BUF_ERROR: - if (zf->zstr->avail_in == 0) { - i = _zip_file_fillbuf(zf->buffer, BUFSIZE, zf); - if (i == 0) { - _zip_error_set(&zf->error, ZIP_ER_INCONS, 0); - return -1; - } - else if (i < 0) - return -1; - zf->zstr->next_in = (Bytef *)zf->buffer; - zf->zstr->avail_in = i; - continue; - } - /* fallthrough */ - case Z_NEED_DICT: - case Z_DATA_ERROR: - case Z_STREAM_ERROR: - case Z_MEM_ERROR: - _zip_error_set(&zf->error, ZIP_ER_ZLIB, ret); - return -1; - } - } + return n; } diff --git a/ext/libzip/zip_fseek.c b/ext/libzip/zip_fseek.c new file mode 100644 index 0000000000..dc6d9787ef --- /dev/null +++ b/ext/libzip/zip_fseek.c @@ -0,0 +1,51 @@ +/* + zip_fseek.c -- seek in file + Copyright (C) 2016-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + +ZIP_EXTERN zip_int8_t +zip_fseek(zip_file_t *zf, zip_int64_t offset, int whence) { + if (!zf) + return -1; + + if (zf->error.zip_err != 0) + return -1; + + if (zip_source_seek(zf->src, offset, whence) < 0) { + _zip_error_set_from_source(&zf->error, zf->src); + return -1; + } + + return 0; +} diff --git a/ext/libzip/zip_ftell.c b/ext/libzip/zip_ftell.c new file mode 100644 index 0000000000..944db7b978 --- /dev/null +++ b/ext/libzip/zip_ftell.c @@ -0,0 +1,54 @@ +/* + zip_ftell.c -- tell position in file + Copyright (C) 2016-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + +ZIP_EXTERN zip_int64_t +zip_ftell(zip_file_t *zf) { + zip_int64_t res; + + if (!zf) + return -1; + + if (zf->error.zip_err != 0) + return -1; + + res = zip_source_tell(zf->src); + if (res < 0) { + _zip_error_set_from_source(&zf->error, zf->src); + return -1; + } + + return res; +} diff --git a/ext/libzip/zip_get_archive_comment.c b/ext/libzip/zip_get_archive_comment.c index 669eb70216..953c6f42db 100644 --- a/ext/libzip/zip_get_archive_comment.c +++ b/ext/libzip/zip_get_archive_comment.c @@ -1,6 +1,6 @@ /* zip_get_archive_comment.c -- get archive comment - Copyright (C) 2006-2007 Dieter Baron and Thomas Klausner + Copyright (C) 2006-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,30 +31,28 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - + +#include #include "zipint.h" - ZIP_EXTERN const char * -zip_get_archive_comment(struct zip *za, int *lenp, int flags) -{ - if ((flags & ZIP_FL_UNCHANGED) - || (za->ch_comment_len == -1)) { - if (za->cdir) { - if (lenp != NULL) - *lenp = za->cdir->comment_len; - return za->cdir->comment; - } - else { - if (lenp != NULL) - *lenp = -1; - return NULL; - } - } - - if (lenp != NULL) - *lenp = za->ch_comment_len; - return za->ch_comment; +zip_get_archive_comment(zip_t *za, int *lenp, zip_flags_t flags) { + zip_string_t *comment; + zip_uint32_t len; + const zip_uint8_t *str; + + if ((flags & ZIP_FL_UNCHANGED) || (za->comment_changes == NULL)) + comment = za->comment_orig; + else + comment = za->comment_changes; + + if ((str = _zip_string_get(comment, &len, flags, &za->error)) == NULL) + return NULL; + + if (lenp) + *lenp = (int)len; + + return (const char *)str; } diff --git a/ext/libzip/zip_get_archive_flag.c b/ext/libzip/zip_get_archive_flag.c index 4733d92b58..fa4dd502b2 100644 --- a/ext/libzip/zip_get_archive_flag.c +++ b/ext/libzip/zip_get_archive_flag.c @@ -1,6 +1,6 @@ /* zip_get_archive_flag.c -- get archive global flag - Copyright (C) 2008 Dieter Baron and Thomas Klausner + Copyright (C) 2008-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,16 +31,13 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "zipint.h" - ZIP_EXTERN int -zip_get_archive_flag(struct zip *za, int flag, int flags) -{ - int fl; +zip_get_archive_flag(zip_t *za, zip_flags_t flag, zip_flags_t flags) { + unsigned int fl; fl = (flags & ZIP_FL_UNCHANGED) ? za->flags : za->ch_flags; diff --git a/ext/libzip/zip_get_encryption_implementation.c b/ext/libzip/zip_get_encryption_implementation.c new file mode 100644 index 0000000000..3909e6f6b4 --- /dev/null +++ b/ext/libzip/zip_get_encryption_implementation.c @@ -0,0 +1,62 @@ +/* + zip_get_encryption_implementation.c -- get encryption implementation + Copyright (C) 2009-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + + +zip_encryption_implementation +_zip_get_encryption_implementation(zip_uint16_t em, int operation) { + switch (em) { + case ZIP_EM_TRAD_PKWARE: + return operation == ZIP_CODEC_DECODE ? zip_source_pkware_decode : zip_source_pkware_encode; + +#if defined(HAVE_CRYPTO) + case ZIP_EM_AES_128: + case ZIP_EM_AES_192: + case ZIP_EM_AES_256: + return operation == ZIP_CODEC_DECODE ? zip_source_winzip_aes_decode : zip_source_winzip_aes_encode; +#endif + + default: + return NULL; + } +} + +ZIP_EXTERN int +zip_encryption_method_supported(zip_uint16_t method, int encode) { + if (method == ZIP_EM_NONE) { + return 1; + } + return _zip_get_encryption_implementation(method, encode ? ZIP_CODEC_ENCODE : ZIP_CODEC_DECODE) != NULL; +} diff --git a/ext/libzip/zip_get_file_comment.c b/ext/libzip/zip_get_file_comment.c index adace72474..f80068612c 100644 --- a/ext/libzip/zip_get_file_comment.c +++ b/ext/libzip/zip_get_file_comment.c @@ -1,6 +1,6 @@ /* zip_get_file_comment.c -- get file comment - Copyright (C) 2006-2007 Dieter Baron and Thomas Klausner + Copyright (C) 2006-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,28 +31,20 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - +#define _ZIP_COMPILING_DEPRECATED #include "zipint.h" - ZIP_EXTERN const char * -zip_get_file_comment(struct zip *za, int idx, int *lenp, int flags) -{ - if (idx < 0 || idx >= za->nentry) { - _zip_error_set(&za->error, ZIP_ER_INVAL, 0); - return NULL; +zip_get_file_comment(zip_t *za, zip_uint64_t idx, int *lenp, int flags) { + zip_uint32_t len; + const char *s; + + if ((s = zip_file_get_comment(za, idx, &len, (zip_flags_t)flags)) != NULL) { + if (lenp) + *lenp = (int)len; } - if ((flags & ZIP_FL_UNCHANGED) - || (za->entry[idx].ch_comment_len == -1)) { - if (lenp != NULL) - *lenp = za->cdir->entry[idx].comment_len; - return za->cdir->entry[idx].comment; - } - - if (lenp != NULL) - *lenp = za->entry[idx].ch_comment_len; - return za->entry[idx].ch_comment; + return s; } diff --git a/ext/libzip/zip_get_name.c b/ext/libzip/zip_get_name.c index 8f5050bd2e..4344209303 100644 --- a/ext/libzip/zip_get_name.c +++ b/ext/libzip/zip_get_name.c @@ -1,6 +1,6 @@ /* zip_get_name.c -- get filename for a file in zip file - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,41 +31,28 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - + +#include #include "zipint.h" - ZIP_EXTERN const char * -zip_get_name(struct zip *za, int idx, int flags) -{ +zip_get_name(zip_t *za, zip_uint64_t idx, zip_flags_t flags) { return _zip_get_name(za, idx, flags, &za->error); } - const char * -_zip_get_name(struct zip *za, int idx, int flags, struct zip_error *error) -{ - if (idx < 0 || idx >= za->nentry) { - _zip_error_set(error, ZIP_ER_INVAL, 0); - return NULL; - } +_zip_get_name(zip_t *za, zip_uint64_t idx, zip_flags_t flags, zip_error_t *error) { + zip_dirent_t *de; + const zip_uint8_t *str; - if ((flags & ZIP_FL_UNCHANGED) == 0) { - if (za->entry[idx].state == ZIP_ST_DELETED) { - _zip_error_set(error, ZIP_ER_DELETED, 0); - return NULL; - } - if (za->entry[idx].ch_filename) - return za->entry[idx].ch_filename; - } - - if (za->cdir == NULL || idx >= za->cdir->nentry) { - _zip_error_set(error, ZIP_ER_INVAL, 0); + if ((de = _zip_get_dirent(za, idx, flags, error)) == NULL) return NULL; - } - - return za->cdir->entry[idx].filename; + + if ((str = _zip_string_get(de->filename, NULL, flags, error)) == NULL) + return NULL; + + return (const char *)str; } diff --git a/ext/libzip/zip_source_file.c b/ext/libzip/zip_get_num_entries.c similarity index 77% rename from ext/libzip/zip_source_file.c rename to ext/libzip/zip_get_num_entries.c index eb1129a275..410d26e117 100644 --- a/ext/libzip/zip_source_file.c +++ b/ext/libzip/zip_get_num_entries.c @@ -1,6 +1,6 @@ /* - zip_source_file.c -- create data source from file - Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner + zip_get_num_entries.c -- get number of entries in archive + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,25 +31,22 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - - -#include -#include #include "zipint.h" - -ZIP_EXTERN struct zip_source * -zip_source_file(struct zip *za, const char *fname, off_t start, off_t len) -{ +ZIP_EXTERN zip_int64_t +zip_get_num_entries(zip_t *za, zip_flags_t flags) { + zip_uint64_t n; + if (za == NULL) - return NULL; + return -1; - if (fname == NULL || start < 0 || len < -1) { - _zip_error_set(&za->error, ZIP_ER_INVAL, 0); - return NULL; + if (flags & ZIP_FL_UNCHANGED) { + n = za->nentry; + while (n > 0 && za->entry[n - 1].orig == NULL) + --n; + return (zip_int64_t)n; } - - return _zip_source_file_or_p(za, fname, NULL, start, len); + return (zip_int64_t)za->nentry; } diff --git a/ext/libzip/zip_get_num_files.c b/ext/libzip/zip_get_num_files.c index 1d9baa3d65..4895eaabe4 100644 --- a/ext/libzip/zip_get_num_files.c +++ b/ext/libzip/zip_get_num_files.c @@ -1,6 +1,6 @@ /* zip_get_num_files.c -- get number of files in archive - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,17 +31,21 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - +#define _ZIP_COMPILING_DEPRECATED #include "zipint.h" +#include - ZIP_EXTERN int -zip_get_num_files(struct zip *za) -{ +zip_get_num_files(zip_t *za) { if (za == NULL) return -1; - return za->nentry; + if (za->nentry > INT_MAX) { + zip_error_set(&za->error, ZIP_ER_OPNOTSUPP, 0); + return -1; + } + + return (int)za->nentry; } diff --git a/ext/libzip/zip_hash.c b/ext/libzip/zip_hash.c new file mode 100644 index 0000000000..3206dbf749 --- /dev/null +++ b/ext/libzip/zip_hash.c @@ -0,0 +1,410 @@ +/* + zip_hash.c -- hash table string -> uint64 + Copyright (C) 2015-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "zipint.h" +#include +#include + +/* parameter for the string hash function */ +#define HASH_MULTIPLIER 33 +#define HASH_START 5381 + +/* hash table's fill ratio is kept between these by doubling/halfing its size as necessary */ +#define HASH_MAX_FILL .75 +#define HASH_MIN_FILL .01 + +/* but hash table size is kept between these */ +#define HASH_MIN_SIZE 256 +#define HASH_MAX_SIZE 0x80000000ul + +struct zip_hash_entry { + const zip_uint8_t *name; + zip_int64_t orig_index; + zip_int64_t current_index; + struct zip_hash_entry *next; + zip_uint32_t hash_value; +}; +typedef struct zip_hash_entry zip_hash_entry_t; + +struct zip_hash { + zip_uint32_t table_size; + zip_uint64_t nentries; + zip_hash_entry_t **table; +}; + + +/* free list of entries */ +static void +free_list(zip_hash_entry_t *entry) { + while (entry != NULL) { + zip_hash_entry_t *next = entry->next; + free(entry); + entry = next; + } +} + + +/* compute hash of string, full 32 bit value */ +static zip_uint32_t +hash_string(const zip_uint8_t *name) { + zip_uint64_t value = HASH_START; + + if (name == NULL) { + return 0; + } + + while (*name != 0) { + value = (zip_uint64_t)(((value * HASH_MULTIPLIER) + (zip_uint8_t)*name) % 0x100000000ul); + name++; + } + + return (zip_uint32_t)value; +} + + +/* resize hash table; new_size must be a power of 2, can be larger or smaller than current size */ +static bool +hash_resize(zip_hash_t *hash, zip_uint32_t new_size, zip_error_t *error) { + zip_hash_entry_t **new_table; + + if (new_size == hash->table_size) { + return true; + } + + if ((new_table = (zip_hash_entry_t **)calloc(new_size, sizeof(zip_hash_entry_t *))) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return false; + } + + if (hash->nentries > 0) { + zip_uint32_t i; + + for (i = 0; i < hash->table_size; i++) { + zip_hash_entry_t *entry = hash->table[i]; + while (entry) { + zip_hash_entry_t *next = entry->next; + + zip_uint32_t new_index = entry->hash_value % new_size; + + entry->next = new_table[new_index]; + new_table[new_index] = entry; + + entry = next; + } + } + } + + free(hash->table); + hash->table = new_table; + hash->table_size = new_size; + + return true; +} + + +static zip_uint32_t +size_for_capacity(zip_uint64_t capacity) { + double needed_size = capacity / HASH_MAX_FILL; + zip_uint32_t v; + + if (needed_size > ZIP_UINT32_MAX) { + v = ZIP_UINT32_MAX; + } + else { + v = (zip_uint32_t)needed_size; + } + + if (v > HASH_MAX_SIZE) { + return HASH_MAX_SIZE; + } + + /* From Bit Twiddling Hacks by Sean Eron Anderson + (http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2). */ + + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + + return v; +} + + +zip_hash_t * +_zip_hash_new(zip_error_t *error) { + zip_hash_t *hash; + + if ((hash = (zip_hash_t *)malloc(sizeof(zip_hash_t))) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + hash->table_size = 0; + hash->nentries = 0; + hash->table = NULL; + + return hash; +} + + +void +_zip_hash_free(zip_hash_t *hash) { + zip_uint32_t i; + + if (hash == NULL) { + return; + } + + if (hash->table != NULL) { + for (i = 0; i < hash->table_size; i++) { + if (hash->table[i] != NULL) { + free_list(hash->table[i]); + } + } + free(hash->table); + } + free(hash); +} + + +/* insert into hash, return error on existence or memory issues */ +bool +_zip_hash_add(zip_hash_t *hash, const zip_uint8_t *name, zip_uint64_t index, zip_flags_t flags, zip_error_t *error) { + zip_uint32_t hash_value, table_index; + zip_hash_entry_t *entry; + + if (hash == NULL || name == NULL || index > ZIP_INT64_MAX) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return false; + } + + if (hash->table_size == 0) { + if (!hash_resize(hash, HASH_MIN_SIZE, error)) { + return false; + } + } + + hash_value = hash_string(name); + table_index = hash_value % hash->table_size; + + for (entry = hash->table[table_index]; entry != NULL; entry = entry->next) { + if (entry->hash_value == hash_value && strcmp((const char *)name, (const char *)entry->name) == 0) { + if (((flags & ZIP_FL_UNCHANGED) && entry->orig_index != -1) || entry->current_index != -1) { + zip_error_set(error, ZIP_ER_EXISTS, 0); + return false; + } + else { + break; + } + } + } + + if (entry == NULL) { + if ((entry = (zip_hash_entry_t *)malloc(sizeof(zip_hash_entry_t))) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return false; + } + entry->name = name; + entry->next = hash->table[table_index]; + hash->table[table_index] = entry; + entry->hash_value = hash_value; + entry->orig_index = -1; + hash->nentries++; + if (hash->nentries > hash->table_size * HASH_MAX_FILL && hash->table_size < HASH_MAX_SIZE) { + if (!hash_resize(hash, hash->table_size * 2, error)) { + return false; + } + } + } + + if (flags & ZIP_FL_UNCHANGED) { + entry->orig_index = (zip_int64_t)index; + } + entry->current_index = (zip_int64_t)index; + + return true; +} + + +/* remove entry from hash, error if not found */ +bool +_zip_hash_delete(zip_hash_t *hash, const zip_uint8_t *name, zip_error_t *error) { + zip_uint32_t hash_value, index; + zip_hash_entry_t *entry, *previous; + + if (hash == NULL || name == NULL) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return false; + } + + if (hash->nentries > 0) { + hash_value = hash_string(name); + index = hash_value % hash->table_size; + previous = NULL; + entry = hash->table[index]; + while (entry) { + if (entry->hash_value == hash_value && strcmp((const char *)name, (const char *)entry->name) == 0) { + if (entry->orig_index == -1) { + if (previous) { + previous->next = entry->next; + } + else { + hash->table[index] = entry->next; + } + free(entry); + hash->nentries--; + if (hash->nentries < hash->table_size * HASH_MIN_FILL && hash->table_size > HASH_MIN_SIZE) { + if (!hash_resize(hash, hash->table_size / 2, error)) { + return false; + } + } + } + else { + entry->current_index = -1; + } + return true; + } + previous = entry; + entry = entry->next; + } + } + + zip_error_set(error, ZIP_ER_NOENT, 0); + return false; +} + + +/* find value for entry in hash, -1 if not found */ +zip_int64_t +_zip_hash_lookup(zip_hash_t *hash, const zip_uint8_t *name, zip_flags_t flags, zip_error_t *error) { + zip_uint32_t hash_value, index; + zip_hash_entry_t *entry; + + if (hash == NULL || name == NULL) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return -1; + } + + if (hash->nentries > 0) { + hash_value = hash_string(name); + index = hash_value % hash->table_size; + for (entry = hash->table[index]; entry != NULL; entry = entry->next) { + if (strcmp((const char *)name, (const char *)entry->name) == 0) { + if (flags & ZIP_FL_UNCHANGED) { + if (entry->orig_index != -1) { + return entry->orig_index; + } + } + else { + if (entry->current_index != -1) { + return entry->current_index; + } + } + break; + } + } + } + + zip_error_set(error, ZIP_ER_NOENT, 0); + return -1; +} + + +bool +_zip_hash_reserve_capacity(zip_hash_t *hash, zip_uint64_t capacity, zip_error_t *error) { + zip_uint32_t new_size; + + if (capacity == 0) { + return true; + } + + new_size = size_for_capacity(capacity); + + if (new_size <= hash->table_size) { + return true; + } + + if (!hash_resize(hash, new_size, error)) { + return false; + } + + return true; +} + + +bool +_zip_hash_revert(zip_hash_t *hash, zip_error_t *error) { + zip_uint32_t i; + zip_hash_entry_t *entry, *previous; + + for (i = 0; i < hash->table_size; i++) { + previous = NULL; + entry = hash->table[i]; + while (entry) { + if (entry->orig_index == -1) { + zip_hash_entry_t *p; + if (previous) { + previous->next = entry->next; + } + else { + hash->table[i] = entry->next; + } + p = entry; + entry = entry->next; + /* previous does not change */ + free(p); + hash->nentries--; + } + else { + entry->current_index = entry->orig_index; + previous = entry; + entry = entry->next; + } + } + } + + if (hash->nentries < hash->table_size * HASH_MIN_FILL && hash->table_size > HASH_MIN_SIZE) { + zip_uint32_t new_size = hash->table_size / 2; + while (hash->nentries < new_size * HASH_MIN_FILL && new_size > HASH_MIN_SIZE) { + new_size /= 2; + } + if (!hash_resize(hash, new_size, error)) { + return false; + } + } + + return true; +} diff --git a/ext/libzip/zip_io_util.c b/ext/libzip/zip_io_util.c new file mode 100644 index 0000000000..dd07cbb9aa --- /dev/null +++ b/ext/libzip/zip_io_util.c @@ -0,0 +1,134 @@ +/* + zip_io_util.c -- I/O helper functions + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "zipint.h" + +int +_zip_read(zip_source_t *src, zip_uint8_t *b, zip_uint64_t length, zip_error_t *error) { + zip_int64_t n; + + if (length > ZIP_INT64_MAX) { + zip_error_set(error, ZIP_ER_INTERNAL, 0); + return -1; + } + + if ((n = zip_source_read(src, b, length)) < 0) { + _zip_error_set_from_source(error, src); + return -1; + } + + if (n < (zip_int64_t)length) { + zip_error_set(error, ZIP_ER_EOF, 0); + return -1; + } + + return 0; +} + + +zip_uint8_t * +_zip_read_data(zip_buffer_t *buffer, zip_source_t *src, size_t length, bool nulp, zip_error_t *error) { + zip_uint8_t *r; + + if (length == 0 && !nulp) { + return NULL; + } + + r = (zip_uint8_t *)malloc(length + (nulp ? 1 : 0)); + if (!r) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + if (buffer) { + zip_uint8_t *data = _zip_buffer_get(buffer, length); + + if (data == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + free(r); + return NULL; + } + memcpy(r, data, length); + } + else { + if (_zip_read(src, r, length, error) < 0) { + free(r); + return NULL; + } + } + + if (nulp) { + zip_uint8_t *o; + /* replace any in-string NUL characters with spaces */ + r[length] = 0; + for (o = r; o < r + length; o++) + if (*o == '\0') + *o = ' '; + } + + return r; +} + + +zip_string_t * +_zip_read_string(zip_buffer_t *buffer, zip_source_t *src, zip_uint16_t len, bool nulp, zip_error_t *error) { + zip_uint8_t *raw; + zip_string_t *s; + + if ((raw = _zip_read_data(buffer, src, len, nulp, error)) == NULL) + return NULL; + + s = _zip_string_new(raw, len, ZIP_FL_ENC_GUESS, error); + free(raw); + return s; +} + + +int +_zip_write(zip_t *za, const void *data, zip_uint64_t length) { + zip_int64_t n; + + if ((n = zip_source_write(za->src, data, length)) < 0) { + _zip_error_set_from_source(&za->error, za->src); + return -1; + } + if ((zip_uint64_t)n != length) { + zip_error_set(&za->error, ZIP_ER_WRITE, EINTR); + return -1; + } + + return 0; +} diff --git a/ext/libzip/zip_libzip_version.c b/ext/libzip/zip_libzip_version.c new file mode 100644 index 0000000000..fadbc69bcc --- /dev/null +++ b/ext/libzip/zip_libzip_version.c @@ -0,0 +1,41 @@ +/* + zip_libzip_version.c -- return run-time version of library + Copyright (C) 2017-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + + +ZIP_EXTERN const char * +zip_libzip_version(void) { + return LIBZIP_VERSION; +} diff --git a/ext/libzip/zip_memdup.c b/ext/libzip/zip_memdup.c index 641125ed2d..05fcb7b77e 100644 --- a/ext/libzip/zip_memdup.c +++ b/ext/libzip/zip_memdup.c @@ -1,6 +1,6 @@ /* zip_memdup.c -- internal zip function, "strdup" with len - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -36,16 +36,17 @@ #include "zipint.h" - void * -_zip_memdup(const void *mem, size_t len, struct zip_error *error) -{ +_zip_memdup(const void *mem, size_t len, zip_error_t *error) { void *ret; + if (len == 0) + return NULL; + ret = malloc(len); if (!ret) { - _zip_error_set(error, ZIP_ER_MEMORY, 0); + zip_error_set(error, ZIP_ER_MEMORY, 0); return NULL; } diff --git a/ext/libzip/zip_mkstempm.c b/ext/libzip/zip_mkstempm.c new file mode 100644 index 0000000000..c1afade331 --- /dev/null +++ b/ext/libzip/zip_mkstempm.c @@ -0,0 +1,93 @@ +/* + zip_mkstempm.c -- mkstemp replacement that accepts a mode argument + Copyright (C) 2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include + +#include "zipint.h" + +/* + * create temporary file with same permissions as previous one; + * or default permissions if there is no previous file + */ +int +_zip_mkstempm(char *path, int mode) { + int fd; + char *start, *end, *xs; + + int xcnt = 0; + + end = path + strlen(path); + start = end - 1; + while (start >= path && *start == 'X') { + xcnt++; + start--; + } + + if (xcnt == 0) { + errno = EINVAL; + return -1; + } + + start++; + + for (;;) { + zip_uint32_t value = zip_random_uint32(); + + xs = start; + + while (xs < end) { + char digit = value % 36; + if (digit < 10) { + *(xs++) = digit + '0'; + } + else { + *(xs++) = digit - 10 + 'a'; + } + value /= 36; + } + + if ((fd = open(path, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, mode == -1 ? 0666 : (mode_t)mode)) >= 0) { + if (mode != -1) { + /* open() honors umask(), which we don't want in this case */ + (void)chmod(path, (mode_t)mode); + } + return fd; + } + if (errno != EEXIST) { + return -1; + } + } +} diff --git a/ext/libzip/zip_name_locate.c b/ext/libzip/zip_name_locate.c index 926abc1b90..f7318ecede 100644 --- a/ext/libzip/zip_name_locate.c +++ b/ext/libzip/zip_name_locate.c @@ -1,6 +1,6 @@ /* zip_name_locate.c -- get index by name - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,60 +31,62 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - - -#include "zipint.h" #include #ifdef HAVE_STRINGS_H #include #endif - -ZIP_EXTERN int -zip_name_locate(struct zip *za, const char *fname, int flags) -{ +#include "zipint.h" + + +ZIP_EXTERN zip_int64_t +zip_name_locate(zip_t *za, const char *fname, zip_flags_t flags) { return _zip_name_locate(za, fname, flags, &za->error); } - -int -_zip_name_locate(struct zip *za, const char *fname, int flags, - struct zip_error *error) -{ +zip_int64_t +_zip_name_locate(zip_t *za, const char *fname, zip_flags_t flags, zip_error_t *error) { int (*cmp)(const char *, const char *); const char *fn, *p; - int i, n; + zip_uint64_t i; + + if (za == NULL) + return -1; if (fname == NULL) { - _zip_error_set(error, ZIP_ER_INVAL, 0); + zip_error_set(error, ZIP_ER_INVAL, 0); return -1; } - - cmp = (flags & ZIP_FL_NOCASE) ? strcasecmp : strcmp; - n = (flags & ZIP_FL_UNCHANGED) ? za->cdir->nentry : za->nentry; - for (i=0; icdir->entry[i].filename; - else + if (flags & (ZIP_FL_NOCASE | ZIP_FL_NODIR | ZIP_FL_ENC_CP437)) { + /* can't use hash table */ + cmp = (flags & ZIP_FL_NOCASE) ? strcasecmp : strcmp; + + for (i = 0; i < za->nentry; i++) { fn = _zip_get_name(za, i, flags, error); - /* newly added (partially filled) entry */ - if (fn == NULL) - continue; - - if (flags & ZIP_FL_NODIR) { - p = strrchr(fn, '/'); - if (p) - fn = p+1; + /* newly added (partially filled) entry or error */ + if (fn == NULL) + continue; + + if (flags & ZIP_FL_NODIR) { + p = strrchr(fn, '/'); + if (p) + fn = p + 1; + } + + if (cmp(fname, fn) == 0) { + _zip_error_clear(error); + return (zip_int64_t)i; + } } - if (cmp(fname, fn) == 0) - return i; + zip_error_set(error, ZIP_ER_NOENT, 0); + return -1; + } + else { + return _zip_hash_lookup(za->names, (const zip_uint8_t *)fname, flags, error); } - - _zip_error_set(error, ZIP_ER_NOENT, 0); - return -1; } diff --git a/ext/libzip/zip_new.c b/ext/libzip/zip_new.c index 3e8ccee644..4f58ecc7ee 100644 --- a/ext/libzip/zip_new.c +++ b/ext/libzip/zip_new.c @@ -1,6 +1,6 @@ /* zip_new.c -- create and init struct zip - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,40 +31,43 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include "zipint.h" - /* _zip_new: creates a new zipfile struct, and sets the contents to zero; returns the new struct. */ -struct zip * -_zip_new(struct zip_error *error) -{ - struct zip *za; +zip_t * +_zip_new(zip_error_t *error) { + zip_t *za; - za = (struct zip *)malloc(sizeof(struct zip)); + za = (zip_t *)malloc(sizeof(struct zip)); if (!za) { - _zip_error_set(error, ZIP_ER_MEMORY, 0); + zip_error_set(error, ZIP_ER_MEMORY, 0); return NULL; } - za->zn = NULL; - za->zp = NULL; - _zip_error_init(&za->error); - za->cdir = NULL; - za->ch_comment = NULL; - za->ch_comment_len = -1; + if ((za->names = _zip_hash_new(error)) == NULL) { + free(za); + return NULL; + } + + za->src = NULL; + za->open_flags = 0; + zip_error_init(&za->error); + za->flags = za->ch_flags = 0; + za->default_password = NULL; + za->comment_orig = za->comment_changes = NULL; + za->comment_changed = 0; za->nentry = za->nentry_alloc = 0; za->entry = NULL; - za->nfile = za->nfile_alloc = 0; - za->file = NULL; - za->flags = za->ch_flags = 0; - + za->nopen_source = za->nopen_source_alloc = 0; + za->open_source = NULL; + za->progress = NULL; + return za; } diff --git a/ext/libzip/zip_open.c b/ext/libzip/zip_open.c index f9cfff547d..019cd987b8 100644 --- a/ext/libzip/zip_open.c +++ b/ext/libzip/zip_open.c @@ -1,6 +1,6 @@ /* - zip_open.c -- open zip archive - Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner + zip_open.c -- open zip archive by name + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,243 +31,380 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef _MSC_VER -#pragma warning (disable:4996) -#pragma warning (disable:4244) -#endif -#include -#include -#include #include #include #include #include "zipint.h" -static void set_error(int *, struct zip_error *, int); -static int _zip_checkcons(FILE *, struct zip_cdir *, struct zip_error *); -static void _zip_check_torrentzip(struct zip *); -static struct zip_cdir *_zip_find_central_dir(FILE *, int, int *, off_t); -#ifdef UNICODE -static struct zip *_zip_allocate_new(const wchar_t *, int *); -static int _zip_file_exists(const wchar_t *, int, int *); -#else -static struct zip *_zip_allocate_new(const char *, int *); -static int _zip_file_exists(const char *, int, int *); -#endif -static int _zip_headercomp(struct zip_dirent *, int, - struct zip_dirent *, int); -static unsigned char *_zip_memmem(const unsigned char *, int, - const unsigned char *, int); -static struct zip_cdir *_zip_readcdir(FILE *, unsigned char *, unsigned char *, - int, int, struct zip_error *); +typedef enum { EXISTS_ERROR = -1, EXISTS_NOT = 0, EXISTS_OK } exists_t; +static zip_t *_zip_allocate_new(zip_source_t *src, unsigned int flags, zip_error_t *error); +static zip_int64_t _zip_checkcons(zip_t *za, zip_cdir_t *cdir, zip_error_t *error); +static zip_cdir_t *_zip_find_central_dir(zip_t *za, zip_uint64_t len); +static exists_t _zip_file_exists(zip_source_t *src, zip_error_t *error); +static int _zip_headercomp(const zip_dirent_t *, const zip_dirent_t *); +static unsigned char *_zip_memmem(const unsigned char *, size_t, const unsigned char *, size_t); +static zip_cdir_t *_zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error); +static zip_cdir_t *_zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error); +static zip_cdir_t *_zip_read_eocd64(zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error); - -ZIP_EXTERN struct zip * -#ifdef UNICODE -zip_open(const wchar_t *fn, int flags, int *zep) -#else -zip_open(const char *fn, int flags, int *zep) -#endif -{ - FILE *fp; - struct zip *za; - struct zip_cdir *cdir; - int i; - off_t len; - - switch (_zip_file_exists(fn, flags, zep)) { - case -1: - return NULL; - case 0: - return _zip_allocate_new(fn, zep); - default: - break; - } -#ifdef UNICODE - if ((fp=_wfopen(fn, L"rb")) == NULL) { -#else - if ((fp=fopen(fn, "rb")) == NULL) { -#endif - set_error(zep, NULL, ZIP_ER_OPEN); +ZIP_EXTERN zip_t * +zip_open(const char *fn, int _flags, int *zep) { + zip_t *za; + zip_source_t *src; + struct zip_error error; + + zip_error_init(&error); + if ((src = zip_source_file_create(fn, 0, -1, &error)) == NULL) { + _zip_set_open_error(zep, &error, 0); + zip_error_fini(&error); return NULL; } - fseeko(fp, 0, SEEK_END); - len = ftello(fp); + if ((za = zip_open_from_source(src, _flags, &error)) == NULL) { + zip_source_free(src); + _zip_set_open_error(zep, &error, 0); + zip_error_fini(&error); + return NULL; + } + + zip_error_fini(&error); + return za; +} + + +ZIP_EXTERN zip_t * +zip_open_from_source(zip_source_t *src, int _flags, zip_error_t *error) { + static zip_int64_t needed_support_read = -1; + static zip_int64_t needed_support_write = -1; + + unsigned int flags; + zip_int64_t supported; + exists_t exists; + + if (_flags < 0 || src == NULL) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + flags = (unsigned int)_flags; + + supported = zip_source_supports(src); + if (needed_support_read == -1) { + needed_support_read = zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_SEEK, ZIP_SOURCE_TELL, ZIP_SOURCE_STAT, -1); + needed_support_write = zip_source_make_command_bitmap(ZIP_SOURCE_BEGIN_WRITE, ZIP_SOURCE_COMMIT_WRITE, ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_REMOVE, -1); + } + if ((supported & needed_support_read) != needed_support_read) { + zip_error_set(error, ZIP_ER_OPNOTSUPP, 0); + return NULL; + } + if ((supported & needed_support_write) != needed_support_write) { + flags |= ZIP_RDONLY; + } + + if ((flags & (ZIP_RDONLY | ZIP_TRUNCATE)) == (ZIP_RDONLY | ZIP_TRUNCATE)) { + zip_error_set(error, ZIP_ER_RDONLY, 0); + return NULL; + } + + exists = _zip_file_exists(src, error); + switch (exists) { + case EXISTS_ERROR: + return NULL; + + case EXISTS_NOT: + if ((flags & ZIP_CREATE) == 0) { + zip_error_set(error, ZIP_ER_NOENT, 0); + return NULL; + } + return _zip_allocate_new(src, flags, error); + + default: { + zip_t *za; + if (flags & ZIP_EXCL) { + zip_error_set(error, ZIP_ER_EXISTS, 0); + return NULL; + } + if (zip_source_open(src) < 0) { + _zip_error_set_from_source(error, src); + return NULL; + } + + if (flags & ZIP_TRUNCATE) { + za = _zip_allocate_new(src, flags, error); + } + else { + /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL, just like open() */ + za = _zip_open(src, flags, error); + } + + if (za == NULL) { + zip_source_close(src); + return NULL; + } + return za; + } + } +} + + +zip_t * +_zip_open(zip_source_t *src, unsigned int flags, zip_error_t *error) { + zip_t *za; + zip_cdir_t *cdir; + struct zip_stat st; + zip_uint64_t len, idx; + + zip_stat_init(&st); + if (zip_source_stat(src, &st) < 0) { + _zip_error_set_from_source(error, src); + return NULL; + } + if ((st.valid & ZIP_STAT_SIZE) == 0) { + zip_error_set(error, ZIP_ER_SEEK, EOPNOTSUPP); + return NULL; + } + len = st.size; + + + if ((za = _zip_allocate_new(src, flags, error)) == NULL) { + return NULL; + } /* treat empty files as empty archives */ - if (len == 0) { - if ((za=_zip_allocate_new(fn, zep)) == NULL) - fclose(fp); - else - za->zp = fp; + if (len == 0 && zip_source_accept_empty(src)) { return za; } - cdir = _zip_find_central_dir(fp, flags, zep, len); - if (cdir == NULL) { - fclose(fp); + if ((cdir = _zip_find_central_dir(za, len)) == NULL) { + _zip_error_copy(error, &za->error); + /* keep src so discard does not get rid of it */ + zip_source_keep(src); + zip_discard(za); return NULL; } - if ((za=_zip_allocate_new(fn, zep)) == NULL) { - _zip_cdir_free(cdir); - fclose(fp); - return NULL; + za->entry = cdir->entry; + za->nentry = cdir->nentry; + za->nentry_alloc = cdir->nentry_alloc; + za->comment_orig = cdir->comment; + + free(cdir); + + _zip_hash_reserve_capacity(za->names, za->nentry, &za->error); + + for (idx = 0; idx < za->nentry; idx++) { + const zip_uint8_t *name = _zip_string_get(za->entry[idx].orig->filename, NULL, 0, error); + if (name == NULL) { + /* keep src so discard does not get rid of it */ + zip_source_keep(src); + zip_discard(za); + return NULL; + } + + if (_zip_hash_add(za->names, name, idx, ZIP_FL_UNCHANGED, &za->error) == false) { + if (za->error.zip_err != ZIP_ER_EXISTS || (flags & ZIP_CHECKCONS)) { + _zip_error_copy(error, &za->error); + /* keep src so discard does not get rid of it */ + zip_source_keep(src); + zip_discard(za); + return NULL; + } + } } - za->cdir = cdir; - za->zp = fp; - - if ((za->entry=(struct zip_entry *)malloc(sizeof(*(za->entry)) - * cdir->nentry)) == NULL) { - set_error(zep, NULL, ZIP_ER_MEMORY); - _zip_free(za); - return NULL; - } - for (i=0; inentry; i++) - _zip_entry_new(za); - - _zip_check_torrentzip(za); za->ch_flags = za->flags; return za; } - - -static void -set_error(int *zep, struct zip_error *err, int ze) -{ - int se; +void +_zip_set_open_error(int *zep, const zip_error_t *err, int ze) { if (err) { - _zip_error_get(err, &ze, &se); - if (zip_error_get_sys_type(ze) == ZIP_ET_SYS) - errno = se; + ze = zip_error_code_zip(err); + if (zip_error_system_type(err) == ZIP_ET_SYS) { + errno = zip_error_code_system(err); + } } if (zep) *zep = ze; } - /* _zip_readcdir: tries to find a valid end-of-central-directory at the beginning of buf, and then the corresponding central directory entries. - Returns a struct zip_cdir which contains the central directory + Returns a struct zip_cdir which contains the central directory entries, or NULL if unsuccessful. */ -static struct zip_cdir * -_zip_readcdir(FILE *fp, unsigned char *buf, unsigned char *eocd, int buflen, - int flags, struct zip_error *error) -{ - struct zip_cdir *cd; - unsigned char *cdp, **bufp; - int i, comlen, nentry; - unsigned int left; +static zip_cdir_t * +_zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error) { + zip_cdir_t *cd; + zip_uint16_t comment_len; + zip_uint64_t i, left; + zip_uint64_t eocd_offset = _zip_buffer_offset(buffer); + zip_buffer_t *cd_buffer; - comlen = buf + buflen - eocd - EOCDLEN; - if (comlen < 0) { + if (_zip_buffer_left(buffer) < EOCDLEN) { /* not enough bytes left for comment */ - _zip_error_set(error, ZIP_ER_NOZIP, 0); + zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; } /* check for end-of-central-dir magic */ - if (memcmp(eocd, EOCD_MAGIC, 4) != 0) { - _zip_error_set(error, ZIP_ER_NOZIP, 0); + if (memcmp(_zip_buffer_get(buffer, 4), EOCD_MAGIC, 4) != 0) { + zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; } - if (memcmp(eocd+4, "\0\0\0\0", 4) != 0) { - _zip_error_set(error, ZIP_ER_MULTIDISK, 0); - return NULL; - } - - cdp = eocd + 8; - /* number of cdir-entries on this disk */ - i = _zip_read2(&cdp); - /* number of cdir-entries */ - nentry = _zip_read2(&cdp); - - if ((cd=_zip_cdir_new(nentry, error)) == NULL) - return NULL; - - cd->size = _zip_read4(&cdp); - cd->offset = _zip_read4(&cdp); - cd->comment = NULL; - cd->comment_len = _zip_read2(&cdp); - - if ((comlen < cd->comment_len) || (cd->nentry != i)) { - _zip_error_set(error, ZIP_ER_NOZIP, 0); - free(cd); - return NULL; - } - if ((flags & ZIP_CHECKCONS) && comlen != cd->comment_len) { - _zip_error_set(error, ZIP_ER_INCONS, 0); - free(cd); - return NULL; - } - - if (cd->comment_len) { - if ((cd->comment=(char *)_zip_memdup(eocd+EOCDLEN, - cd->comment_len, error)) - == NULL) { - free(cd); - return NULL; - } - } - - if (cd->size < (unsigned int)(eocd-buf)) { - /* if buffer already read in, use it */ - cdp = eocd - cd->size; - bufp = &cdp; + if (eocd_offset >= EOCD64LOCLEN && memcmp(_zip_buffer_data(buffer) + eocd_offset - EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0) { + _zip_buffer_set_offset(buffer, eocd_offset - EOCD64LOCLEN); + cd = _zip_read_eocd64(za->src, buffer, buf_offset, za->flags, error); } else { - /* go to start of cdir and read it entry by entry */ - bufp = NULL; - clearerr(fp); - fseeko(fp, cd->offset, SEEK_SET); - /* possible consistency check: cd->offset = - len-(cd->size+cd->comment_len+EOCDLEN) ? */ - if (ferror(fp) || ((unsigned long)ftello(fp) != cd->offset)) { - /* seek error or offset of cdir wrong */ - if (ferror(fp)) - _zip_error_set(error, ZIP_ER_SEEK, errno); - else - _zip_error_set(error, ZIP_ER_NOZIP, 0); - free(cd); - return NULL; - } + _zip_buffer_set_offset(buffer, eocd_offset); + cd = _zip_read_eocd(buffer, buf_offset, za->flags, error); } - left = cd->size; - i=0; - do { - if (i == cd->nentry && left > 0) { - /* Infozip extension for more than 64k entries: - nentries wraps around, size indicates correct EOCD */ - _zip_cdir_grow(cd, cd->nentry+0x10000, error); - } + if (cd == NULL) + return NULL; - if ((_zip_dirent_read(cd->entry+i, fp, bufp, &left, 0, error)) < 0) { - cd->nentry = i; + _zip_buffer_set_offset(buffer, eocd_offset + 20); + comment_len = _zip_buffer_get_16(buffer); + + if (cd->offset + cd->size > buf_offset + eocd_offset) { + /* cdir spans past EOCD record */ + zip_error_set(error, ZIP_ER_INCONS, 0); + _zip_cdir_free(cd); + return NULL; + } + + if (comment_len || (za->open_flags & ZIP_CHECKCONS)) { + zip_uint64_t tail_len; + + _zip_buffer_set_offset(buffer, eocd_offset + EOCDLEN); + tail_len = _zip_buffer_left(buffer); + + if (tail_len < comment_len || ((za->open_flags & ZIP_CHECKCONS) && tail_len != comment_len)) { + zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } + + if (comment_len) { + if ((cd->comment = _zip_string_new(_zip_buffer_get(buffer, comment_len), comment_len, ZIP_FL_ENC_GUESS, error)) == NULL) { + _zip_cdir_free(cd); + return NULL; + } + } + } + + if (cd->offset >= buf_offset) { + zip_uint8_t *data; + /* if buffer already read in, use it */ + _zip_buffer_set_offset(buffer, cd->offset - buf_offset); + + if ((data = _zip_buffer_get(buffer, cd->size)) == NULL) { + zip_error_set(error, ZIP_ER_INCONS, 0); + _zip_cdir_free(cd); + return NULL; + } + if ((cd_buffer = _zip_buffer_new(data, cd->size)) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + _zip_cdir_free(cd); + return NULL; + } + } + else { + cd_buffer = NULL; + + if (zip_source_seek(za->src, (zip_int64_t)cd->offset, SEEK_SET) < 0) { + _zip_error_set_from_source(error, za->src); + _zip_cdir_free(cd); + return NULL; + } + + /* possible consistency check: cd->offset = len-(cd->size+cd->comment_len+EOCDLEN) ? */ + if (zip_source_tell(za->src) != (zip_int64_t)cd->offset) { + zip_error_set(error, ZIP_ER_NOZIP, 0); + _zip_cdir_free(cd); + return NULL; + } + } + + left = (zip_uint64_t)cd->size; + i = 0; + while (left > 0) { + bool grown = false; + zip_int64_t entry_size; + + if (i == cd->nentry) { + /* InfoZIP has a hack to avoid using Zip64: it stores nentries % 0x10000 */ + /* This hack isn't applicable if we're using Zip64, or if there is no central directory entry following. */ + + if (cd->is_zip64 || left < CDENTRYSIZE) { + break; + } + + if (!_zip_cdir_grow(cd, 0x10000, error)) { + _zip_cdir_free(cd); + _zip_buffer_free(cd_buffer); + return NULL; + } + grown = true; + } + + if ((cd->entry[i].orig = _zip_dirent_new()) == NULL || (entry_size = _zip_dirent_read(cd->entry[i].orig, za->src, cd_buffer, false, error)) < 0) { + if (grown && zip_error_code_zip(error) == ZIP_ER_NOZIP) { + zip_error_set(error, ZIP_ER_INCONS, 0); + } + _zip_cdir_free(cd); + _zip_buffer_free(cd_buffer); + return NULL; + } i++; - - } while (inentry); - + left -= (zip_uint64_t)entry_size; + } + + if (i != cd->nentry || left > 0) { + zip_error_set(error, ZIP_ER_INCONS, 0); + _zip_buffer_free(cd_buffer); + _zip_cdir_free(cd); + return NULL; + } + + if (za->open_flags & ZIP_CHECKCONS) { + bool ok; + + if (cd_buffer) { + ok = _zip_buffer_eof(cd_buffer); + } + else { + zip_int64_t offset = zip_source_tell(za->src); + + if (offset < 0) { + _zip_error_set_from_source(error, za->src); + _zip_cdir_free(cd); + return NULL; + } + ok = ((zip_uint64_t)offset == cd->offset + cd->size); + } + + if (!ok) { + zip_error_set(error, ZIP_ER_INCONS, 0); + _zip_buffer_free(cd_buffer); + _zip_cdir_free(cd); + return NULL; + } + } + + _zip_buffer_free(cd_buffer); return cd; } - /* _zip_checkcons: Checks the consistency of the central directory by comparing central @@ -275,303 +412,208 @@ _zip_readcdir(FILE *fp, unsigned char *buf, unsigned char *eocd, int buflen, file and header offsets. Returns -1 if not plausible, else the difference between the lowest and the highest fileposition reached */ -static int -_zip_checkcons(FILE *fp, struct zip_cdir *cd, struct zip_error *error) -{ - int i; - unsigned int min, max, j; +static zip_int64_t +_zip_checkcons(zip_t *za, zip_cdir_t *cd, zip_error_t *error) { + zip_uint64_t i; + zip_uint64_t min, max, j; struct zip_dirent temp; + _zip_dirent_init(&temp); if (cd->nentry) { - max = cd->entry[0].offset; - min = cd->entry[0].offset; + max = cd->entry[0].orig->offset; + min = cd->entry[0].orig->offset; } else min = max = 0; - for (i=0; inentry; i++) { - if (cd->entry[i].offset < min) - min = cd->entry[i].offset; - if (min > cd->offset) { - _zip_error_set(error, ZIP_ER_NOZIP, 0); + for (i = 0; i < cd->nentry; i++) { + if (cd->entry[i].orig->offset < min) + min = cd->entry[i].orig->offset; + if (min > (zip_uint64_t)cd->offset) { + zip_error_set(error, ZIP_ER_NOZIP, 0); return -1; } - - j = cd->entry[i].offset + cd->entry[i].comp_size - + cd->entry[i].filename_len + LENTRYSIZE; + + j = cd->entry[i].orig->offset + cd->entry[i].orig->comp_size + _zip_string_length(cd->entry[i].orig->filename) + LENTRYSIZE; if (j > max) max = j; - if (max > cd->offset) { - _zip_error_set(error, ZIP_ER_NOZIP, 0); + if (max > (zip_uint64_t)cd->offset) { + zip_error_set(error, ZIP_ER_NOZIP, 0); return -1; } - - if (fseeko(fp, cd->entry[i].offset, SEEK_SET) != 0) { - _zip_error_set(error, ZIP_ER_SEEK, 0); + + if (zip_source_seek(za->src, (zip_int64_t)cd->entry[i].orig->offset, SEEK_SET) < 0) { + _zip_error_set_from_source(error, za->src); return -1; } - - if (_zip_dirent_read(&temp, fp, NULL, NULL, 1, error) == -1) - return -1; - - if (_zip_headercomp(cd->entry+i, 0, &temp, 1) != 0) { - _zip_error_set(error, ZIP_ER_INCONS, 0); + + if (_zip_dirent_read(&temp, za->src, NULL, true, error) == -1) { _zip_dirent_finalize(&temp); return -1; } + + if (_zip_headercomp(cd->entry[i].orig, &temp) != 0) { + zip_error_set(error, ZIP_ER_INCONS, 0); + _zip_dirent_finalize(&temp); + return -1; + } + + cd->entry[i].orig->extra_fields = _zip_ef_merge(cd->entry[i].orig->extra_fields, temp.extra_fields); + cd->entry[i].orig->local_extra_fields_read = 1; + temp.extra_fields = NULL; + _zip_dirent_finalize(&temp); } - return max - min; + return (max - min) < ZIP_INT64_MAX ? (zip_int64_t)(max - min) : ZIP_INT64_MAX; } - - -/* _zip_check_torrentzip: - check wether ZA has a valid TORRENTZIP comment, i.e. is torrentzipped */ - -static void -_zip_check_torrentzip(struct zip *za) -{ - uLong crc_got, crc_should; - char buf[8+1]; - char *end; - - if (za->zp == NULL || za->cdir == NULL) - return; - - if (za->cdir->comment_len != TORRENT_SIG_LEN+8 - || strncmp(za->cdir->comment, TORRENT_SIG, TORRENT_SIG_LEN) != 0) - return; - - memcpy(buf, za->cdir->comment+TORRENT_SIG_LEN, 8); - buf[8] = '\0'; - errno = 0; - crc_should = strtoul(buf, &end, 16); - if ((crc_should == UINT_MAX && errno != 0) || (end && *end)) - return; - - if (_zip_filerange_crc(za->zp, za->cdir->offset, za->cdir->size, - &crc_got, NULL) < 0) - return; - - if (crc_got == crc_should) - za->flags |= ZIP_AFL_TORRENT; -} - - - /* _zip_headercomp: - compares two headers h1 and h2; if they are local headers, set - local1p or local2p respectively to 1, else 0. Return 0 if they - are identical, -1 if not. */ + compares a central directory entry and a local file header + Return 0 if they are consistent, -1 if not. */ static int -_zip_headercomp(struct zip_dirent *h1, int local1p, struct zip_dirent *h2, - int local2p) -{ - if ((h1->version_needed != h2->version_needed) +_zip_headercomp(const zip_dirent_t *central, const zip_dirent_t *local) { + if ((central->version_needed < local->version_needed) #if 0 /* some zip-files have different values in local and global headers for the bitflags */ - || (h1->bitflags != h2->bitflags) + || (central->bitflags != local->bitflags) #endif - || (h1->comp_method != h2->comp_method) - || (h1->last_mod != h2->last_mod) - || (h1->filename_len != h2->filename_len) - || !h1->filename || !h2->filename - || strcmp(h1->filename, h2->filename)) + || (central->comp_method != local->comp_method) || (central->last_mod != local->last_mod) || !_zip_string_equal(central->filename, local->filename)) return -1; - /* check that CRC and sizes are zero if data descriptor is used */ - if ((h1->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) && local1p - && (h1->crc != 0 - || h1->comp_size != 0 - || h1->uncomp_size != 0)) - return -1; - if ((h2->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) && local2p - && (h2->crc != 0 - || h2->comp_size != 0 - || h2->uncomp_size != 0)) - return -1; - - /* check that CRC and sizes are equal if no data descriptor is used */ - if (((h1->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0 || local1p == 0) - && ((h2->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0 || local2p == 0)) { - if ((h1->crc != h2->crc) - || (h1->comp_size != h2->comp_size) - || (h1->uncomp_size != h2->uncomp_size)) + if ((central->crc != local->crc) || (central->comp_size != local->comp_size) || (central->uncomp_size != local->uncomp_size)) { + /* InfoZip stores valid values in local header even when data descriptor is used. + This is in violation of the appnote. */ + if (((local->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0 || local->crc != 0 || local->comp_size != 0 || local->uncomp_size != 0)) return -1; } - - if ((local1p == local2p) - && ((h1->extrafield_len != h2->extrafield_len) - || (h1->extrafield_len && h2->extrafield - && memcmp(h1->extrafield, h2->extrafield, - h1->extrafield_len)))) - return -1; - - /* if either is local, nothing more to check */ - if (local1p || local2p) - return 0; - - if ((h1->version_madeby != h2->version_madeby) - || (h1->disk_number != h2->disk_number) - || (h1->int_attrib != h2->int_attrib) - || (h1->ext_attrib != h2->ext_attrib) - || (h1->offset != h2->offset) - || (h1->comment_len != h2->comment_len) - || (h1->comment_len && h2->comment - && memcmp(h1->comment, h2->comment, h1->comment_len))) - return -1; return 0; } - -static struct zip * -#ifdef UNICODE -_zip_allocate_new(const wchar_t *fn, int *zep) -#else -_zip_allocate_new(const char *fn, int *zep) -#endif -{ - struct zip *za; - struct zip_error error; +static zip_t * +_zip_allocate_new(zip_source_t *src, unsigned int flags, zip_error_t *error) { + zip_t *za; - if ((za=_zip_new(&error)) == NULL) { - set_error(zep, &error, 0); + if ((za = _zip_new(error)) == NULL) { return NULL; } -#ifdef UNICODE - za->zn = _wcsdup(fn); -#else - za->zn = strdup(fn); -#endif - if (!za->zn) { - _zip_free(za); - set_error(zep, NULL, ZIP_ER_MEMORY); - return NULL; + za->src = src; + za->open_flags = flags; + if (flags & ZIP_RDONLY) { + za->flags |= ZIP_AFL_RDONLY; + za->ch_flags |= ZIP_AFL_RDONLY; } return za; } - -static int -#ifdef UNICODE -_zip_file_exists(const wchar_t *fn, int flags, int *zep) -#else -_zip_file_exists(const char *fn, int flags, int *zep) -#endif -{ -#ifdef UNICODE - struct _stat st; -#else - struct stat st; -#endif - if (fn == NULL) { - set_error(zep, NULL, ZIP_ER_INVAL); - return -1; - } - -#ifdef UNICODE - if (_wstat(fn, &st) != 0) { -#else - if (stat(fn, &st) != 0) { -#endif - if (flags & ZIP_CREATE) - return 0; - else { - set_error(zep, NULL, ZIP_ER_OPEN); - return -1; +/* + * tests for file existence + */ +static exists_t +_zip_file_exists(zip_source_t *src, zip_error_t *error) { + struct zip_stat st; + + zip_stat_init(&st); + if (zip_source_stat(src, &st) != 0) { + zip_error_t *src_error = zip_source_error(src); + if (zip_error_code_zip(src_error) == ZIP_ER_READ && zip_error_code_system(src_error) == ENOENT) { + return EXISTS_NOT; } + _zip_error_copy(error, src_error); + return EXISTS_ERROR; } - else if ((flags & ZIP_EXCL)) { - set_error(zep, NULL, ZIP_ER_EXISTS); - return -1; - } - /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL, - just like open() */ - return 1; + return EXISTS_OK; } - -static struct zip_cdir * -_zip_find_central_dir(FILE *fp, int flags, int *zep, off_t len) -{ - struct zip_cdir *cdir, *cdirnew; - unsigned char *buf, *match; - int a, best, buflen, i; - struct zip_error zerr; +static zip_cdir_t * +_zip_find_central_dir(zip_t *za, zip_uint64_t len) { + zip_cdir_t *cdir, *cdirnew; + zip_uint8_t *match; + zip_int64_t buf_offset; + zip_uint64_t buflen; + zip_int64_t a; + zip_int64_t best; + zip_error_t error; + zip_buffer_t *buffer; - i = fseeko(fp, -(len < CDBUFSIZE ? len : CDBUFSIZE), SEEK_END); - if (i == -1 && errno != EFBIG) { - /* seek before start of file on my machine */ - set_error(zep, NULL, ZIP_ER_SEEK); + if (len < EOCDLEN) { + zip_error_set(&za->error, ZIP_ER_NOZIP, 0); return NULL; } - /* 64k is too much for stack */ - if ((buf=(unsigned char *)malloc(CDBUFSIZE)) == NULL) { - set_error(zep, NULL, ZIP_ER_MEMORY); + buflen = (len < CDBUFSIZE ? len : CDBUFSIZE); + if (zip_source_seek(za->src, -(zip_int64_t)buflen, SEEK_END) < 0) { + zip_error_t *src_error = zip_source_error(za->src); + if (zip_error_code_zip(src_error) != ZIP_ER_SEEK || zip_error_code_system(src_error) != EFBIG) { + /* seek before start of file on my machine */ + _zip_error_copy(&za->error, src_error); + return NULL; + } + } + if ((buf_offset = zip_source_tell(za->src)) < 0) { + _zip_error_set_from_source(&za->error, za->src); return NULL; } - clearerr(fp); - buflen = (int)fread(buf, 1, CDBUFSIZE, fp); - - if (ferror(fp)) { - set_error(zep, NULL, ZIP_ER_READ); - free(buf); + if ((buffer = _zip_buffer_new_from_source(za->src, buflen, NULL, &za->error)) == NULL) { return NULL; } - + best = -1; cdir = NULL; - match = buf; - _zip_error_set(&zerr, ZIP_ER_NOZIP, 0); + if (buflen >= CDBUFSIZE) { + /* EOCD64 locator is before EOCD, so leave place for it */ + _zip_buffer_set_offset(buffer, EOCD64LOCLEN); + } + zip_error_set(&error, ZIP_ER_NOZIP, 0); - while ((match=_zip_memmem(match, buflen-(match-buf)-18, - (const unsigned char *)EOCD_MAGIC, 4))!=NULL) { - /* found match -- check, if good */ - /* to avoid finding the same match all over again */ - match++; - if ((cdirnew=_zip_readcdir(fp, buf, match-1, buflen, flags, - &zerr)) == NULL) - continue; + match = _zip_buffer_get(buffer, 0); + while ((match = _zip_memmem(match, _zip_buffer_left(buffer) - (EOCDLEN - 4), (const unsigned char *)EOCD_MAGIC, 4)) != NULL) { + _zip_buffer_set_offset(buffer, (zip_uint64_t)(match - _zip_buffer_data(buffer))); + if ((cdirnew = _zip_read_cdir(za, buffer, (zip_uint64_t)buf_offset, &error)) != NULL) { + if (cdir) { + if (best <= 0) { + best = _zip_checkcons(za, cdir, &error); + } - if (cdir) { - if (best <= 0) - best = _zip_checkcons(fp, cdir, &zerr); - a = _zip_checkcons(fp, cdirnew, &zerr); - if (best < a) { - _zip_cdir_free(cdir); - cdir = cdirnew; - best = a; + a = _zip_checkcons(za, cdirnew, &error); + if (best < a) { + _zip_cdir_free(cdir); + cdir = cdirnew; + best = a; + } + else { + _zip_cdir_free(cdirnew); + } } - else - _zip_cdir_free(cdirnew); + else { + cdir = cdirnew; + if (za->open_flags & ZIP_CHECKCONS) + best = _zip_checkcons(za, cdir, &error); + else { + best = 0; + } + } + cdirnew = NULL; } - else { - cdir = cdirnew; - if (flags & ZIP_CHECKCONS) - best = _zip_checkcons(fp, cdir, &zerr); - else - best = 0; - } - cdirnew = NULL; + + match++; + _zip_buffer_set_offset(buffer, (zip_uint64_t)(match - _zip_buffer_data(buffer))); } - free(buf); - + _zip_buffer_free(buffer); + if (best < 0) { - set_error(zep, &zerr, 0); + _zip_error_copy(&za->error, &error); _zip_cdir_free(cdir); return NULL; } @@ -579,23 +621,229 @@ _zip_find_central_dir(FILE *fp, int flags, int *zep, off_t len) return cdir; } - static unsigned char * -_zip_memmem(const unsigned char *big, int biglen, const unsigned char *little, - int littlelen) -{ +_zip_memmem(const unsigned char *big, size_t biglen, const unsigned char *little, size_t littlelen) { const unsigned char *p; - + if ((biglen < littlelen) || (littlelen == 0)) return NULL; - p = big-1; - while ((p=(const unsigned char *) - memchr(p+1, little[0], (size_t)(big-(p+1)+biglen-littlelen+1))) - != NULL) { - if (memcmp(p+1, little+1, littlelen-1)==0) + p = big - 1; + while ((p = (const unsigned char *)memchr(p + 1, little[0], (size_t)(big - (p + 1)) + (size_t)(biglen - littlelen) + 1)) != NULL) { + if (memcmp(p + 1, little + 1, littlelen - 1) == 0) return (unsigned char *)p; } return NULL; } + + +static zip_cdir_t * +_zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error) { + zip_cdir_t *cd; + zip_uint64_t i, nentry, size, offset, eocd_offset; + + if (_zip_buffer_left(buffer) < EOCDLEN) { + zip_error_set(error, ZIP_ER_INCONS, 0); + return NULL; + } + + eocd_offset = _zip_buffer_offset(buffer); + + _zip_buffer_get(buffer, 4); /* magic already verified */ + + if (_zip_buffer_get_32(buffer) != 0) { + zip_error_set(error, ZIP_ER_MULTIDISK, 0); + return NULL; + } + + /* number of cdir-entries on this disk */ + i = _zip_buffer_get_16(buffer); + /* number of cdir-entries */ + nentry = _zip_buffer_get_16(buffer); + + if (nentry != i) { + zip_error_set(error, ZIP_ER_NOZIP, 0); + return NULL; + } + + size = _zip_buffer_get_32(buffer); + offset = _zip_buffer_get_32(buffer); + + if (offset + size < offset) { + zip_error_set(error, ZIP_ER_SEEK, EFBIG); + return NULL; + } + + if (offset + size > buf_offset + eocd_offset) { + /* cdir spans past EOCD record */ + zip_error_set(error, ZIP_ER_INCONS, 0); + return NULL; + } + + if ((flags & ZIP_CHECKCONS) && offset + size != buf_offset + eocd_offset) { + zip_error_set(error, ZIP_ER_INCONS, 0); + return NULL; + } + + if ((cd = _zip_cdir_new(nentry, error)) == NULL) + return NULL; + + cd->is_zip64 = false; + cd->size = size; + cd->offset = offset; + + return cd; +} + + +static zip_cdir_t * +_zip_read_eocd64(zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error) { + zip_cdir_t *cd; + zip_uint64_t offset; + zip_uint8_t eocd[EOCD64LEN]; + zip_uint64_t eocd_offset; + zip_uint64_t size, nentry, i, eocdloc_offset; + bool free_buffer; + zip_uint32_t num_disks, num_disks64, eocd_disk, eocd_disk64; + + eocdloc_offset = _zip_buffer_offset(buffer); + + _zip_buffer_get(buffer, 4); /* magic already verified */ + + num_disks = _zip_buffer_get_16(buffer); + eocd_disk = _zip_buffer_get_16(buffer); + eocd_offset = _zip_buffer_get_64(buffer); + + /* valid seek value for start of EOCD */ + if (eocd_offset > ZIP_INT64_MAX) { + zip_error_set(error, ZIP_ER_SEEK, EFBIG); + return NULL; + } + + /* does EOCD fit before EOCD locator? */ + if (eocd_offset + EOCD64LEN > eocdloc_offset + buf_offset) { + zip_error_set(error, ZIP_ER_INCONS, 0); + return NULL; + } + + /* make sure current position of buffer is beginning of EOCD */ + if (eocd_offset >= buf_offset && eocd_offset + EOCD64LEN <= buf_offset + _zip_buffer_size(buffer)) { + _zip_buffer_set_offset(buffer, eocd_offset - buf_offset); + free_buffer = false; + } + else { + if (zip_source_seek(src, (zip_int64_t)eocd_offset, SEEK_SET) < 0) { + _zip_error_set_from_source(error, src); + return NULL; + } + if ((buffer = _zip_buffer_new_from_source(src, EOCD64LEN, eocd, error)) == NULL) { + return NULL; + } + free_buffer = true; + } + + if (memcmp(_zip_buffer_get(buffer, 4), EOCD64_MAGIC, 4) != 0) { + zip_error_set(error, ZIP_ER_INCONS, 0); + if (free_buffer) { + _zip_buffer_free(buffer); + } + return NULL; + } + + /* size of EOCD */ + size = _zip_buffer_get_64(buffer); + + /* is there a hole between EOCD and EOCD locator, or do they overlap? */ + if ((flags & ZIP_CHECKCONS) && size + eocd_offset + 12 != buf_offset + eocdloc_offset) { + zip_error_set(error, ZIP_ER_INCONS, 0); + if (free_buffer) { + _zip_buffer_free(buffer); + } + return NULL; + } + + _zip_buffer_get(buffer, 4); /* skip version made by/needed */ + + num_disks64 = _zip_buffer_get_32(buffer); + eocd_disk64 = _zip_buffer_get_32(buffer); + + /* if eocd values are 0xffff, we have to use eocd64 values. + otherwise, if the values are not the same, it's inconsistent; + in any case, if the value is not 0, we don't support it */ + if (num_disks == 0xffff) { + num_disks = num_disks64; + } + if (eocd_disk == 0xffff) { + eocd_disk = eocd_disk64; + } + if ((flags & ZIP_CHECKCONS) && (eocd_disk != eocd_disk64 || num_disks != num_disks64)) { + zip_error_set(error, ZIP_ER_INCONS, 0); + if (free_buffer) { + _zip_buffer_free(buffer); + } + return NULL; + } + if (num_disks != 0 || eocd_disk != 0) { + zip_error_set(error, ZIP_ER_MULTIDISK, 0); + if (free_buffer) { + _zip_buffer_free(buffer); + } + return NULL; + } + + nentry = _zip_buffer_get_64(buffer); + i = _zip_buffer_get_64(buffer); + + if (nentry != i) { + zip_error_set(error, ZIP_ER_MULTIDISK, 0); + if (free_buffer) { + _zip_buffer_free(buffer); + } + return NULL; + } + + size = _zip_buffer_get_64(buffer); + offset = _zip_buffer_get_64(buffer); + + /* did we read past the end of the buffer? */ + if (!_zip_buffer_ok(buffer)) { + zip_error_set(error, ZIP_ER_INTERNAL, 0); + if (free_buffer) { + _zip_buffer_free(buffer); + } + return NULL; + } + + if (free_buffer) { + _zip_buffer_free(buffer); + } + + if (offset > ZIP_INT64_MAX || offset + size < offset) { + zip_error_set(error, ZIP_ER_SEEK, EFBIG); + return NULL; + } + if (offset + size > buf_offset + eocd_offset) { + /* cdir spans past EOCD record */ + zip_error_set(error, ZIP_ER_INCONS, 0); + return NULL; + } + if ((flags & ZIP_CHECKCONS) && offset + size != buf_offset + eocd_offset) { + zip_error_set(error, ZIP_ER_INCONS, 0); + return NULL; + } + + if (nentry > size / CDENTRYSIZE) { + zip_error_set(error, ZIP_ER_INCONS, 0); + return NULL; + } + + if ((cd = _zip_cdir_new(nentry, error)) == NULL) + return NULL; + + cd->is_zip64 = true; + cd->size = size; + cd->offset = offset; + + return cd; +} diff --git a/ext/libzip/zip_pkware.c b/ext/libzip/zip_pkware.c new file mode 100644 index 0000000000..eb12b1e948 --- /dev/null +++ b/ext/libzip/zip_pkware.c @@ -0,0 +1,112 @@ +/* + zip_pkware.c -- Traditional PKWARE de/encryption backend routines + Copyright (C) 2009-2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include +#include + +#include "zipint.h" + +#define PKWARE_KEY0 305419896 +#define PKWARE_KEY1 591751049 +#define PKWARE_KEY2 878082192 + + +static void +update_keys(zip_pkware_keys_t *keys, zip_uint8_t b) { + keys->key[0] = (zip_uint32_t)crc32(keys->key[0] ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL; + keys->key[1] = (keys->key[1] + (keys->key[0] & 0xff)) * 134775813 + 1; + b = (zip_uint8_t)(keys->key[1] >> 24); + keys->key[2] = (zip_uint32_t)crc32(keys->key[2] ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL; +} + + +static zip_uint8_t +crypt_byte(zip_pkware_keys_t *keys) { + zip_uint16_t tmp; + tmp = (zip_uint16_t)(keys->key[2] | 2); + tmp = (zip_uint16_t)(((zip_uint32_t)tmp * (tmp ^ 1)) >> 8); + return (zip_uint8_t)tmp; +} + + +void +_zip_pkware_keys_reset(zip_pkware_keys_t *keys) { + keys->key[0] = PKWARE_KEY0; + keys->key[1] = PKWARE_KEY1; + keys->key[2] = PKWARE_KEY2; +} + + +void +_zip_pkware_encrypt(zip_pkware_keys_t *keys, zip_uint8_t *out, const zip_uint8_t *in, zip_uint64_t len) { + zip_uint64_t i; + zip_uint8_t b; + zip_uint8_t tmp; + + for (i = 0; i < len; i++) { + b = in[i]; + + if (out != NULL) { + tmp = crypt_byte(keys); + update_keys(keys, b); + b ^= tmp; + out[i] = b; + } + else { + /* during initialization, we're only interested in key updates */ + update_keys(keys, b); + } + } +} + + +void +_zip_pkware_decrypt(zip_pkware_keys_t *keys, zip_uint8_t *out, const zip_uint8_t *in, zip_uint64_t len) { + zip_uint64_t i; + zip_uint8_t b; + zip_uint8_t tmp; + + for (i = 0; i < len; i++) { + b = in[i]; + + /* during initialization, we're only interested in key updates */ + if (out != NULL) { + tmp = crypt_byte(keys); + b ^= tmp; + out[i] = b; + } + + update_keys(keys, b); + } +} diff --git a/ext/libzip/zip_progress.c b/ext/libzip/zip_progress.c new file mode 100644 index 0000000000..ddb47c70a5 --- /dev/null +++ b/ext/libzip/zip_progress.c @@ -0,0 +1,293 @@ +/* + zip_progress.c -- progress reporting + Copyright (C) 2017-2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include + + +#define _ZIP_COMPILING_DEPRECATED +#include "zipint.h" + +struct zip_progress { + zip_t *za; + + zip_progress_callback callback_progress; + void (*ud_progress_free)(void *); + void *ud_progress; + + zip_cancel_callback callback_cancel; + void (*ud_cancel_free)(void *); + void *ud_cancel; + + double precision; + + /* state */ + double last_update; /* last value callback function was called with */ + + double start; /* start of sub-progress section */ + double end; /* end of sub-progress section */ +}; + +static void _zip_progress_free_cancel_callback(zip_progress_t *progress); +static void _zip_progress_free_progress_callback(zip_progress_t *progress); +static zip_progress_t *_zip_progress_new(zip_t *za); +static void _zip_progress_set_cancel_callback(zip_progress_t *progress, zip_cancel_callback callback, void (*ud_free)(void *), void *ud); +static void _zip_progress_set_progress_callback(zip_progress_t *progress, double precision, zip_progress_callback callback, void (*ud_free)(void *), void *ud); + +void +_zip_progress_end(zip_progress_t *progress) { + _zip_progress_update(progress, 1.0); +} + + +void +_zip_progress_free(zip_progress_t *progress) { + if (progress == NULL) { + return; + } + + _zip_progress_free_progress_callback(progress); + _zip_progress_free_cancel_callback(progress); + + free(progress); +} + + +static zip_progress_t * +_zip_progress_new(zip_t *za) { + zip_progress_t *progress = (zip_progress_t *)malloc(sizeof(*progress)); + + if (progress == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return NULL; + } + + progress->za = za; + + progress->callback_progress = NULL; + progress->ud_progress_free = NULL; + progress->ud_progress = NULL; + progress->precision = 0.0; + + progress->callback_cancel = NULL; + progress->ud_cancel_free = NULL; + progress->ud_cancel = NULL; + + return progress; +} + +static void +_zip_progress_free_progress_callback(zip_progress_t *progress) { + if (progress->ud_progress_free) { + progress->ud_progress_free(progress->ud_progress); + } + + progress->callback_progress = NULL; + progress->ud_progress = NULL; + progress->ud_progress_free = NULL; +} + +static void +_zip_progress_free_cancel_callback(zip_progress_t *progress) { + if (progress->ud_cancel_free) { + progress->ud_cancel_free(progress->ud_cancel); + } + + progress->callback_cancel = NULL; + progress->ud_cancel = NULL; + progress->ud_cancel_free = NULL; +} + +static void +_zip_progress_set_progress_callback(zip_progress_t *progress, double precision, zip_progress_callback callback, void (*ud_free)(void *), void *ud) { + _zip_progress_free_progress_callback(progress); + + progress->callback_progress = callback; + progress->ud_progress_free = ud_free; + progress->ud_progress = ud; + progress->precision = precision; +} + +void +_zip_progress_set_cancel_callback(zip_progress_t *progress, zip_cancel_callback callback, void (*ud_free)(void *), void *ud) { + _zip_progress_free_cancel_callback(progress); + + progress->callback_cancel = callback; + progress->ud_cancel_free = ud_free; + progress->ud_cancel = ud; +} + +int +_zip_progress_start(zip_progress_t *progress) { + if (progress == NULL) { + return 0; + } + + if (progress->callback_progress != NULL) { + progress->last_update = 0.0; + progress->callback_progress(progress->za, 0.0, progress->ud_progress); + } + + if (progress->callback_cancel != NULL) { + if (progress->callback_cancel(progress->za, progress->ud_cancel)) { + return -1; + } + } + + return 0; +} + + +int +_zip_progress_subrange(zip_progress_t *progress, double start, double end) { + if (progress == NULL) { + return 0; + } + + progress->start = start; + progress->end = end; + + return _zip_progress_update(progress, 0.0); +} + +int +_zip_progress_update(zip_progress_t *progress, double sub_current) { + double current; + + if (progress == NULL) { + return 0; + } + + if (progress->callback_progress != NULL) { + current = ZIP_MIN(ZIP_MAX(sub_current, 0.0), 1.0) * (progress->end - progress->start) + progress->start; + + if (current - progress->last_update > progress->precision) { + progress->callback_progress(progress->za, current, progress->ud_progress); + progress->last_update = current; + } + } + + if (progress->callback_cancel != NULL) { + if (progress->callback_cancel(progress->za, progress->ud_cancel)) { + return -1; + } + } + + return 0; +} + + +ZIP_EXTERN int +zip_register_progress_callback_with_state(zip_t *za, double precision, zip_progress_callback callback, void (*ud_free)(void *), void *ud) { + if (callback != NULL) { + if (za->progress == NULL) { + if ((za->progress = _zip_progress_new(za)) == NULL) { + return -1; + } + } + + _zip_progress_set_progress_callback(za->progress, precision, callback, ud_free, ud); + } + else { + if (za->progress != NULL) { + if (za->progress->callback_cancel == NULL) { + _zip_progress_free(za->progress); + za->progress = NULL; + } + else { + _zip_progress_free_progress_callback(za->progress); + } + } + } + + return 0; +} + + +ZIP_EXTERN int +zip_register_cancel_callback_with_state(zip_t *za, zip_cancel_callback callback, void (*ud_free)(void *), void *ud) { + if (callback != NULL) { + if (za->progress == NULL) { + if ((za->progress = _zip_progress_new(za)) == NULL) { + return -1; + } + } + + _zip_progress_set_cancel_callback(za->progress, callback, ud_free, ud); + } + else { + if (za->progress != NULL) { + if (za->progress->callback_progress == NULL) { + _zip_progress_free(za->progress); + za->progress = NULL; + } + else { + _zip_progress_free_cancel_callback(za->progress); + } + } + } + + return 0; +} + + +struct legacy_ud { + zip_progress_callback_t callback; +}; + + +static void +_zip_legacy_progress_callback(zip_t *za, double progress, void *vud) { + struct legacy_ud *ud = (struct legacy_ud *)vud; + + ud->callback(progress); +} + +ZIP_EXTERN void +zip_register_progress_callback(zip_t *za, zip_progress_callback_t progress_callback) { + struct legacy_ud *ud; + + if (progress_callback == NULL) { + zip_register_progress_callback_with_state(za, 0, NULL, NULL, NULL); + } + + if ((ud = (struct legacy_ud *)malloc(sizeof(*ud))) == NULL) { + return; + } + + ud->callback = progress_callback; + + if (zip_register_progress_callback_with_state(za, 0.001, _zip_legacy_progress_callback, free, ud) < 0) { + free(ud); + } +} diff --git a/ext/libzip/zip_random_unix.c b/ext/libzip/zip_random_unix.c new file mode 100644 index 0000000000..1d0c9674a7 --- /dev/null +++ b/ext/libzip/zip_random_unix.c @@ -0,0 +1,104 @@ +/* + zip_random_unix.c -- fill the user's buffer with random stuff (Unix version) + Copyright (C) 2016-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "zipint.h" + +#ifdef HAVE_CRYPTO +#include "zip_crypto.h" +#endif + +#ifdef HAVE_ARC4RANDOM + +#include + +#ifndef HAVE_SECURE_RANDOM +ZIP_EXTERN bool +zip_secure_random(zip_uint8_t *buffer, zip_uint16_t length) { + arc4random_buf(buffer, length); + return true; +} +#endif + +#ifndef HAVE_RANDOM_UINT32 +zip_uint32_t +zip_random_uint32(void) { + return arc4random(); +} +#endif + +#else /* HAVE_ARC4RANDOM */ + +#ifndef HAVE_SECURE_RANDOM +#include +#include + +ZIP_EXTERN bool +zip_secure_random(zip_uint8_t *buffer, zip_uint16_t length) { + int fd; + + if ((fd = open("/dev/urandom", O_RDONLY)) < 0) { + return false; + } + + if (read(fd, buffer, length) != length) { + close(fd); + return false; + } + + close(fd); + return true; +} +#endif + +#ifndef HAVE_RANDOM_UINT32 +#include + +zip_uint32_t +zip_random_uint32(void) { + static bool seeded = false; + + zip_uint32_t value; + + if (zip_secure_random((zip_uint8_t *)&value, sizeof(value))) { + return value; + } + + if (!seeded) { + srandom((unsigned int)time(NULL)); + } + + return (zip_uint32_t)random(); +} +#endif + +#endif /* HAVE_ARC4RANDOM */ diff --git a/ext/libzip/zip_random_uwp.c b/ext/libzip/zip_random_uwp.c new file mode 100644 index 0000000000..70c998cfa4 --- /dev/null +++ b/ext/libzip/zip_random_uwp.c @@ -0,0 +1,82 @@ +/* + zip_random_uwp.c -- fill the user's buffer with random stuff (UWP version) + Copyright (C) 2017-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "zipint.h" + +#ifdef HAVE_CRYPTO +#include "zip_crypto.h" +#endif + +#ifndef HAVE_SECURE_RANDOM + +#include +#include +#include + +ZIP_EXTERN bool +zip_secure_random(zip_uint8_t *buffer, zip_uint16_t length) { + BCRYPT_ALG_HANDLE hAlg = NULL; + NTSTATUS hr = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); + if (hr != STATUS_SUCCESS || hAlg == NULL) { + return false; + } + hr = BCryptGenRandom(&hAlg, buffer, length, 0); + BCryptCloseAlgorithmProvider(&hAlg, 0); + if (hr != STATUS_SUCCESS) { + return false; + } + return true; +} + +#endif + +#ifndef HAVE_RANDOM_UINT32 +#include + +zip_uint32_t +zip_random_uint32(void) { + static bool seeded = false; + + zip_uint32_t value; + + if (zip_secure_random((zip_uint8_t *)&value, sizeof(value))) { + return value; + } + + if (!seeded) { + srand((unsigned int)time(NULL)); + } + + return (zip_uint32_t)rand(); +} +#endif diff --git a/ext/libzip/zip_entry_new.c b/ext/libzip/zip_random_win32.c similarity index 62% rename from ext/libzip/zip_entry_new.c rename to ext/libzip/zip_random_win32.c index 7059b1b060..19d488eced 100644 --- a/ext/libzip/zip_entry_new.c +++ b/ext/libzip/zip_random_win32.c @@ -1,6 +1,6 @@ /* - zip_entry_new.c -- create and init struct zip_entry - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + zip_random_win32.c -- fill the user's buffer with random stuff (Windows version) + Copyright (C) 2016-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,48 +31,51 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - - -#include - #include "zipint.h" - +#ifdef HAVE_CRYPTO +#include "zip_crypto.h" +#endif -struct zip_entry * -_zip_entry_new(struct zip *za) -{ - struct zip_entry *ze; - if (!za) { - ze = (struct zip_entry *)malloc(sizeof(struct zip_entry)); - if (!ze) { - _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); - return NULL; - } +#include + +#ifndef HAVE_SECURE_RANDOM + +#include + +ZIP_EXTERN bool +zip_secure_random(zip_uint8_t *buffer, zip_uint16_t length) { + HCRYPTPROV hprov; + if (!CryptAcquireContext(&hprov, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { + return false; } - else { - if (za->nentry >= za->nentry_alloc-1) { - za->nentry_alloc += 16; - za->entry = (struct zip_entry *)realloc(za->entry, - sizeof(struct zip_entry) - * za->nentry_alloc); - if (!za->entry) { - _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); - return NULL; - } - } - ze = za->entry+za->nentry; + if (!CryptGenRandom(hprov, length, buffer)) { + return false; } - - ze->state = ZIP_ST_UNCHANGED; - - ze->ch_filename = NULL; - ze->ch_comment = NULL; - ze->ch_comment_len = -1; - ze->source = NULL; - - if (za) - za->nentry++; - - return ze; + if (!CryptReleaseContext(hprov, 0)) { + return false; + } + return true; } +#endif + +#ifndef HAVE_RANDOM_UINT32 +#include + +zip_uint32_t +zip_random_uint32(void) { + static bool seeded = false; + + zip_uint32_t value; + + if (zip_secure_random((zip_uint8_t *)&value, sizeof(value))) { + return value; + } + + if (!seeded) { + srand((unsigned int)time(NULL)); + } + + return (zip_uint32_t)rand(); +} +#endif diff --git a/ext/libzip/zip_rename.c b/ext/libzip/zip_rename.c index 1d056bbe37..75b456de56 100644 --- a/ext/libzip/zip_rename.c +++ b/ext/libzip/zip_rename.c @@ -1,6 +1,6 @@ /* zip_rename.c -- rename file in zip archive - Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,35 +31,12 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - - -#include +#define _ZIP_COMPILING_DEPRECATED #include "zipint.h" - ZIP_EXTERN int -zip_rename(struct zip *za, int idx, const char *name) -{ - const char *old_name; - int old_is_dir, new_is_dir; - - if (idx >= za->nentry || idx < 0 || name[0] == '\0') { - _zip_error_set(&za->error, ZIP_ER_INVAL, 0); - return -1; - } - - if ((old_name=zip_get_name(za, idx, 0)) == NULL) - return -1; - - new_is_dir = (name[strlen(name)-1] == '/'); - old_is_dir = (old_name[strlen(old_name)-1] == '/'); - - if (new_is_dir != old_is_dir) { - _zip_error_set(&za->error, ZIP_ER_INVAL, 0); - return -1; - } - - return _zip_set_name(za, idx, name); +zip_rename(zip_t *za, zip_uint64_t idx, const char *name) { + return zip_file_rename(za, idx, name, 0); } diff --git a/ext/libzip/zip_replace.c b/ext/libzip/zip_replace.c index 6cdb80c503..30a2eb1d63 100644 --- a/ext/libzip/zip_replace.c +++ b/ext/libzip/zip_replace.c @@ -1,6 +1,6 @@ /* zip_replace.c -- replace file via callback function - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,48 +31,12 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - +#define _ZIP_COMPILING_DEPRECATED #include "zipint.h" - ZIP_EXTERN int -zip_replace(struct zip *za, int idx, struct zip_source *source) -{ - if (idx < 0 || idx >= za->nentry || source == NULL) { - _zip_error_set(&za->error, ZIP_ER_INVAL, 0); - return -1; - } - - if (_zip_replace(za, idx, NULL, source) == -1) - return -1; - - return 0; -} - - - - -int -_zip_replace(struct zip *za, int idx, const char *name, - struct zip_source *source) -{ - if (idx == -1) { - if (_zip_entry_new(za) == NULL) - return -1; - - idx = za->nentry - 1; - } - - _zip_unchange_data(za->entry+idx); - - if (name && _zip_set_name(za, idx, name) != 0) - return -1; - - za->entry[idx].state = ((za->cdir == NULL || idx >= za->cdir->nentry) - ? ZIP_ST_ADDED : ZIP_ST_REPLACED); - za->entry[idx].source = source; - - return idx; +zip_replace(zip_t *za, zip_uint64_t idx, zip_source_t *source) { + return zip_file_replace(za, idx, source, 0); } diff --git a/ext/libzip/zip_set_archive_comment.c b/ext/libzip/zip_set_archive_comment.c index 343fa5491c..3f050b3eaa 100644 --- a/ext/libzip/zip_set_archive_comment.c +++ b/ext/libzip/zip_set_archive_comment.c @@ -1,6 +1,6 @@ /* zip_set_archive_comment.c -- set archive comment - Copyright (C) 2006-2007 Dieter Baron and Thomas Klausner + Copyright (C) 2006-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,35 +31,50 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include "zipint.h" - ZIP_EXTERN int -zip_set_archive_comment(struct zip *za, const char *comment, int len) -{ - char *tmpcom; +zip_set_archive_comment(zip_t *za, const char *comment, zip_uint16_t len) { + zip_string_t *cstr; - if (len < 0 || len > MAXCOMLEN - || (len > 0 && comment == NULL)) { - _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + if (ZIP_IS_RDONLY(za)) { + zip_error_set(&za->error, ZIP_ER_RDONLY, 0); + return -1; + } + + if (len > 0 && comment == NULL) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); return -1; } if (len > 0) { - if ((tmpcom=(char *)_zip_memdup(comment, len, &za->error)) == NULL) + if ((cstr = _zip_string_new((const zip_uint8_t *)comment, len, ZIP_FL_ENC_GUESS, &za->error)) == NULL) return -1; + + if (_zip_guess_encoding(cstr, ZIP_ENCODING_UNKNOWN) == ZIP_ENCODING_CP437) { + _zip_string_free(cstr); + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } } else - tmpcom = NULL; + cstr = NULL; + + _zip_string_free(za->comment_changes); + za->comment_changes = NULL; + + if (((za->comment_orig && _zip_string_equal(za->comment_orig, cstr)) || (za->comment_orig == NULL && cstr == NULL))) { + _zip_string_free(cstr); + za->comment_changed = 0; + } + else { + za->comment_changes = cstr; + za->comment_changed = 1; + } - free(za->ch_comment); - za->ch_comment = tmpcom; - za->ch_comment_len = len; - return 0; } diff --git a/ext/libzip/zip_set_archive_flag.c b/ext/libzip/zip_set_archive_flag.c index 720e1f3508..e0d550212f 100644 --- a/ext/libzip/zip_set_archive_flag.c +++ b/ext/libzip/zip_set_archive_flag.c @@ -1,6 +1,6 @@ /* zip_get_archive_flag.c -- set archive global flag - Copyright (C) 2008 Dieter Baron and Thomas Klausner + Copyright (C) 2008-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,19 +31,35 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "zipint.h" - ZIP_EXTERN int -zip_set_archive_flag(struct zip *za, int flag, int value) -{ +zip_set_archive_flag(zip_t *za, zip_flags_t flag, int value) { + unsigned int new_flags; + if (value) - za->ch_flags |= flag; + new_flags = za->ch_flags | flag; else - za->ch_flags &= ~flag; + new_flags = za->ch_flags & ~flag; + + if (new_flags == za->ch_flags) + return 0; + + if (ZIP_IS_RDONLY(za)) { + zip_error_set(&za->error, ZIP_ER_RDONLY, 0); + return -1; + } + + if ((flag & ZIP_AFL_RDONLY) && value && (za->ch_flags & ZIP_AFL_RDONLY) == 0) { + if (_zip_changed(za, NULL)) { + zip_error_set(&za->error, ZIP_ER_CHANGED, 0); + return -1; + } + } + + za->ch_flags = new_flags; return 0; } diff --git a/ext/libzip/zip_set_default_password.c b/ext/libzip/zip_set_default_password.c new file mode 100644 index 0000000000..41c7c51969 --- /dev/null +++ b/ext/libzip/zip_set_default_password.c @@ -0,0 +1,58 @@ +/* + zip_set_default_password.c -- set default password for decryption + Copyright (C) 2009-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include +#include + +#include "zipint.h" + + +ZIP_EXTERN int +zip_set_default_password(zip_t *za, const char *passwd) { + if (za == NULL) + return -1; + + free(za->default_password); + + if (passwd) { + if ((za->default_password = strdup(passwd)) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + } + else + za->default_password = NULL; + + return 0; +} diff --git a/ext/libzip/zip_set_file_comment.c b/ext/libzip/zip_set_file_comment.c index bc1938e1df..4277893f23 100644 --- a/ext/libzip/zip_set_file_comment.c +++ b/ext/libzip/zip_set_file_comment.c @@ -1,6 +1,6 @@ /* zip_set_file_comment.c -- set comment for file in archive - Copyright (C) 2006-2007 Dieter Baron and Thomas Klausner + Copyright (C) 2006-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,36 +31,16 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - - -#include +#define _ZIP_COMPILING_DEPRECATED #include "zipint.h" - ZIP_EXTERN int -zip_set_file_comment(struct zip *za, int idx, const char *comment, int len) -{ - char *tmpcom; - - if (idx < 0 || idx >= za->nentry - || len < 0 || len > MAXCOMLEN - || (len > 0 && comment == NULL)) { - _zip_error_set(&za->error, ZIP_ER_INVAL, 0); +zip_set_file_comment(zip_t *za, zip_uint64_t idx, const char *comment, int len) { + if (len < 0 || len > ZIP_UINT16_MAX) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); return -1; } - - if (len > 0) { - if ((tmpcom=(char *)_zip_memdup(comment, len, &za->error)) == NULL) - return -1; - } - else - tmpcom = NULL; - - free(za->entry[idx].ch_comment); - za->entry[idx].ch_comment = tmpcom; - za->entry[idx].ch_comment_len = len; - - return 0; + return zip_file_set_comment(za, idx, comment, (zip_uint16_t)len, 0); } diff --git a/ext/libzip/zip_set_file_compression.c b/ext/libzip/zip_set_file_compression.c new file mode 100644 index 0000000000..927fbb7682 --- /dev/null +++ b/ext/libzip/zip_set_file_compression.c @@ -0,0 +1,91 @@ +/* + zip_set_file_compression.c -- set compression for file in archive + Copyright (C) 2012-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + + +ZIP_EXTERN int +zip_set_file_compression(zip_t *za, zip_uint64_t idx, zip_int32_t method, zip_uint32_t flags) { + zip_entry_t *e; + zip_int32_t old_method; + + if (idx >= za->nentry || flags > 9) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (ZIP_IS_RDONLY(za)) { + zip_error_set(&za->error, ZIP_ER_RDONLY, 0); + return -1; + } + + if (!zip_compression_method_supported(method, true)) { + zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0); + return -1; + } + + e = za->entry + idx; + + old_method = (e->orig == NULL ? ZIP_CM_DEFAULT : e->orig->comp_method); + + /* TODO: do we want to recompress if level is set? Only if it's + * different than what bit flags tell us, but those are not + * defined for all compression methods, or not directly mappable + * to levels */ + + if (method == old_method) { + if (e->changes) { + e->changes->changed &= ~ZIP_DIRENT_COMP_METHOD; + e->changes->compression_level = 0; + if (e->changes->changed == 0) { + _zip_dirent_free(e->changes); + e->changes = NULL; + } + } + } + else { + if (e->changes == NULL) { + if ((e->changes = _zip_dirent_clone(e->orig)) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + } + + e->changes->comp_method = method; + e->changes->compression_level = (zip_uint16_t)flags; + e->changes->changed |= ZIP_DIRENT_COMP_METHOD; + } + + return 0; +} diff --git a/ext/libzip/zip_set_name.c b/ext/libzip/zip_set_name.c index 65b60a65cb..43c772e48a 100644 --- a/ext/libzip/zip_set_name.c +++ b/ext/libzip/zip_set_name.c @@ -1,6 +1,6 @@ /* zip_set_name.c -- rename helper function - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,48 +31,127 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef _MSC_VER -#pragma warning (disable:4996) -#pragma warning (disable:4244) -#endif #include #include #include "zipint.h" - int -_zip_set_name(struct zip *za, int idx, const char *name) -{ - char *s; - int i; - - if (idx < 0 || idx >= za->nentry || name == NULL) { - _zip_error_set(&za->error, ZIP_ER_INVAL, 0); +_zip_set_name(zip_t *za, zip_uint64_t idx, const char *name, zip_flags_t flags) { + zip_entry_t *e; + zip_string_t *str; + bool same_as_orig; + zip_int64_t i; + const zip_uint8_t *old_name, *new_name; + zip_string_t *old_str; + + if (idx >= za->nentry) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); return -1; } - if ((i=_zip_name_locate(za, name, 0, NULL)) != -1 && i != idx) { - _zip_error_set(&za->error, ZIP_ER_EXISTS, 0); + if (ZIP_IS_RDONLY(za)) { + zip_error_set(&za->error, ZIP_ER_RDONLY, 0); + return -1; + } + + if (name && name[0] != '\0') { + /* TODO: check for string too long */ + if ((str = _zip_string_new((const zip_uint8_t *)name, (zip_uint16_t)strlen(name), flags, &za->error)) == NULL) + return -1; + if ((flags & ZIP_FL_ENCODING_ALL) == ZIP_FL_ENC_GUESS && _zip_guess_encoding(str, ZIP_ENCODING_UNKNOWN) == ZIP_ENCODING_UTF8_GUESSED) + str->encoding = ZIP_ENCODING_UTF8_KNOWN; + } + else + str = NULL; + + /* TODO: encoding flags needed for CP437? */ + if ((i = _zip_name_locate(za, name, 0, NULL)) >= 0 && (zip_uint64_t)i != idx) { + _zip_string_free(str); + zip_error_set(&za->error, ZIP_ER_EXISTS, 0); return -1; } /* no effective name change */ - if (i == idx) + if (i >= 0 && (zip_uint64_t)i == idx) { + _zip_string_free(str); return 0; - - if ((s=strdup(name)) == NULL) { - _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + } + + e = za->entry + idx; + + if (e->orig) + same_as_orig = _zip_string_equal(e->orig->filename, str); + else + same_as_orig = false; + + if (!same_as_orig && e->changes == NULL) { + if ((e->changes = _zip_dirent_clone(e->orig)) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + _zip_string_free(str); + return -1; + } + } + + if ((new_name = _zip_string_get(same_as_orig ? e->orig->filename : str, NULL, 0, &za->error)) == NULL) { + _zip_string_free(str); return -1; } - - if (za->entry[idx].state == ZIP_ST_UNCHANGED) - za->entry[idx].state = ZIP_ST_RENAMED; - free(za->entry[idx].ch_filename); - za->entry[idx].ch_filename = s; + if (e->changes) { + old_str = e->changes->filename; + } + else if (e->orig) { + old_str = e->orig->filename; + } + else { + old_str = NULL; + } + + if (old_str) { + if ((old_name = _zip_string_get(old_str, NULL, 0, &za->error)) == NULL) { + _zip_string_free(str); + return -1; + } + } + else { + old_name = NULL; + } + + if (_zip_hash_add(za->names, new_name, idx, 0, &za->error) == false) { + _zip_string_free(str); + return -1; + } + if (old_name) { + _zip_hash_delete(za->names, old_name, NULL); + } + + if (same_as_orig) { + if (e->changes) { + if (e->changes->changed & ZIP_DIRENT_FILENAME) { + _zip_string_free(e->changes->filename); + e->changes->changed &= ~ZIP_DIRENT_FILENAME; + if (e->changes->changed == 0) { + _zip_dirent_free(e->changes); + e->changes = NULL; + } + else { + /* TODO: what if not cloned? can that happen? */ + e->changes->filename = e->orig->filename; + } + } + } + _zip_string_free(str); + } + else { + if (e->changes->changed & ZIP_DIRENT_FILENAME) { + _zip_string_free(e->changes->filename); + } + e->changes->changed |= ZIP_DIRENT_FILENAME; + e->changes->filename = str; + } return 0; } diff --git a/ext/libzip/zip_source_accept_empty.c b/ext/libzip/zip_source_accept_empty.c new file mode 100644 index 0000000000..e6d515168c --- /dev/null +++ b/ext/libzip/zip_source_accept_empty.c @@ -0,0 +1,52 @@ +/* + zip_source_accept_empty.c -- if empty source is a valid archive + Copyright (C) 2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + + +bool +zip_source_accept_empty(zip_source_t *src) { + int ret; + + if ((zip_source_supports(src) & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_ACCEPT_EMPTY)) == 0) { + if (ZIP_SOURCE_IS_LAYERED(src)) { + return zip_source_accept_empty(src->src); + } + return true; + } + + ret = (int)_zip_source_call(src, NULL, 0, ZIP_SOURCE_ACCEPT_EMPTY); + + return ret != 0; +} diff --git a/ext/libzip/zip_source_begin_write.c b/ext/libzip/zip_source_begin_write.c new file mode 100644 index 0000000000..432c2c15a8 --- /dev/null +++ b/ext/libzip/zip_source_begin_write.c @@ -0,0 +1,52 @@ +/* + zip_source_begin_write.c -- start a new file for writing + Copyright (C) 2014-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + + +ZIP_EXTERN int +zip_source_begin_write(zip_source_t *src) { + if (ZIP_SOURCE_IS_OPEN_WRITING(src)) { + zip_error_set(&src->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (_zip_source_call(src, NULL, 0, ZIP_SOURCE_BEGIN_WRITE) < 0) { + return -1; + } + + src->write_state = ZIP_SOURCE_WRITE_OPEN; + + return 0; +} diff --git a/ext/libzip/zip_source_begin_write_cloning.c b/ext/libzip/zip_source_begin_write_cloning.c new file mode 100644 index 0000000000..41b4c73c3d --- /dev/null +++ b/ext/libzip/zip_source_begin_write_cloning.c @@ -0,0 +1,52 @@ +/* + zip_source_begin_write_cloning.c -- clone part of file for writing + Copyright (C) 2017-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + + +ZIP_EXTERN int +zip_source_begin_write_cloning(zip_source_t *src, zip_uint64_t offset) { + if (ZIP_SOURCE_IS_OPEN_WRITING(src)) { + zip_error_set(&src->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (_zip_source_call(src, NULL, offset, ZIP_SOURCE_BEGIN_WRITE_CLONING) < 0) { + return -1; + } + + src->write_state = ZIP_SOURCE_WRITE_OPEN; + + return 0; +} diff --git a/ext/libzip/zip_source_buffer.c b/ext/libzip/zip_source_buffer.c index 1f24a1642e..519d2c30da 100644 --- a/ext/libzip/zip_source_buffer.c +++ b/ext/libzip/zip_source_buffer.c @@ -1,6 +1,6 @@ /* zip_source_buffer.c -- create zip data source from buffer - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,127 +31,590 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - - #include #include #include "zipint.h" -struct read_data { - const char *buf, *data, *end; - time_t mtime; - int freep; +#ifndef WRITE_FRAGMENT_SIZE +#define WRITE_FRAGMENT_SIZE (64 * 1024) +#endif + +struct buffer { + zip_buffer_fragment_t *fragments; /* fragments */ + zip_uint64_t *fragment_offsets; /* offset of each fragment from start of buffer, nfragments+1 entries */ + zip_uint64_t nfragments; /* number of allocated fragments */ + zip_uint64_t fragments_capacity; /* size of fragments (number of pointers) */ + + zip_uint64_t first_owned_fragment; /* first fragment to free data from */ + + zip_uint64_t shared_fragments; /* number of shared fragments */ + struct buffer *shared_buffer; /* buffer fragments are shared with */ + zip_uint64_t size; /* size of buffer */ + + zip_uint64_t offset; /* current offset in buffer */ + zip_uint64_t current_fragment; /* fragment current offset is in */ }; -static ssize_t read_data(void *state, void *data, size_t len, - enum zip_source_cmd cmd); +typedef struct buffer buffer_t; - +struct read_data { + zip_error_t error; + time_t mtime; + zip_file_attributes_t attributes; + buffer_t *in; + buffer_t *out; +}; -ZIP_EXTERN struct zip_source * -zip_source_buffer(struct zip *za, const void *data, off_t len, int freep) -{ - struct read_data *f; - struct zip_source *zs; +#define buffer_capacity(buffer) ((buffer)->fragment_offsets[(buffer)->nfragments]) +#define buffer_size(buffer) ((buffer)->size) +static buffer_t *buffer_clone(buffer_t *buffer, zip_uint64_t length, zip_error_t *error); +static zip_uint64_t buffer_find_fragment(const buffer_t *buffer, zip_uint64_t offset); +static void buffer_free(buffer_t *buffer); +static bool buffer_grow_fragments(buffer_t *buffer, zip_uint64_t capacity, zip_error_t *error); +static buffer_t *buffer_new(const zip_buffer_fragment_t *fragments, zip_uint64_t nfragments, int free_data, zip_error_t *error); +static zip_int64_t buffer_read(buffer_t *buffer, zip_uint8_t *data, zip_uint64_t length); +static int buffer_seek(buffer_t *buffer, void *data, zip_uint64_t len, zip_error_t *error); +static zip_int64_t buffer_write(buffer_t *buffer, const zip_uint8_t *data, zip_uint64_t length, zip_error_t *); + +static zip_int64_t read_data(void *, void *, zip_uint64_t, zip_source_cmd_t); + +zip_source_t *zip_source_buffer_with_attributes_create(const void *data, zip_uint64_t len, int freep, zip_file_attributes_t *attributes, zip_error_t *error); +zip_source_t *zip_source_buffer_fragment_with_attributes_create(const zip_buffer_fragment_t *fragments, zip_uint64_t nfragments, int freep, zip_file_attributes_t *attributes, zip_error_t *error); + + +ZIP_EXTERN zip_source_t * +zip_source_buffer(zip_t *za, const void *data, zip_uint64_t len, int freep) { if (za == NULL) return NULL; - if (len < 0 || (data == NULL && len > 0)) { - _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return zip_source_buffer_with_attributes_create(data, len, freep, NULL, &za->error); +} + + +ZIP_EXTERN zip_source_t * +zip_source_buffer_create(const void *data, zip_uint64_t len, int freep, zip_error_t *error) { + return zip_source_buffer_with_attributes_create(data, len, freep, NULL, error); +} + + +zip_source_t * +zip_source_buffer_with_attributes_create(const void *data, zip_uint64_t len, int freep, zip_file_attributes_t *attributes, zip_error_t *error) { + zip_buffer_fragment_t fragment; + + if (data == NULL) { + if (len > 0) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + return zip_source_buffer_fragment_with_attributes_create(NULL, 0, freep, attributes, error); + } + + fragment.data = (zip_uint8_t *)data; + fragment.length = len; + + return zip_source_buffer_fragment_with_attributes_create(&fragment, 1, freep, attributes, error); +} + + +ZIP_EXTERN zip_source_t * +zip_source_buffer_fragment(zip_t *za, const zip_buffer_fragment_t *fragments, zip_uint64_t nfragments, int freep) { + if (za == NULL) { return NULL; } - if ((f=(struct read_data *)malloc(sizeof(*f))) == NULL) { - _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return zip_source_buffer_fragment_with_attributes_create(fragments, nfragments, freep, NULL, &za->error); +} + + +ZIP_EXTERN zip_source_t * +zip_source_buffer_fragment_create(const zip_buffer_fragment_t *fragments, zip_uint64_t nfragments, int freep, zip_error_t *error) { + return zip_source_buffer_fragment_with_attributes_create(fragments, nfragments, freep, NULL, error); +} + +zip_source_t * +zip_source_buffer_fragment_with_attributes_create(const zip_buffer_fragment_t *fragments, zip_uint64_t nfragments, int freep, zip_file_attributes_t *attributes, zip_error_t *error) { + struct read_data *ctx; + zip_source_t *zs; + buffer_t *buffer; + + if (fragments == NULL && nfragments > 0) { + zip_error_set(error, ZIP_ER_INVAL, 0); return NULL; } - f->data = (const char *)data; - f->end = ((const char *)data)+len; - f->freep = freep; - f->mtime = time(NULL); - - if ((zs=zip_source_function(za, read_data, f)) == NULL) { - free(f); + if ((buffer = buffer_new(fragments, nfragments, freep, error)) == NULL) { + return NULL; + } + + if ((ctx = (struct read_data *)malloc(sizeof(*ctx))) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + buffer_free(buffer); + return NULL; + } + + ctx->in = buffer; + ctx->out = NULL; + ctx->mtime = time(NULL); + if (attributes) { + memcpy(&ctx->attributes, attributes, sizeof(ctx->attributes)); + } + else { + zip_file_attributes_init(&ctx->attributes); + } + zip_error_init(&ctx->error); + + if ((zs = zip_source_function_create(read_data, ctx, error)) == NULL) { + buffer_free(ctx->in); + free(ctx); return NULL; } return zs; } - -static ssize_t -read_data(void *state, void *data, size_t len, enum zip_source_cmd cmd) -{ - struct read_data *z; - char *buf; - size_t n; +zip_source_t * +zip_source_buffer_with_attributes(zip_t *za, const void *data, zip_uint64_t len, int freep, zip_file_attributes_t *attributes) { + return zip_source_buffer_with_attributes_create(data, len, freep, attributes, &za->error); +} - z = (struct read_data *)state; - buf = (char *)data; +static zip_int64_t +read_data(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd) { + struct read_data *ctx = (struct read_data *)state; switch (cmd) { - case ZIP_SOURCE_OPEN: - z->buf = z->data; - return 0; - - case ZIP_SOURCE_READ: - n = z->end - z->buf; - if (n > len) - n = len; - - if (n) { - memcpy(buf, z->buf, n); - z->buf += n; + case ZIP_SOURCE_BEGIN_WRITE: + if ((ctx->out = buffer_new(NULL, 0, 0, &ctx->error)) == NULL) { + return -1; } + ctx->out->offset = 0; + ctx->out->current_fragment = 0; + return 0; + + case ZIP_SOURCE_BEGIN_WRITE_CLONING: + if ((ctx->out = buffer_clone(ctx->in, len, &ctx->error)) == NULL) { + return -1; + } + ctx->out->offset = len; + ctx->out->current_fragment = ctx->out->nfragments; + return 0; - return n; - case ZIP_SOURCE_CLOSE: return 0; - case ZIP_SOURCE_STAT: - { - struct zip_stat *st; - - if (len < sizeof(*st)) - return -1; - - st = (struct zip_stat *)data; - - zip_stat_init(st); - st->mtime = z->mtime; - st->size = (off_t)(z->end - z->data); - - return sizeof(*st); - } - - case ZIP_SOURCE_ERROR: - { - int *e; - - if (len < sizeof(int)*2) - return -1; - - e = (int *)data; - e[0] = e[1] = 0; - } - return sizeof(int)*2; - - case ZIP_SOURCE_FREE: - if (z->freep) { - free((void *)z->data); - z->data = NULL; - } - free(z); + case ZIP_SOURCE_COMMIT_WRITE: + buffer_free(ctx->in); + ctx->in = ctx->out; + ctx->out = NULL; return 0; - default: - ; + case ZIP_SOURCE_ERROR: + return zip_error_to_data(&ctx->error, data, len); + + case ZIP_SOURCE_FREE: + buffer_free(ctx->in); + buffer_free(ctx->out); + free(ctx); + return 0; + + case ZIP_SOURCE_GET_FILE_ATTRIBUTES: { + if (len < sizeof(ctx->attributes)) { + zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); + return -1; + } + + memcpy(data, &ctx->attributes, sizeof(ctx->attributes)); + + return sizeof(ctx->attributes); } - return -1; + case ZIP_SOURCE_OPEN: + ctx->in->offset = 0; + ctx->in->current_fragment = 0; + return 0; + + case ZIP_SOURCE_READ: + if (len > ZIP_INT64_MAX) { + zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); + return -1; + } + return buffer_read(ctx->in, data, len); + + case ZIP_SOURCE_REMOVE: { + buffer_t *empty = buffer_new(NULL, 0, 0, &ctx->error); + if (empty == NULL) { + return -1; + } + + buffer_free(ctx->in); + ctx->in = empty; + return 0; + } + + case ZIP_SOURCE_ROLLBACK_WRITE: + buffer_free(ctx->out); + ctx->out = NULL; + return 0; + + case ZIP_SOURCE_SEEK: + return buffer_seek(ctx->in, data, len, &ctx->error); + + case ZIP_SOURCE_SEEK_WRITE: + return buffer_seek(ctx->out, data, len, &ctx->error); + + case ZIP_SOURCE_STAT: { + zip_stat_t *st; + + if (len < sizeof(*st)) { + zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); + return -1; + } + + st = (zip_stat_t *)data; + + zip_stat_init(st); + st->mtime = ctx->mtime; + st->size = ctx->in->size; + st->comp_size = st->size; + st->comp_method = ZIP_CM_STORE; + st->encryption_method = ZIP_EM_NONE; + st->valid = ZIP_STAT_MTIME | ZIP_STAT_SIZE | ZIP_STAT_COMP_SIZE | ZIP_STAT_COMP_METHOD | ZIP_STAT_ENCRYPTION_METHOD; + + return sizeof(*st); + } + + case ZIP_SOURCE_SUPPORTS: + return zip_source_make_command_bitmap(ZIP_SOURCE_GET_FILE_ATTRIBUTES, ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_SEEK, ZIP_SOURCE_TELL, ZIP_SOURCE_BEGIN_WRITE, ZIP_SOURCE_BEGIN_WRITE_CLONING, ZIP_SOURCE_COMMIT_WRITE, ZIP_SOURCE_REMOVE, ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_WRITE, -1); + + case ZIP_SOURCE_TELL: + if (ctx->in->offset > ZIP_INT64_MAX) { + zip_error_set(&ctx->error, ZIP_ER_TELL, EOVERFLOW); + return -1; + } + return (zip_int64_t)ctx->in->offset; + + + case ZIP_SOURCE_TELL_WRITE: + if (ctx->out->offset > ZIP_INT64_MAX) { + zip_error_set(&ctx->error, ZIP_ER_TELL, EOVERFLOW); + return -1; + } + return (zip_int64_t)ctx->out->offset; + + case ZIP_SOURCE_WRITE: + if (len > ZIP_INT64_MAX) { + zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); + return -1; + } + return buffer_write(ctx->out, data, len, &ctx->error); + + default: + zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0); + return -1; + } +} + + +static buffer_t * +buffer_clone(buffer_t *buffer, zip_uint64_t offset, zip_error_t *error) { + zip_uint64_t fragment, fragment_offset, waste; + buffer_t *clone; + + if (offset == 0) { + return buffer_new(NULL, 0, 1, error); + } + + if (offset > buffer->size) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + if (buffer->shared_buffer != NULL) { + zip_error_set(error, ZIP_ER_INUSE, 0); + return NULL; + } + + fragment = buffer_find_fragment(buffer, offset); + fragment_offset = offset - buffer->fragment_offsets[fragment]; + + if (fragment_offset == 0) { + fragment--; + fragment_offset = buffer->fragments[fragment].length; + } + + waste = buffer->fragments[fragment].length - fragment_offset; + if (waste > offset) { + zip_error_set(error, ZIP_ER_OPNOTSUPP, 0); + return NULL; + } + + if ((clone = buffer_new(buffer->fragments, fragment + 1, 0, error)) == NULL) { + return NULL; + } + +#ifndef __clang_analyzer__ + /* clone->fragments can't be null, since it was created with at least one fragment */ + clone->fragments[clone->nfragments - 1].length = fragment_offset; +#endif + clone->fragment_offsets[clone->nfragments] = offset; + clone->size = offset; + + clone->first_owned_fragment = ZIP_MIN(buffer->first_owned_fragment, clone->nfragments - 1); + + buffer->shared_buffer = clone; + clone->shared_buffer = buffer; + buffer->shared_fragments = clone->nfragments; + clone->shared_fragments = fragment + 1; + + return clone; +} + + +static zip_uint64_t +buffer_find_fragment(const buffer_t *buffer, zip_uint64_t offset) { + zip_uint64_t low, high, mid; + + low = 0; + high = buffer->nfragments - 1; + + while (low < high) { + mid = (high - low) / 2 + low; + if (buffer->fragment_offsets[mid] > offset) { + high = mid - 1; + } + else if (mid == buffer->nfragments || buffer->fragment_offsets[mid + 1] > offset) { + return mid; + } + else { + low = mid + 1; + } + } + + return low; +} + + +static void +buffer_free(buffer_t *buffer) { + zip_uint64_t i; + + if (buffer == NULL) { + return; + } + + if (buffer->shared_buffer != NULL) { + buffer->shared_buffer->shared_buffer = NULL; + buffer->shared_buffer->shared_fragments = 0; + + buffer->first_owned_fragment = ZIP_MAX(buffer->first_owned_fragment, buffer->shared_fragments); + } + + for (i = buffer->first_owned_fragment; i < buffer->nfragments; i++) { + free(buffer->fragments[i].data); + } + free(buffer->fragments); + free(buffer->fragment_offsets); + free(buffer); +} + + +static bool +buffer_grow_fragments(buffer_t *buffer, zip_uint64_t capacity, zip_error_t *error) { + zip_buffer_fragment_t *fragments; + zip_uint64_t *offsets; + + if (capacity < buffer->fragments_capacity) { + return true; + } + + if ((fragments = realloc(buffer->fragments, sizeof(buffer->fragments[0]) * capacity)) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return false; + } + buffer->fragments = fragments; + if ((offsets = realloc(buffer->fragment_offsets, sizeof(buffer->fragment_offsets[0]) * (capacity + 1))) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return false; + } + buffer->fragment_offsets = offsets; + buffer->fragments_capacity = capacity; + + return true; +} + + +static buffer_t * +buffer_new(const zip_buffer_fragment_t *fragments, zip_uint64_t nfragments, int free_data, zip_error_t *error) { + buffer_t *buffer; + + if ((buffer = malloc(sizeof(*buffer))) == NULL) { + return NULL; + } + + buffer->offset = 0; + buffer->first_owned_fragment = 0; + buffer->size = 0; + buffer->fragments = NULL; + buffer->fragment_offsets = NULL; + buffer->nfragments = 0; + buffer->fragments_capacity = 0; + buffer->shared_buffer = NULL; + buffer->shared_fragments = 0; + + if (nfragments == 0) { + if ((buffer->fragment_offsets = malloc(sizeof(buffer->fragment_offsets[0]))) == NULL) { + free(buffer); + zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + buffer->fragment_offsets[0] = 0; + } + else { + zip_uint64_t i, j, offset; + + if (!buffer_grow_fragments(buffer, nfragments, NULL)) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + buffer_free(buffer); + return NULL; + } + + offset = 0; + for (i = 0, j = 0; i < nfragments; i++) { + if (fragments[i].length == 0) { + continue; + } + if (fragments[i].data == NULL) { + zip_error_set(error, ZIP_ER_INVAL, 0); + buffer_free(buffer); + return NULL; + } + buffer->fragments[j].data = fragments[i].data; + buffer->fragments[j].length = fragments[i].length; + buffer->fragment_offsets[i] = offset; + offset += fragments[i].length; + j++; + } + buffer->nfragments = j; + buffer->first_owned_fragment = free_data ? 0 : buffer->nfragments; + buffer->fragment_offsets[buffer->nfragments] = offset; + buffer->size = offset; + } + + return buffer; +} + +static zip_int64_t +buffer_read(buffer_t *buffer, zip_uint8_t *data, zip_uint64_t length) { + zip_uint64_t n, i, fragment_offset; + + length = ZIP_MIN(length, buffer->size - buffer->offset); + + if (length == 0) { + return 0; + } + if (length > ZIP_INT64_MAX) { + return -1; + } + + i = buffer->current_fragment; + fragment_offset = buffer->offset - buffer->fragment_offsets[i]; + n = 0; + while (n < length) { + zip_uint64_t left = ZIP_MIN(length - n, buffer->fragments[i].length - fragment_offset); + + memcpy(data + n, buffer->fragments[i].data + fragment_offset, left); + + if (left == buffer->fragments[i].length - fragment_offset) { + i++; + } + n += left; + fragment_offset = 0; + } + + buffer->offset += n; + buffer->current_fragment = i; + return (zip_int64_t)n; +} + + +static int +buffer_seek(buffer_t *buffer, void *data, zip_uint64_t len, zip_error_t *error) { + zip_int64_t new_offset = zip_source_seek_compute_offset(buffer->offset, buffer->size, data, len, error); + + if (new_offset < 0) { + return -1; + } + + buffer->offset = (zip_uint64_t)new_offset; + buffer->current_fragment = buffer_find_fragment(buffer, buffer->offset); + return 0; +} + + +static zip_int64_t +buffer_write(buffer_t *buffer, const zip_uint8_t *data, zip_uint64_t length, zip_error_t *error) { + zip_uint64_t n, i, fragment_offset, capacity; + + if (buffer->offset + length + WRITE_FRAGMENT_SIZE - 1 < length) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return -1; + } + + /* grow buffer if needed */ + capacity = buffer_capacity(buffer); + if (buffer->offset + length > capacity) { + zip_uint64_t needed_fragments = buffer->nfragments + (length - (capacity - buffer->offset) + WRITE_FRAGMENT_SIZE - 1) / WRITE_FRAGMENT_SIZE; + + if (needed_fragments > buffer->fragments_capacity) { + zip_uint64_t new_capacity = buffer->fragments_capacity; + + if (new_capacity == 0) { + new_capacity = 16; + } + while (new_capacity < needed_fragments) { + new_capacity *= 2; + } + + if (!buffer_grow_fragments(buffer, new_capacity, error)) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return -1; + } + } + + while (buffer->nfragments < needed_fragments) { + if ((buffer->fragments[buffer->nfragments].data = malloc(WRITE_FRAGMENT_SIZE)) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return -1; + } + buffer->fragments[buffer->nfragments].length = WRITE_FRAGMENT_SIZE; + buffer->nfragments++; + capacity += WRITE_FRAGMENT_SIZE; + buffer->fragment_offsets[buffer->nfragments] = capacity; + } + } + + i = buffer->current_fragment; + fragment_offset = buffer->offset - buffer->fragment_offsets[i]; + n = 0; + while (n < length) { + zip_uint64_t left = ZIP_MIN(length - n, buffer->fragments[i].length - fragment_offset); + + memcpy(buffer->fragments[i].data + fragment_offset, data + n, left); + + if (left == buffer->fragments[i].length - fragment_offset) { + i++; + } + n += left; + fragment_offset = 0; + } + + buffer->offset += n; + buffer->current_fragment = i; + if (buffer->offset > buffer->size) { + buffer->size = buffer->offset; + } + + return (zip_int64_t)n; } diff --git a/ext/libzip/zip_source_call.c b/ext/libzip/zip_source_call.c new file mode 100644 index 0000000000..e7c295444d --- /dev/null +++ b/ext/libzip/zip_source_call.c @@ -0,0 +1,68 @@ +/* + zip_source_call.c -- invoke callback command on zip_source + Copyright (C) 2009-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "zipint.h" + + +zip_int64_t +_zip_source_call(zip_source_t *src, void *data, zip_uint64_t length, zip_source_cmd_t command) { + zip_int64_t ret; + + if ((src->supports & ZIP_SOURCE_MAKE_COMMAND_BITMASK(command)) == 0) { + zip_error_set(&src->error, ZIP_ER_OPNOTSUPP, 0); + return -1; + } + + if (src->src == NULL) { + ret = src->cb.f(src->ud, data, length, command); + } + else { + ret = src->cb.l(src->src, src->ud, data, length, command); + } + + if (ret < 0) { + if (command != ZIP_SOURCE_ERROR && command != ZIP_SOURCE_SUPPORTS) { + int e[2]; + + if (_zip_source_call(src, e, sizeof(e), ZIP_SOURCE_ERROR) < 0) { + zip_error_set(&src->error, ZIP_ER_INTERNAL, 0); + } + else { + zip_error_set(&src->error, e[0], e[1]); + } + } + } + + return ret; +} diff --git a/ext/libzip/zip_source_close.c b/ext/libzip/zip_source_close.c new file mode 100644 index 0000000000..ab7898ce73 --- /dev/null +++ b/ext/libzip/zip_source_close.c @@ -0,0 +1,57 @@ +/* + zip_source_close.c -- close zip_source (stop reading) + Copyright (C) 2009-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + + +int +zip_source_close(zip_source_t *src) { + if (!ZIP_SOURCE_IS_OPEN_READING(src)) { + zip_error_set(&src->error, ZIP_ER_INVAL, 0); + return -1; + } + + src->open_count--; + if (src->open_count == 0) { + _zip_source_call(src, NULL, 0, ZIP_SOURCE_CLOSE); + + if (ZIP_SOURCE_IS_LAYERED(src)) { + if (zip_source_close(src->src) < 0) { + zip_error_set(&src->error, ZIP_ER_INTERNAL, 0); + } + } + } + + return 0; +} diff --git a/ext/libzip/zip_filerange_crc.c b/ext/libzip/zip_source_commit_write.c similarity index 69% rename from ext/libzip/zip_filerange_crc.c rename to ext/libzip/zip_source_commit_write.c index 4da727b337..d1254bf1f8 100644 --- a/ext/libzip/zip_filerange_crc.c +++ b/ext/libzip/zip_source_commit_write.c @@ -1,6 +1,6 @@ /* - zip_filerange_crc.c -- compute CRC32 for a range of a file - Copyright (C) 2008 Dieter Baron and Thomas Klausner + zip_source_commit_write.c -- commit changes to file + Copyright (C) 2014-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -32,44 +32,32 @@ */ -#ifdef _MSC_VER -#pragma warning (disable:4996) -#pragma warning (disable:4244) -#endif - -#include -#include - #include "zipint.h" - - -int -_zip_filerange_crc(FILE *fp, off_t start, off_t len, uLong *crcp, - struct zip_error *errp) -{ - Bytef buf[BUFSIZE]; - size_t n; - - *crcp = crc32(0L, Z_NULL, 0); - - if (fseeko(fp, start, SEEK_SET) != 0) { - _zip_error_set(errp, ZIP_ER_SEEK, errno); +ZIP_EXTERN int +zip_source_commit_write(zip_source_t *src) { + if (!ZIP_SOURCE_IS_OPEN_WRITING(src)) { + zip_error_set(&src->error, ZIP_ER_INVAL, 0); return -1; } - - while (len > 0) { - n = len > BUFSIZE ? BUFSIZE : len; - if ((n=fread(buf, 1, n, fp)) <= 0) { - _zip_error_set(errp, ZIP_ER_READ, errno); + + if (src->open_count > 1) { + zip_error_set(&src->error, ZIP_ER_INUSE, 0); + return -1; + } + else if (ZIP_SOURCE_IS_OPEN_READING(src)) { + if (zip_source_close(src) < 0) { return -1; } - - *crcp = crc32(*crcp, buf, (uInt)n); - - len-= (off_t)n; } + if (_zip_source_call(src, NULL, 0, ZIP_SOURCE_COMMIT_WRITE) < 0) { + src->write_state = ZIP_SOURCE_WRITE_FAILED; + return -1; + } + + src->write_state = ZIP_SOURCE_WRITE_CLOSED; + return 0; } diff --git a/ext/libzip/zip_source_compress.c b/ext/libzip/zip_source_compress.c new file mode 100644 index 0000000000..e49929f563 --- /dev/null +++ b/ext/libzip/zip_source_compress.c @@ -0,0 +1,389 @@ +/* + zip_source_compress.c -- (de)compression routines + Copyright (C) 2017-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "zipint.h" + +struct context { + zip_error_t error; + + bool end_of_input; + bool end_of_stream; + bool can_store; + bool is_stored; /* only valid if end_of_stream is true */ + bool compress; + zip_int32_t method; + + zip_uint64_t size; + zip_int64_t first_read; + zip_uint8_t buffer[BUFSIZE]; + + zip_compression_algorithm_t *algorithm; + void *ud; +}; + + +struct implementation { + zip_uint16_t method; + zip_compression_algorithm_t *compress; + zip_compression_algorithm_t *decompress; +}; + +static struct implementation implementations[] = { + {ZIP_CM_DEFLATE, &zip_algorithm_deflate_compress, &zip_algorithm_deflate_decompress}, +#if defined(HAVE_LIBBZ2) + {ZIP_CM_BZIP2, &zip_algorithm_bzip2_compress, &zip_algorithm_bzip2_decompress}, +#endif +#if defined(HAVE_LIBLZMA) + /* Disabled - because 7z isn't able to unpack ZIP+LZMA ZIP+LZMA2 + archives made this way - and vice versa. + + {ZIP_CM_LZMA, &zip_algorithm_xz_compress, &zip_algorithm_xz_decompress}, + {ZIP_CM_LZMA2, &zip_algorithm_xz_compress, &zip_algorithm_xz_decompress}, + */ + {ZIP_CM_XZ, &zip_algorithm_xz_compress, &zip_algorithm_xz_decompress}, +#endif + +}; + +static size_t implementations_size = sizeof(implementations) / sizeof(implementations[0]); + +static zip_source_t *compression_source_new(zip_t *za, zip_source_t *src, zip_int32_t method, bool compress, int compression_flags); +static zip_int64_t compress_callback(zip_source_t *, void *, void *, zip_uint64_t, zip_source_cmd_t); +static void context_free(struct context *ctx); +static struct context *context_new(zip_int32_t method, bool compress, int compression_flags, zip_compression_algorithm_t *algorithm); +static zip_int64_t compress_read(zip_source_t *, struct context *, void *, zip_uint64_t); + +static zip_compression_algorithm_t * +get_algorithm(zip_int32_t method, bool compress) { + size_t i; + zip_uint16_t real_method = ZIP_CM_ACTUAL(method); + + for (i = 0; i < implementations_size; i++) { + if (implementations[i].method == real_method) { + if (compress) { + return implementations[i].compress; + } + else { + return implementations[i].decompress; + } + } + } + + return NULL; +} + +ZIP_EXTERN int +zip_compression_method_supported(zip_int32_t method, int compress) { + if (method == ZIP_CM_STORE) { + return 1; + } + return get_algorithm(method, compress) != NULL; +} + +zip_source_t * +zip_source_compress(zip_t *za, zip_source_t *src, zip_int32_t method, int compression_flags) { + return compression_source_new(za, src, method, true, compression_flags); +} + +zip_source_t * +zip_source_decompress(zip_t *za, zip_source_t *src, zip_int32_t method) { + return compression_source_new(za, src, method, false, 0); +} + + +static zip_source_t * +compression_source_new(zip_t *za, zip_source_t *src, zip_int32_t method, bool compress, int compression_flags) { + struct context *ctx; + zip_source_t *s2; + zip_compression_algorithm_t *algorithm = NULL; + + if (src == NULL) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((algorithm = get_algorithm(method, compress)) == NULL) { + zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0); + return NULL; + } + + if ((ctx = context_new(method, compress, compression_flags, algorithm)) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return NULL; + } + + if ((s2 = zip_source_layered(za, src, compress_callback, ctx)) == NULL) { + context_free(ctx); + return NULL; + } + + return s2; +} + + +static struct context * +context_new(zip_int32_t method, bool compress, int compression_flags, zip_compression_algorithm_t *algorithm) { + struct context *ctx; + + if ((ctx = (struct context *)malloc(sizeof(*ctx))) == NULL) { + return NULL; + } + zip_error_init(&ctx->error); + ctx->can_store = compress ? ZIP_CM_IS_DEFAULT(method) : false; + ctx->algorithm = algorithm; + ctx->method = method; + ctx->compress = compress; + ctx->end_of_input = false; + ctx->end_of_stream = false; + ctx->is_stored = false; + + if ((ctx->ud = ctx->algorithm->allocate(ZIP_CM_ACTUAL(method), compression_flags, &ctx->error)) == NULL) { + zip_error_fini(&ctx->error); + free(ctx); + return NULL; + } + + return ctx; +} + + +static void +context_free(struct context *ctx) { + if (ctx == NULL) { + return; + } + + ctx->algorithm->deallocate(ctx->ud); + zip_error_fini(&ctx->error); + + free(ctx); +} + + +static zip_int64_t +compress_read(zip_source_t *src, struct context *ctx, void *data, zip_uint64_t len) { + zip_compression_status_t ret; + bool end; + zip_int64_t n; + zip_uint64_t out_offset; + zip_uint64_t out_len; + + if (zip_error_code_zip(&ctx->error) != ZIP_ER_OK) { + return -1; + } + + if (len == 0 || ctx->end_of_stream) { + return 0; + } + + out_offset = 0; + + end = false; + while (!end && out_offset < len) { + out_len = len - out_offset; + ret = ctx->algorithm->process(ctx->ud, (zip_uint8_t *)data + out_offset, &out_len); + + if (ret != ZIP_COMPRESSION_ERROR) { + out_offset += out_len; + } + + switch (ret) { + case ZIP_COMPRESSION_END: + ctx->end_of_stream = true; + + if (!ctx->end_of_input) { + /* TODO: garbage after stream, or compression ended before all data read */ + } + + if (ctx->first_read < 0) { + /* we got end of processed stream before reading any input data */ + zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0); + end = true; + break; + } + if (ctx->can_store && (zip_uint64_t)ctx->first_read <= out_offset) { + ctx->is_stored = true; + ctx->size = (zip_uint64_t)ctx->first_read; + memcpy(data, ctx->buffer, ctx->size); + return (zip_int64_t)ctx->size; + } + end = true; + break; + + case ZIP_COMPRESSION_OK: + break; + + case ZIP_COMPRESSION_NEED_DATA: + if (ctx->end_of_input) { + /* TODO: error: stream not ended, but no more input */ + end = true; + break; + } + + if ((n = zip_source_read(src, ctx->buffer, sizeof(ctx->buffer))) < 0) { + _zip_error_set_from_source(&ctx->error, src); + end = true; + break; + } + else if (n == 0) { + ctx->end_of_input = true; + ctx->algorithm->end_of_input(ctx->ud); + if (ctx->first_read < 0) { + ctx->first_read = 0; + } + } + else { + if (ctx->first_read >= 0) { + /* we overwrote a previously filled ctx->buffer */ + ctx->can_store = false; + } + else { + ctx->first_read = n; + } + + ctx->algorithm->input(ctx->ud, ctx->buffer, (zip_uint64_t)n); + } + break; + + case ZIP_COMPRESSION_ERROR: + /* error set by algorithm */ + if (zip_error_code_zip(&ctx->error) == ZIP_ER_OK) { + zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0); + } + end = true; + break; + } + } + + if (out_offset > 0) { + ctx->can_store = false; + ctx->size += out_offset; + return (zip_int64_t)out_offset; + } + + return (zip_error_code_zip(&ctx->error) == ZIP_ER_OK) ? 0 : -1; +} + + +static zip_int64_t +compress_callback(zip_source_t *src, void *ud, void *data, zip_uint64_t len, zip_source_cmd_t cmd) { + struct context *ctx; + + ctx = (struct context *)ud; + + switch (cmd) { + case ZIP_SOURCE_OPEN: + ctx->size = 0; + ctx->end_of_input = false; + ctx->end_of_stream = false; + ctx->is_stored = false; + ctx->first_read = -1; + + if (!ctx->algorithm->start(ctx->ud)) { + return -1; + } + + return 0; + + case ZIP_SOURCE_READ: + return compress_read(src, ctx, data, len); + + case ZIP_SOURCE_CLOSE: + if (!ctx->algorithm->end(ctx->ud)) { + return -1; + } + return 0; + + case ZIP_SOURCE_STAT: { + zip_stat_t *st; + + st = (zip_stat_t *)data; + + if (ctx->compress) { + if (ctx->end_of_stream) { + st->comp_method = ctx->is_stored ? ZIP_CM_STORE : ZIP_CM_ACTUAL(ctx->method); + st->comp_size = ctx->size; + st->valid |= ZIP_STAT_COMP_SIZE | ZIP_STAT_COMP_METHOD; + } + else { + st->valid &= ~(ZIP_STAT_COMP_SIZE | ZIP_STAT_COMP_METHOD); + } + } + else { + st->comp_method = ZIP_CM_STORE; + st->valid |= ZIP_STAT_COMP_METHOD; + if (ctx->end_of_stream) { + st->size = ctx->size; + st->valid |= ZIP_STAT_SIZE; + } + else { + st->valid &= ~ZIP_STAT_SIZE; + } + } + } + return 0; + + case ZIP_SOURCE_ERROR: + return zip_error_to_data(&ctx->error, data, len); + + case ZIP_SOURCE_FREE: + context_free(ctx); + return 0; + + case ZIP_SOURCE_GET_FILE_ATTRIBUTES: { + zip_file_attributes_t *attributes = (zip_file_attributes_t *)data; + + if (len < sizeof(*attributes)) { + zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); + return -1; + } + + attributes->valid |= ZIP_FILE_ATTRIBUTES_VERSION_NEEDED | ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS; + attributes->version_needed = ctx->algorithm->version_needed; + attributes->general_purpose_bit_mask = ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK; + attributes->general_purpose_bit_flags = (ctx->is_stored ? 0 : ctx->algorithm->general_purpose_bit_flags(ctx->ud)); + + return sizeof(*attributes); + } + + case ZIP_SOURCE_SUPPORTS: + return ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_GET_FILE_ATTRIBUTES, -1); + + default: + zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0); + return -1; + } +} diff --git a/ext/libzip/zip_source_crc.c b/ext/libzip/zip_source_crc.c new file mode 100644 index 0000000000..4f3f223f3a --- /dev/null +++ b/ext/libzip/zip_source_crc.c @@ -0,0 +1,197 @@ +/* + zip_source_crc.c -- pass-through source that calculates CRC32 and size + Copyright (C) 2009-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include +#include +#include + +#include "zipint.h" + +struct crc_context { + int validate; /* whether to check CRC on EOF and return error on mismatch */ + int crc_complete; /* whether CRC was computed for complete file */ + zip_error_t error; + zip_uint64_t size; + zip_uint64_t position; /* current reading position */ + zip_uint64_t crc_position; /* how far we've computed the CRC */ + zip_uint32_t crc; +}; + +static zip_int64_t crc_read(zip_source_t *, void *, void *, zip_uint64_t, zip_source_cmd_t); + + +zip_source_t * +zip_source_crc(zip_t *za, zip_source_t *src, int validate) { + struct crc_context *ctx; + + if (src == NULL) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((ctx = (struct crc_context *)malloc(sizeof(*ctx))) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return NULL; + } + + zip_error_init(&ctx->error); + ctx->validate = validate; + ctx->crc_complete = 0; + ctx->crc_position = 0; + ctx->crc = (zip_uint32_t)crc32(0, NULL, 0); + ctx->size = 0; + + return zip_source_layered(za, src, crc_read, ctx); +} + + +static zip_int64_t +crc_read(zip_source_t *src, void *_ctx, void *data, zip_uint64_t len, zip_source_cmd_t cmd) { + struct crc_context *ctx; + zip_int64_t n; + + ctx = (struct crc_context *)_ctx; + + switch (cmd) { + case ZIP_SOURCE_OPEN: + ctx->position = 0; + return 0; + + case ZIP_SOURCE_READ: + if ((n = zip_source_read(src, data, len)) < 0) { + _zip_error_set_from_source(&ctx->error, src); + return -1; + } + + if (n == 0) { + if (ctx->crc_position == ctx->position) { + ctx->crc_complete = 1; + ctx->size = ctx->position; + + if (ctx->validate) { + struct zip_stat st; + + if (zip_source_stat(src, &st) < 0) { + _zip_error_set_from_source(&ctx->error, src); + return -1; + } + + if ((st.valid & ZIP_STAT_CRC) && st.crc != ctx->crc) { + zip_error_set(&ctx->error, ZIP_ER_CRC, 0); + return -1; + } + if ((st.valid & ZIP_STAT_SIZE) && st.size != ctx->size) { + zip_error_set(&ctx->error, ZIP_ER_INCONS, 0); + return -1; + } + } + } + } + else if (!ctx->crc_complete && ctx->position <= ctx->crc_position) { + zip_uint64_t i, nn; + + for (i = ctx->crc_position - ctx->position; i < (zip_uint64_t)n; i += nn) { + nn = ZIP_MIN(UINT_MAX, (zip_uint64_t)n - i); + + ctx->crc = (zip_uint32_t)crc32(ctx->crc, (const Bytef *)data + i, (uInt)nn); + ctx->crc_position += nn; + } + } + ctx->position += (zip_uint64_t)n; + return n; + + case ZIP_SOURCE_CLOSE: + return 0; + + case ZIP_SOURCE_STAT: { + zip_stat_t *st; + + st = (zip_stat_t *)data; + + if (ctx->crc_complete) { + /* TODO: Set comp_size, comp_method, encryption_method? + After all, this only works for uncompressed data. */ + st->size = ctx->size; + st->crc = ctx->crc; + st->comp_size = ctx->size; + st->comp_method = ZIP_CM_STORE; + st->encryption_method = ZIP_EM_NONE; + st->valid |= ZIP_STAT_SIZE | ZIP_STAT_CRC | ZIP_STAT_COMP_SIZE | ZIP_STAT_COMP_METHOD | ZIP_STAT_ENCRYPTION_METHOD; + } + return 0; + } + + case ZIP_SOURCE_ERROR: + return zip_error_to_data(&ctx->error, data, len); + + case ZIP_SOURCE_FREE: + free(ctx); + return 0; + + case ZIP_SOURCE_SUPPORTS: { + zip_int64_t mask = zip_source_supports(src); + + if (mask < 0) { + _zip_error_set_from_source(&ctx->error, src); + return -1; + } + + return mask & ~zip_source_make_command_bitmap(ZIP_SOURCE_BEGIN_WRITE, ZIP_SOURCE_COMMIT_WRITE, ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_REMOVE, ZIP_SOURCE_GET_FILE_ATTRIBUTES, -1); + } + + case ZIP_SOURCE_SEEK: { + zip_int64_t new_position; + zip_source_args_seek_t *args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error); + + if (args == NULL) { + return -1; + } + if (zip_source_seek(src, args->offset, args->whence) < 0 || (new_position = zip_source_tell(src)) < 0) { + _zip_error_set_from_source(&ctx->error, src); + return -1; + } + + ctx->position = (zip_uint64_t)new_position; + + return 0; + } + + case ZIP_SOURCE_TELL: + return (zip_int64_t)ctx->position; + + default: + zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0); + return -1; + } +} diff --git a/ext/libzip/zip_entry_free.c b/ext/libzip/zip_source_error.c similarity index 82% rename from ext/libzip/zip_entry_free.c rename to ext/libzip/zip_source_error.c index c50c9434bd..2257f71559 100644 --- a/ext/libzip/zip_entry_free.c +++ b/ext/libzip/zip_source_error.c @@ -1,6 +1,6 @@ /* - zip_entry_free.c -- free struct zip_entry - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + zip_source_error.c -- get last error from zip_source + Copyright (C) 2009-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,22 +31,16 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - - -#include #include "zipint.h" - -void -_zip_entry_free(struct zip_entry *ze) -{ - free(ze->ch_filename); - ze->ch_filename = NULL; - free(ze->ch_comment); - ze->ch_comment = NULL; - ze->ch_comment_len = -1; +zip_error_t * +zip_source_error(zip_source_t *src) { + return &src->error; +} - _zip_unchange_data(ze); +bool +_zip_source_had_error(zip_source_t *src) { + return zip_source_error(src)->zip_err != ZIP_ER_OK; } diff --git a/ext/libzip/zip_source_file.h b/ext/libzip/zip_source_file.h new file mode 100644 index 0000000000..43a464533c --- /dev/null +++ b/ext/libzip/zip_source_file.h @@ -0,0 +1,90 @@ +/* + zip_source_file.h -- header for common file operations + Copyright (C) 2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +struct zip_source_file_stat { + zip_uint64_t size; /* must be valid for regular files */ + time_t mtime; /* must always be valid, is initialized to current time */ + bool exists; /* must always be vaild */ + bool regular_file; /* must always be valid */ +}; + +typedef struct zip_source_file_context zip_source_file_context_t; +typedef struct zip_source_file_operations zip_source_file_operations_t; +typedef struct zip_source_file_stat zip_source_file_stat_t; + +struct zip_source_file_context { + zip_error_t error; /* last error information */ + zip_int64_t supports; + + /* reading */ + char *fname; /* name of file to read from */ + void *f; /* file to read from */ + zip_stat_t st; /* stat information passed in */ + zip_file_attributes_t attributes; /* additional file attributes */ + zip_error_t stat_error; /* error returned for stat */ + zip_uint64_t start; /* start offset of data to read */ + zip_uint64_t len; /* length of the file, 0 for up to EOF */ + zip_uint64_t offset; /* current offset relative to start (0 is beginning of part we read) */ + + /* writing */ + char *tmpname; + void *fout; + + zip_source_file_operations_t *ops; + void *ops_userdata; +}; + + +/* The following methods must be implemented to support each feature: + - close, read, seek, and stat must always be implemented. + - To support specifying the file by name, open, and strdup must be implemented. + - For write support, the file must be specified by name and close, commit_write, create_temp_output, remove, rollback_write, and tell must be implemented. + - create_temp_output_cloning is always optional. */ + +struct zip_source_file_operations { + void (*close)(zip_source_file_context_t *ctx); + zip_int64_t (*commit_write)(zip_source_file_context_t *ctx); + zip_int64_t (*create_temp_output)(zip_source_file_context_t *ctx); + zip_int64_t (*create_temp_output_cloning)(zip_source_file_context_t *ctx, zip_uint64_t len); + bool (*open)(zip_source_file_context_t *ctx); + zip_int64_t (*read)(zip_source_file_context_t *ctx, void *buf, zip_uint64_t len); + zip_int64_t (*remove)(zip_source_file_context_t *ctx); + void (*rollback_write)(zip_source_file_context_t *ctx); + bool (*seek)(zip_source_file_context_t *ctx, void *f, zip_int64_t offset, int whence); + bool (*stat)(zip_source_file_context_t *ctx, zip_source_file_stat_t *st); + char *(*string_duplicate)(zip_source_file_context_t *ctx, const char *); + zip_int64_t (*tell)(zip_source_file_context_t *ctx, void *f); + zip_int64_t (*write)(zip_source_file_context_t *ctx, const void *data, zip_uint64_t len); +}; + +zip_source_t *zip_source_file_common_new(const char *fname, void *file, zip_uint64_t start, zip_int64_t len, const zip_stat_t *st, zip_source_file_operations_t *ops, void *ops_userdata, zip_error_t *error); diff --git a/ext/libzip/zip_source_file_common.c b/ext/libzip/zip_source_file_common.c new file mode 100644 index 0000000000..74e21009bd --- /dev/null +++ b/ext/libzip/zip_source_file_common.c @@ -0,0 +1,378 @@ +/* + zip_source_file_common.c -- create data source from file + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "zipint.h" + +#include "zip_source_file.h" + +static zip_int64_t read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd); + +static void +zip_source_file_stat_init(zip_source_file_stat_t *st) { + st->size = 0; + st->mtime = time(NULL); + st->exists = false; + st->regular_file = false; +} + +zip_source_t * +zip_source_file_common_new(const char *fname, void *file, zip_uint64_t start, zip_int64_t len, const zip_stat_t *st, zip_source_file_operations_t *ops, void *ops_userdata, zip_error_t *error) { + zip_source_file_context_t *ctx; + zip_source_t *zs; + zip_source_file_stat_t sb; + + if (ops == NULL) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + if (ops->close == NULL || ops->read == NULL || ops->seek == NULL || ops->stat == NULL) { + zip_error_set(error, ZIP_ER_INTERNAL, 0); + return NULL; + } + + if (ops->write != NULL && (ops->commit_write == NULL || ops->create_temp_output == NULL || ops->remove == NULL || ops->rollback_write == NULL || ops->tell == NULL)) { + zip_error_set(error, ZIP_ER_INTERNAL, 0); + return NULL; + } + + if (fname != NULL) { + if (ops->open == NULL || ops->string_duplicate == NULL) { + zip_error_set(error, ZIP_ER_INTERNAL, 0); + return NULL; + } + } + else if (file == NULL) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + if (len < 0) { + len = 0; + } + + if (start > ZIP_INT64_MAX || start + (zip_uint64_t)len < start) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((ctx = (zip_source_file_context_t *)malloc(sizeof(zip_source_file_context_t))) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + ctx->ops = ops; + ctx->ops_userdata = ops_userdata; + ctx->fname = NULL; + if (fname) { + if ((ctx->fname = ops->string_duplicate(ctx, fname)) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + free(ctx); + return NULL; + } + } + ctx->f = file; + ctx->start = start; + ctx->len = (zip_uint64_t)len; + if (st) { + memcpy(&ctx->st, st, sizeof(ctx->st)); + ctx->st.name = NULL; + ctx->st.valid &= ~ZIP_STAT_NAME; + } + else { + zip_stat_init(&ctx->st); + } + + if (ctx->len > 0) { + ctx->st.size = ctx->len; + ctx->st.valid |= ZIP_STAT_SIZE; + } + + zip_error_init(&ctx->stat_error); + + ctx->tmpname = NULL; + ctx->fout = NULL; + + zip_error_init(&ctx->error); + zip_file_attributes_init(&ctx->attributes); + + ctx->supports = ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1); + + zip_source_file_stat_init(&sb); + if (!ops->stat(ctx, &sb)) { + _zip_error_copy(error, &ctx->error); + free(ctx->fname); + free(ctx); + return NULL; + } + + if (!sb.exists) { + if (ctx->fname && ctx->start == 0 && ctx->len == 0 && ops->write != NULL) { + ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE; + /* zip_open_from_source checks for this to detect non-existing files */ + zip_error_set(&ctx->stat_error, ZIP_ER_READ, ENOENT); + } + else { + zip_error_set(&ctx->stat_error, ZIP_ER_READ, ENOENT); + free(ctx->fname); + free(ctx); + return NULL; + } + } + else { + if ((ctx->st.valid & ZIP_STAT_MTIME) == 0) { + ctx->st.mtime = sb.mtime; + ctx->st.valid |= ZIP_STAT_MTIME; + } + if (sb.regular_file) { + ctx->supports = ZIP_SOURCE_SUPPORTS_SEEKABLE; + + if (ctx->start + ctx->len > sb.size) { + zip_error_set(error, ZIP_ER_INVAL, 0); + free(ctx->fname); + free(ctx); + return NULL; + } + + if (ctx->len == 0) { + ctx->len = sb.size - ctx->start; + ctx->st.size = ctx->len; + ctx->st.valid |= ZIP_STAT_SIZE; + + /* when using a partial file, don't allow writing */ + if (ctx->fname && start == 0 && ops->write != NULL) { + ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE; + } + } + } + + ctx->supports |= ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_GET_FILE_ATTRIBUTES); + } + + ctx->supports |= ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_ACCEPT_EMPTY); + if (ops->create_temp_output_cloning != NULL) { + if (ctx->supports & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE)) { + ctx->supports |= ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE_CLONING); + } + } + + if ((zs = zip_source_function_create(read_file, ctx, error)) == NULL) { + free(ctx->fname); + free(ctx); + return NULL; + } + + return zs; +} + + +static zip_int64_t +read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd) { + zip_source_file_context_t *ctx; + char *buf; + + ctx = (zip_source_file_context_t *)state; + buf = (char *)data; + + switch (cmd) { + case ZIP_SOURCE_ACCEPT_EMPTY: + return 0; + + case ZIP_SOURCE_BEGIN_WRITE: + /* write support should not be set if fname is NULL */ + if (ctx->fname == NULL) { + zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0); + return -1; + } + return ctx->ops->create_temp_output(ctx); + + case ZIP_SOURCE_BEGIN_WRITE_CLONING: + /* write support should not be set if fname is NULL */ + if (ctx->fname == NULL) { + zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0); + return -1; + } + return ctx->ops->create_temp_output_cloning(ctx, len); + + case ZIP_SOURCE_CLOSE: + if (ctx->fname) { + ctx->ops->close(ctx); + ctx->f = NULL; + } + return 0; + + case ZIP_SOURCE_COMMIT_WRITE: { + zip_int64_t ret = ctx->ops->commit_write(ctx); + ctx->fout = NULL; + if (ret == 0) { + free(ctx->tmpname); + ctx->tmpname = NULL; + } + return ret; + } + + case ZIP_SOURCE_ERROR: + return zip_error_to_data(&ctx->error, data, len); + + case ZIP_SOURCE_FREE: + free(ctx->fname); + free(ctx->tmpname); + if (ctx->f) { + ctx->ops->close(ctx); + } + free(ctx); + return 0; + + case ZIP_SOURCE_GET_FILE_ATTRIBUTES: + if (len < sizeof(ctx->attributes)) { + zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); + return -1; + } + memcpy(data, &ctx->attributes, sizeof(ctx->attributes)); + return sizeof(ctx->attributes); + + case ZIP_SOURCE_OPEN: + if (ctx->fname) { + if (ctx->ops->open(ctx) == false) { + return -1; + } + } + + if (ctx->start > 0) { // TODO: rewind on re-open + if (ctx->ops->seek(ctx, ctx->f, (zip_int64_t)ctx->start, SEEK_SET) == false) { + /* TODO: skip by reading */ + return -1; + } + } + ctx->offset = 0; + return 0; + + case ZIP_SOURCE_READ: { + zip_int64_t i; + zip_uint64_t n; + + if (ctx->len > 0) { + n = ZIP_MIN(ctx->len - ctx->offset, len); + } + else { + n = len; + } + + if ((i = ctx->ops->read(ctx, buf, n)) < 0) { + zip_error_set(&ctx->error, ZIP_ER_READ, errno); + return -1; + } + ctx->offset += (zip_uint64_t)i; + + return i; + } + + case ZIP_SOURCE_REMOVE: + return ctx->ops->remove(ctx); + + case ZIP_SOURCE_ROLLBACK_WRITE: + ctx->ops->rollback_write(ctx); + ctx->fout = NULL; + free(ctx->tmpname); + ctx->tmpname = NULL; + return 0; + + case ZIP_SOURCE_SEEK: { + zip_int64_t new_offset = zip_source_seek_compute_offset(ctx->offset, ctx->len, data, len, &ctx->error); + + if (new_offset < 0) { + return -1; + } + + /* The actual offset inside the file must be representable as zip_int64_t. */ + if (new_offset > ZIP_INT64_MAX - (zip_int64_t)ctx->start) { + zip_error_set(&ctx->error, ZIP_ER_SEEK, EOVERFLOW); + return -1; + } + + ctx->offset = (zip_uint64_t)new_offset; + + if (ctx->ops->seek(ctx, ctx->f, (zip_int64_t)(ctx->offset + ctx->start), SEEK_SET) == false) { + return -1; + } + return 0; + } + + case ZIP_SOURCE_SEEK_WRITE: { + zip_source_args_seek_t *args; + + args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error); + if (args == NULL) { + return -1; + } + + if (ctx->ops->seek(ctx, ctx->fout, args->offset, args->whence) == false) { + return -1; + } + return 0; + } + + case ZIP_SOURCE_STAT: { + if (len < sizeof(ctx->st)) + return -1; + + if (zip_error_code_zip(&ctx->stat_error) != 0) { + zip_error_set(&ctx->error, zip_error_code_zip(&ctx->stat_error), zip_error_code_system(&ctx->stat_error)); + return -1; + } + + memcpy(data, &ctx->st, sizeof(ctx->st)); + return sizeof(ctx->st); + } + + case ZIP_SOURCE_SUPPORTS: + return ctx->supports; + + case ZIP_SOURCE_TELL: + return (zip_int64_t)ctx->offset; + + case ZIP_SOURCE_TELL_WRITE: + return ctx->ops->tell(ctx, ctx->fout); + + case ZIP_SOURCE_WRITE: + return ctx->ops->write(ctx, data, len); + + default: + zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0); + return -1; + } +} diff --git a/ext/libzip/zip_source_file_stdio.c b/ext/libzip/zip_source_file_stdio.c new file mode 100644 index 0000000000..4a22f78cb4 --- /dev/null +++ b/ext/libzip/zip_source_file_stdio.c @@ -0,0 +1,208 @@ +/* + zip_source_file_stdio.c -- read-only stdio file source implementation + Copyright (C) 2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "zipint.h" + +#include "zip_source_file.h" +#include "zip_source_file_stdio.h" + +#include +#include +#include +#include + +#ifdef _WIN32 +#ifndef S_IWUSR +#define S_IWUSR _S_IWRITE +#endif +#endif + +/* clang-format off */ +static zip_source_file_operations_t ops_stdio_read = { + _zip_stdio_op_close, + NULL, + NULL, + NULL, + NULL, + _zip_stdio_op_read, + NULL, + NULL, + _zip_stdio_op_seek, + _zip_stdio_op_stat, + NULL, + _zip_stdio_op_tell, + NULL +}; +/* clang-format on */ + + +ZIP_EXTERN zip_source_t * +zip_source_filep(zip_t *za, FILE *file, zip_uint64_t start, zip_int64_t len) { + if (za == NULL) { + return NULL; + } + + return zip_source_filep_create(file, start, len, &za->error); +} + + +ZIP_EXTERN zip_source_t * +zip_source_filep_create(FILE *file, zip_uint64_t start, zip_int64_t length, zip_error_t *error) { + if (file == NULL || length < -1) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + return zip_source_file_common_new(NULL, file, start, length, NULL, &ops_stdio_read, NULL, error); +} + + +void +_zip_stdio_op_close(zip_source_file_context_t *ctx) { + fclose((FILE *)ctx->f); +} + + +zip_int64_t +_zip_stdio_op_read(zip_source_file_context_t *ctx, void *buf, zip_uint64_t len) { + size_t i; + if (len > SIZE_MAX) { + len = SIZE_MAX; + } + + if ((i = fread(buf, 1, (size_t)len, ctx->f)) == 0) { + if (ferror((FILE *)ctx->f)) { + zip_error_set(&ctx->error, ZIP_ER_READ, errno); + return -1; + } + } + + return (zip_int64_t)i; +} + + +bool +_zip_stdio_op_seek(zip_source_file_context_t *ctx, void *f, zip_int64_t offset, int whence) { +#if ZIP_FSEEK_MAX > ZIP_INT64_MAX + if (offset > ZIP_FSEEK_MAX || offset < ZIP_FSEEK_MIN) { + zip_error_set(&ctx->error, ZIP_ER_SEEK, EOVERFLOW); + return false; + } +#endif + + if (fseeko((FILE *)f, (off_t)offset, whence) < 0) { + zip_error_set(&ctx->error, ZIP_ER_SEEK, errno); + return false; + } + return true; +} + + +bool +_zip_stdio_op_stat(zip_source_file_context_t *ctx, zip_source_file_stat_t *st) { + struct stat sb; + + int ret; + + if (ctx->fname) { + ret = stat(ctx->fname, &sb); + } + else { + ret = fstat(fileno((FILE *)ctx->f), &sb); + } + + if (ret < 0) { + if (errno == ENOENT) { + st->exists = false; + return true; + } + zip_error_set(&ctx->error, ZIP_ER_READ, errno); + return false; + } + + st->size = (zip_uint64_t)sb.st_size; + st->mtime = sb.st_mtime; + + st->regular_file = S_ISREG(sb.st_mode); + st->exists = true; + + /* We're using UNIX file API, even on Windows; thus, we supply external file attributes with Unix values. */ + /* TODO: This could be improved on Windows by providing Windows-specific file attributes */ + ctx->attributes.valid = ZIP_FILE_ATTRIBUTES_HOST_SYSTEM | ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES; + ctx->attributes.host_system = ZIP_OPSYS_UNIX; + ctx->attributes.external_file_attributes = (((zip_uint32_t)sb.st_mode) << 16) | ((sb.st_mode & S_IWUSR) ? 0 : 1); + + return true; +} + + +zip_int64_t +_zip_stdio_op_tell(zip_source_file_context_t *ctx, void *f) { + off_t offset = ftello((FILE *)f); + + if (offset < 0) { + zip_error_set(&ctx->error, ZIP_ER_SEEK, errno); + } + + return offset; +} + + +/* + * fopen replacement that sets the close-on-exec flag + * some implementations support an fopen 'e' flag for that, + * but e.g. macOS doesn't. + */ +FILE * +_zip_fopen_close_on_exec(const char *name, bool writeable) { + int fd; + int flags; + FILE *fp; + + flags = O_CLOEXEC; + if (writeable) { + flags |= O_RDWR; + } + else { + flags |= O_RDONLY; + } + + /* mode argument needed on Windows */ + if ((fd = open(name, flags, 0666)) < 0) { + return NULL; + } + if ((fp = fdopen(fd, writeable ? "r+b" : "rb")) == NULL) { + return NULL; + } + return fp; +} diff --git a/ext/libzip/zip_source_file_stdio.h b/ext/libzip/zip_source_file_stdio.h new file mode 100644 index 0000000000..1bf698ce28 --- /dev/null +++ b/ext/libzip/zip_source_file_stdio.h @@ -0,0 +1,47 @@ +#ifndef _HAD_ZIP_SOURCE_FILE_STDIO_H +#define _HAD_ZIP_SOURCE_FILE_STDIO_H + +/* + zip_source_file_stdio.h -- common header for stdio file implementation + Copyright (C) 2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +void _zip_stdio_op_close(zip_source_file_context_t *ctx); +zip_int64_t _zip_stdio_op_read(zip_source_file_context_t *ctx, void *buf, zip_uint64_t len); +bool _zip_stdio_op_seek(zip_source_file_context_t *ctx, void *f, zip_int64_t offset, int whence); +bool _zip_stdio_op_stat(zip_source_file_context_t *ctx, zip_source_file_stat_t *st); +zip_int64_t _zip_stdio_op_tell(zip_source_file_context_t *ctx, void *f); + +FILE *_zip_fopen_close_on_exec(const char *name, bool writeable); + +#endif /* _HAD_ZIP_SOURCE_FILE_STDIO_H */ diff --git a/ext/libzip/zip_source_file_stdio_named.c b/ext/libzip/zip_source_file_stdio_named.c new file mode 100644 index 0000000000..dae8177c49 --- /dev/null +++ b/ext/libzip/zip_source_file_stdio_named.c @@ -0,0 +1,313 @@ +/* + zip_source_file_stdio_named.c -- source for stdio file opened by name + Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "zipint.h" + +#include "zip_source_file.h" +#include "zip_source_file_stdio.h" + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_CLONEFILE +#include +#include +#define CAN_CLONE +#endif +#ifdef HAVE_FICLONERANGE +#include +#include +#define CAN_CLONE +#endif + +static zip_int64_t _zip_stdio_op_commit_write(zip_source_file_context_t *ctx); +static zip_int64_t _zip_stdio_op_create_temp_output(zip_source_file_context_t *ctx); +#ifdef CAN_CLONE +static zip_int64_t _zip_stdio_op_create_temp_output_cloning(zip_source_file_context_t *ctx, zip_uint64_t offset); +#endif +static bool _zip_stdio_op_open(zip_source_file_context_t *ctx); +static zip_int64_t _zip_stdio_op_remove(zip_source_file_context_t *ctx); +static void _zip_stdio_op_rollback_write(zip_source_file_context_t *ctx); +static char *_zip_stdio_op_strdup(zip_source_file_context_t *ctx, const char *string); +static zip_int64_t _zip_stdio_op_write(zip_source_file_context_t *ctx, const void *data, zip_uint64_t len); + +/* clang-format off */ +static zip_source_file_operations_t ops_stdio_named = { + _zip_stdio_op_close, + _zip_stdio_op_commit_write, + _zip_stdio_op_create_temp_output, +#ifdef CAN_CLONE + _zip_stdio_op_create_temp_output_cloning, +#else + NULL, +#endif + _zip_stdio_op_open, + _zip_stdio_op_read, + _zip_stdio_op_remove, + _zip_stdio_op_rollback_write, + _zip_stdio_op_seek, + _zip_stdio_op_stat, + _zip_stdio_op_strdup, + _zip_stdio_op_tell, + _zip_stdio_op_write +}; +/* clang-format on */ + +ZIP_EXTERN zip_source_t * +zip_source_file(zip_t *za, const char *fname, zip_uint64_t start, zip_int64_t len) { + if (za == NULL) + return NULL; + + return zip_source_file_create(fname, start, len, &za->error); +} + + +ZIP_EXTERN zip_source_t * +zip_source_file_create(const char *fname, zip_uint64_t start, zip_int64_t length, zip_error_t *error) { + if (fname == NULL || length < -1) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + return zip_source_file_common_new(fname, NULL, start, length, NULL, &ops_stdio_named, NULL, error); +} + + +static zip_int64_t +_zip_stdio_op_commit_write(zip_source_file_context_t *ctx) { + if (fclose(ctx->fout) < 0) { + zip_error_set(&ctx->error, ZIP_ER_WRITE, errno); + return -1; + } + if (rename(ctx->tmpname, ctx->fname) < 0) { + zip_error_set(&ctx->error, ZIP_ER_RENAME, errno); + return -1; + } + + return 0; +} + + +static zip_int64_t +_zip_stdio_op_create_temp_output(zip_source_file_context_t *ctx) { + char *temp; + int tfd; + int mode; + FILE *tfp; + struct stat st; + + if ((temp = (char *)malloc(strlen(ctx->fname) + 8)) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); + return -1; + } + + if (stat(ctx->fname, &st) == 0) { + mode = st.st_mode; + } + else { + mode = -1; + } + + sprintf(temp, "%s.XXXXXX", ctx->fname); + + if ((tfd = _zip_mkstempm(temp, mode)) == -1) { + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + free(temp); + return -1; + } + + if ((tfp = fdopen(tfd, "r+b")) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + close(tfd); + (void)remove(temp); + free(temp); + return -1; + } + + ctx->fout = tfp; + ctx->tmpname = temp; + + return 0; +} + +#ifdef CAN_CLONE +static zip_int64_t +_zip_stdio_op_create_temp_output_cloning(zip_source_file_context_t *ctx, zip_uint64_t offset) { + char *temp; + FILE *tfp; + + if (offset > ZIP_OFF_MAX) { + zip_error_set(&ctx->error, ZIP_ER_SEEK, E2BIG); + return -1; + } + + if ((temp = (char *)malloc(strlen(ctx->fname) + 8)) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); + return -1; + } + sprintf(temp, "%s.XXXXXX", ctx->fname); + +#ifdef HAVE_CLONEFILE +#ifndef __clang_analyzer__ + /* we can't use mkstemp, since clonefile insists on creating the file */ + if (mktemp(temp) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + free(temp); + return -1; + } +#endif + + if (clonefile(ctx->fname, temp, 0) < 0) { + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + free(temp); + return -1; + } + if ((tfp = _zip_fopen_close_on_exec(temp, true)) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + (void)remove(temp); + free(temp); + return -1; + } +#else + { + int fd; + struct file_clone_range range; + struct stat st; + + if (fstat(fileno(ctx->f), &st) < 0) { + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + free(temp); + return -1; + } + + if ((fd = mkstemp(temp)) < 0) { + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + free(temp); + return -1; + } + + range.src_fd = fileno(ctx->f); + range.src_offset = 0; + range.src_length = ((offset + st.st_blksize - 1) / st.st_blksize) * st.st_blksize; + if (range.src_length > st.st_size) { + range.src_length = 0; + } + range.dest_offset = 0; + if (ioctl(fd, FICLONERANGE, &range) < 0) { + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + (void)close(fd); + (void)remove(temp); + free(temp); + return -1; + } + + if ((tfp = fdopen(fd, "r+b")) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + (void)close(fd); + (void)remove(temp); + free(temp); + return -1; + } + } +#endif + + if (ftruncate(fileno(tfp), (off_t)offset) < 0) { + (void)fclose(tfp); + (void)remove(temp); + free(temp); + return -1; + } + if (fseeko(tfp, (off_t)offset, SEEK_SET) < 0) { + (void)fclose(tfp); + (void)remove(temp); + free(temp); + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + } + + ctx->fout = tfp; + ctx->tmpname = temp; + + return 0; +} +#endif + +static bool +_zip_stdio_op_open(zip_source_file_context_t *ctx) { + if ((ctx->f = _zip_fopen_close_on_exec(ctx->fname, false)) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_OPEN, errno); + return false; + } + return true; +} + + +static zip_int64_t +_zip_stdio_op_remove(zip_source_file_context_t *ctx) { + if (remove(ctx->fname) < 0) { + zip_error_set(&ctx->error, ZIP_ER_REMOVE, errno); + return -1; + } + return 0; +} + + +static void +_zip_stdio_op_rollback_write(zip_source_file_context_t *ctx) { + if (ctx->fout) { + fclose(ctx->fout); + } + (void)remove(ctx->tmpname); +} + +static char * +_zip_stdio_op_strdup(zip_source_file_context_t *ctx, const char *string) { + return strdup(string); +} + + +static zip_int64_t +_zip_stdio_op_write(zip_source_file_context_t *ctx, const void *data, zip_uint64_t len) { + size_t ret; + + clearerr((FILE *)ctx->fout); + ret = fwrite(data, 1, len, (FILE *)ctx->fout); + if (ret != len || ferror((FILE *)ctx->fout)) { + zip_error_set(&ctx->error, ZIP_ER_WRITE, errno); + return -1; + } + + return (zip_int64_t)ret; +} diff --git a/ext/libzip/zip_source_file_win32.c b/ext/libzip/zip_source_file_win32.c new file mode 100644 index 0000000000..2fe838f1fe --- /dev/null +++ b/ext/libzip/zip_source_file_win32.c @@ -0,0 +1,230 @@ +/* + zip_source_file_win32.c -- read-only Windows file source implementation + Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "zip_source_file_win32.h" + +static bool _zip_win32_op_stat(zip_source_file_context_t *ctx, zip_source_file_stat_t *st); + +static bool _zip_stat_win32(zip_source_file_context_t *ctx, zip_source_file_stat_t *st, HANDLE h); + +static zip_source_file_operations_t ops_win32_read = { + _zip_win32_op_close, + NULL, + NULL, + NULL, + NULL, + _zip_win32_op_read, + NULL, + NULL, + _zip_win32_op_seek, + _zip_win32_op_stat, + NULL, + _zip_win32_op_tell, + NULL +}; + + +ZIP_EXTERN zip_source_t * +zip_source_win32handle(zip_t *za, HANDLE h, zip_uint64_t start, zip_int64_t len) { + if (za == NULL) { + return NULL; + } + + return zip_source_win32handle_create(h, start, len, &za->error); +} + + +ZIP_EXTERN zip_source_t * +zip_source_win32handle_create(HANDLE h, zip_uint64_t start, zip_int64_t length, zip_error_t *error) { + if (h == INVALID_HANDLE_VALUE || length < -1) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + return zip_source_file_common_new(NULL, h, start, length, NULL, &ops_win32_read, NULL, error); +} + + +void +_zip_win32_op_close(zip_source_file_context_t *ctx) { + CloseHandle((HANDLE)ctx->f); +} + + +zip_int64_t +_zip_win32_op_read(zip_source_file_context_t *ctx, void *buf, zip_uint64_t len) { + DWORD i; + + /* TODO: cap len to "DWORD_MAX" */ + if (!ReadFile((HANDLE)ctx->f, buf, (DWORD)len, &i, NULL)) { + zip_error_set(&ctx->error, ZIP_ER_READ, _zip_win32_error_to_errno(GetLastError())); + return -1; + } + + return (zip_int64_t)i; +} + + +bool +_zip_win32_op_seek(zip_source_file_context_t *ctx, void *f, zip_int64_t offset, int whence) { + LARGE_INTEGER li; + DWORD method; + + switch (whence) { + case SEEK_SET: + method = FILE_BEGIN; + break; + case SEEK_END: + method = FILE_END; + break; + case SEEK_CUR: + method = FILE_CURRENT; + break; + default: + zip_error_set(&ctx->error, ZIP_ER_SEEK, EINVAL); + return -1; + } + + li.QuadPart = (LONGLONG)offset; + if (!SetFilePointerEx((HANDLE)f, li, NULL, method)) { + zip_error_set(&ctx->error, ZIP_ER_SEEK, _zip_win32_error_to_errno(GetLastError())); + return false; + } + + return true; +} + + +static bool +_zip_win32_op_stat(zip_source_file_context_t *ctx, zip_source_file_stat_t *st) { + return _zip_stat_win32(ctx, st, (HANDLE)ctx->f); +} + + +zip_int64_t +_zip_win32_op_tell(zip_source_file_context_t *ctx, void *f) { + LARGE_INTEGER zero; + LARGE_INTEGER new_offset; + + zero.QuadPart = 0; + if (!SetFilePointerEx((HANDLE)f, zero, &new_offset, FILE_CURRENT)) { + zip_error_set(&ctx->error, ZIP_ER_SEEK, _zip_win32_error_to_errno(GetLastError())); + return -1; + } + + return (zip_int64_t)new_offset.QuadPart; +} + + +int +_zip_win32_error_to_errno(DWORD win32err) { + /* Note: This list isn't exhaustive, but should cover common cases. */ + switch (win32err) { + case ERROR_INVALID_PARAMETER: + return EINVAL; + case ERROR_FILE_NOT_FOUND: + return ENOENT; + case ERROR_INVALID_HANDLE: + return EBADF; + case ERROR_ACCESS_DENIED: + return EACCES; + case ERROR_FILE_EXISTS: + return EEXIST; + case ERROR_TOO_MANY_OPEN_FILES: + return EMFILE; + case ERROR_DISK_FULL: + return ENOSPC; + default: + return 10000 + win32err; + } +} + + +static bool +_zip_stat_win32(zip_source_file_context_t *ctx, zip_source_file_stat_t *st, HANDLE h) { + FILETIME mtimeft; + time_t mtime; + LARGE_INTEGER size; + + if (!GetFileTime(h, NULL, NULL, &mtimeft)) { + zip_error_set(&ctx->error, ZIP_ER_READ, _zip_win32_error_to_errno(GetLastError())); + return false; + } + if (_zip_filetime_to_time_t(mtimeft, &mtime) < 0) { + zip_error_set(&ctx->error, ZIP_ER_READ, ERANGE); + return false; + } + + st->exists = true; + st->mtime = mtime; + + if (GetFileType(h) == FILE_TYPE_DISK) { + st->regular_file = 1; + + if (!GetFileSizeEx(h, &size)) { + zip_error_set(&ctx->error, ZIP_ER_READ, _zip_win32_error_to_errno(GetLastError())); + return false; + } + + st->size = (zip_uint64_t)size.QuadPart; + } + + /* TODO: fill in ctx->attributes */ + + return true; +} + + +bool +_zip_filetime_to_time_t(FILETIME ft, time_t *t) { + /* + Inspired by http://stackoverflow.com/questions/6161776/convert-windows-filetime-to-second-in-unix-linux + */ + const zip_int64_t WINDOWS_TICK = 10000000LL; + const zip_int64_t SEC_TO_UNIX_EPOCH = 11644473600LL; + ULARGE_INTEGER li; + zip_int64_t secs; + time_t temp; + + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + secs = (li.QuadPart / WINDOWS_TICK - SEC_TO_UNIX_EPOCH); + + temp = (time_t)secs; + if (secs != (zip_int64_t)temp) { + return false; + } + + *t = temp; + return true; +} diff --git a/ext/libzip/zip_source_file_win32.h b/ext/libzip/zip_source_file_win32.h new file mode 100644 index 0000000000..5092e630d9 --- /dev/null +++ b/ext/libzip/zip_source_file_win32.h @@ -0,0 +1,74 @@ +#ifndef _HAD_ZIP_SOURCE_FILE_WIN32_H +#define _HAD_ZIP_SOURCE_FILE_WIN32_H + +/* + zip_source_file_win32.h -- common header for Windows file implementation + Copyright (C) 2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* 0x0501 => Windows XP; needs to be at least this value because of GetFileSizeEx */ +#if !defined(MS_UWP) && !defined(_WIN32_WINNT) +#define _WIN32_WINNT 0x0501 +#endif + +#include + +#include + +#include "zipint.h" + +#include "zip_source_file.h" + +struct zip_win32_file_operations { + char *(*allocate_tempname)(const char *name, size_t extra_chars, size_t *lengthp); + HANDLE (__stdcall *create_file)(const void *name, DWORD access, DWORD share_mode, PSECURITY_ATTRIBUTES security_attributes, DWORD creation_disposition, DWORD file_attributes, HANDLE template_file); + BOOL (__stdcall *delete_file)(const void *name); + DWORD (__stdcall *get_file_attributes)(const void *name); + BOOL (__stdcall *get_file_attributes_ex)(const void *name, GET_FILEEX_INFO_LEVELS info_level, void *information); + void (*make_tempname)(char *buf, size_t len, const char *name, zip_uint32_t i); + BOOL (__stdcall *move_file)(const void *from, const void *to, DWORD flags); + BOOL (__stdcall *set_file_attributes)(const void *name, DWORD attributes); + char *(*string_duplicate)(const char *string); +}; + +typedef struct zip_win32_file_operations zip_win32_file_operations_t; + +extern zip_source_file_operations_t _zip_source_file_win32_named_ops; + +void _zip_win32_op_close(zip_source_file_context_t *ctx); +zip_int64_t _zip_win32_op_read(zip_source_file_context_t *ctx, void *buf, zip_uint64_t len); +bool _zip_win32_op_seek(zip_source_file_context_t *ctx, void *f, zip_int64_t offset, int whence); +zip_int64_t _zip_win32_op_tell(zip_source_file_context_t *ctx, void *f); + +bool _zip_filetime_to_time_t(FILETIME ft, time_t *t); +int _zip_win32_error_to_errno(DWORD win32err); + +#endif /* _HAD_ZIP_SOURCE_FILE_WIN32_H */ diff --git a/ext/libzip/zip_source_file_win32_ansi.c b/ext/libzip/zip_source_file_win32_ansi.c new file mode 100644 index 0000000000..573b74e4df --- /dev/null +++ b/ext/libzip/zip_source_file_win32_ansi.c @@ -0,0 +1,81 @@ +/* + zip_source_file_win32_ansi.c -- source for Windows file opened by ANSI name + Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "zip_source_file_win32.h" + +static char *ansi_allocate_tempname(const char *name, size_t extra_chars, size_t *lengthp); +static void ansi_make_tempname(char *buf, size_t len, const char *name, zip_uint32_t i); + +zip_win32_file_operations_t ops_ansi = { + ansi_allocate_tempname, + CreateFileA, + DeleteFileA, + GetFileAttributesA, + GetFileAttributesExA, + ansi_make_tempname, + MoveFileExA, + SetFileAttributesA, + strdup +}; + +ZIP_EXTERN zip_source_t * +zip_source_win32a(zip_t *za, const char *fname, zip_uint64_t start, zip_int64_t len) { + if (za == NULL) + return NULL; + + return zip_source_win32a_create(fname, start, len, &za->error); +} + + +ZIP_EXTERN zip_source_t * +zip_source_win32a_create(const char *fname, zip_uint64_t start, zip_int64_t length, zip_error_t *error) { + if (fname == NULL || length < -1) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + return zip_source_file_common_new(fname, NULL, start, length, NULL, &_zip_source_file_win32_named_ops, &ops_ansi, error); +} + + +static char * +ansi_allocate_tempname(const char *name, size_t extra_chars, size_t *lengthp) { + *lengthp = strlen(name) + extra_chars; + return (char *)malloc(*lengthp); +} + + +static void +ansi_make_tempname(char *buf, size_t len, const char *name, zip_uint32_t i) { + snprintf(buf, len, "%s.%08x", name, i); +} diff --git a/ext/libzip/zip_source_file_win32_named.c b/ext/libzip/zip_source_file_win32_named.c new file mode 100644 index 0000000000..9591b26c0a --- /dev/null +++ b/ext/libzip/zip_source_file_win32_named.c @@ -0,0 +1,266 @@ +/* + zip_source_file_win32_named.c -- source for Windows file opened by name + Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "zip_source_file_win32.h" + +static zip_int64_t _zip_win32_named_op_commit_write(zip_source_file_context_t *ctx); +static zip_int64_t _zip_win32_named_op_create_temp_output(zip_source_file_context_t *ctx); +static bool _zip_win32_named_op_open(zip_source_file_context_t *ctx); +static zip_int64_t _zip_win32_named_op_remove(zip_source_file_context_t *ctx); +static void _zip_win32_named_op_rollback_write(zip_source_file_context_t *ctx); +static bool _zip_win32_named_op_stat(zip_source_file_context_t *ctx, zip_source_file_stat_t *st); +static char *_zip_win32_named_op_string_duplicate(zip_source_file_context_t *ctx, const char *string); +static zip_int64_t _zip_win32_named_op_write(zip_source_file_context_t *ctx, const void *data, zip_uint64_t len); + +static HANDLE win32_named_open(zip_source_file_context_t *ctx, const char *name, bool temporary, PSECURITY_ATTRIBUTES security_attributes); + +/* clang-format off */ +zip_source_file_operations_t _zip_source_file_win32_named_ops = { + _zip_win32_op_close, + _zip_win32_named_op_commit_write, + _zip_win32_named_op_create_temp_output, + NULL, + _zip_win32_named_op_open, + _zip_win32_op_read, + _zip_win32_named_op_remove, + _zip_win32_named_op_rollback_write, + _zip_win32_op_seek, + _zip_win32_named_op_stat, + _zip_win32_named_op_string_duplicate, + _zip_win32_op_tell, + _zip_win32_named_op_write +}; +/* clang-format on */ + +static zip_int64_t +_zip_win32_named_op_commit_write(zip_source_file_context_t *ctx) { + zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata; + + if (!CloseHandle((HANDLE)ctx->fout)) { + zip_error_set(&ctx->error, ZIP_ER_WRITE, _zip_win32_error_to_errno(GetLastError())); + return -1; + } + + DWORD attributes = file_ops->get_file_attributes(ctx->tmpname); + if (attributes == INVALID_FILE_ATTRIBUTES) { + zip_error_set(&ctx->error, ZIP_ER_RENAME, _zip_win32_error_to_errno(GetLastError())); + return -1; + } + + if (attributes & FILE_ATTRIBUTE_TEMPORARY) { + if (!file_ops->set_file_attributes(ctx->tmpname, attributes & ~FILE_ATTRIBUTE_TEMPORARY)) { + zip_error_set(&ctx->error, ZIP_ER_RENAME, _zip_win32_error_to_errno(GetLastError())); + return -1; + } + } + + if (!file_ops->move_file(ctx->tmpname, ctx->fname, MOVEFILE_REPLACE_EXISTING)) { + zip_error_set(&ctx->error, ZIP_ER_RENAME, _zip_win32_error_to_errno(GetLastError())); + return -1; + } + + return 0; +} + +static zip_int64_t +_zip_win32_named_op_create_temp_output(zip_source_file_context_t *ctx) { + zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata; + + zip_uint32_t value, i; + HANDLE th = INVALID_HANDLE_VALUE; + void *temp = NULL; + PSECURITY_DESCRIPTOR psd = NULL; + PSECURITY_ATTRIBUTES psa = NULL; + SECURITY_ATTRIBUTES sa; + SECURITY_INFORMATION si; + DWORD success; + PACL dacl = NULL; + char *tempname = NULL; + size_t tempname_size = 0; + + if ((HANDLE)ctx->f != INVALID_HANDLE_VALUE && GetFileType((HANDLE)ctx->f) == FILE_TYPE_DISK) { + si = DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION; + success = GetSecurityInfo((HANDLE)ctx->f, SE_FILE_OBJECT, si, NULL, NULL, &dacl, NULL, &psd); + if (success == ERROR_SUCCESS) { + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = FALSE; + sa.lpSecurityDescriptor = psd; + psa = &sa; + } + } + + #ifndef MS_UWP + value = GetTickCount(); +#else + value = (zip_uint32_t)(GetTickCount64() & 0xffffffff); +#endif + + if ((tempname = file_ops->allocate_tempname(ctx->fname, 10, &tempname_size)) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); + return -1; + } + + for (i = 0; i < 1024 && th == INVALID_HANDLE_VALUE; i++) { + file_ops->make_tempname(tempname, tempname_size, ctx->fname, value + i); + + th = win32_named_open(ctx, tempname, true, psa); + if (th == INVALID_HANDLE_VALUE && GetLastError() != ERROR_FILE_EXISTS) + break; + } + + if (th == INVALID_HANDLE_VALUE) { + free(tempname); + LocalFree(psd); + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, _zip_win32_error_to_errno(GetLastError())); + return -1; + } + + LocalFree(psd); + ctx->fout = th; + ctx->tmpname = tempname; + + return 0; +} + + +static bool +_zip_win32_named_op_open(zip_source_file_context_t *ctx) { + HANDLE h = win32_named_open(ctx, ctx->fname, false, NULL); + + if (h == INVALID_HANDLE_VALUE) { + return false; + } + + ctx->f = h; + return true; +} + + +static zip_int64_t +_zip_win32_named_op_remove(zip_source_file_context_t *ctx) { + zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata; + + if (!file_ops->delete_file(ctx->fname)) { + zip_error_set(&ctx->error, ZIP_ER_REMOVE, _zip_win32_error_to_errno(GetLastError())); + return -1; + } + + return 0; +} + + +static void +_zip_win32_named_op_rollback_write(zip_source_file_context_t *ctx) { + zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata; + + if (ctx->fout) { + CloseHandle((HANDLE)ctx->fout); + } + file_ops->delete_file(ctx->tmpname); +} + + +static bool +_zip_win32_named_op_stat(zip_source_file_context_t *ctx, zip_source_file_stat_t *st) { + zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata; + + WIN32_FILE_ATTRIBUTE_DATA file_attributes; + + if (!file_ops->get_file_attributes_ex(ctx->fname, GetFileExInfoStandard, &file_attributes)) { + DWORD error = GetLastError(); + if (error == ERROR_FILE_NOT_FOUND) { + st->exists = false; + return true; + } + zip_error_set(&ctx->error, ZIP_ER_READ, _zip_win32_error_to_errno(error)); + return false; + } + + st->exists = true; + st->regular_file = true; /* TODO: Is this always right? How to determine without a HANDLE? */ + if (!_zip_filetime_to_time_t(file_attributes.ftLastWriteTime, &st->mtime)) { + zip_error_set(&ctx->error, ZIP_ER_READ, ERANGE); + return false; + } + st->size = ((zip_uint64_t)file_attributes.nFileSizeHigh << 32) | file_attributes.nFileSizeLow; + + /* TODO: fill in ctx->attributes */ + + return true; +} + + +static char * +_zip_win32_named_op_string_duplicate(zip_source_file_context_t *ctx, const char *string) { + zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata; + + return file_ops->string_duplicate(string); +} + + +static zip_int64_t +_zip_win32_named_op_write(zip_source_file_context_t *ctx, const void *data, zip_uint64_t len) { + DWORD ret; + if (!WriteFile((HANDLE)ctx->fout, data, (DWORD)len, &ret, NULL) || ret != len) { + zip_error_set(&ctx->error, ZIP_ER_WRITE, _zip_win32_error_to_errno(GetLastError())); + return -1; + } + + return (zip_int64_t)ret; +} + + +static HANDLE +win32_named_open(zip_source_file_context_t *ctx, const char *name, bool temporary, PSECURITY_ATTRIBUTES security_attributes) { + zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata; + + DWORD access = GENERIC_READ; + DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE; + DWORD creation_disposition = OPEN_EXISTING; + DWORD file_attributes = FILE_ATTRIBUTE_NORMAL; + + if (temporary) { + access = GENERIC_READ | GENERIC_WRITE; + share_mode = FILE_SHARE_READ; + creation_disposition = CREATE_NEW; + file_attributes = FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_TEMPORARY; + } + + HANDLE h = file_ops->create_file(name, access, share_mode, security_attributes, creation_disposition, file_attributes, NULL); + + if (h == INVALID_HANDLE_VALUE) { + zip_error_set(&ctx->error, ZIP_ER_OPEN, _zip_win32_error_to_errno(GetLastError())); + } + + return h; +} diff --git a/ext/libzip/zip_source_file_win32_utf16.c b/ext/libzip/zip_source_file_win32_utf16.c new file mode 100644 index 0000000000..0d90d9becf --- /dev/null +++ b/ext/libzip/zip_source_file_win32_utf16.c @@ -0,0 +1,112 @@ +/* + zip_source_file_win32_utf16.c -- source for Windows file opened by UTF-16 name + Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "zip_source_file_win32.h" + +static char *utf16_allocate_tempname(const char *name, size_t extra_chars, size_t *lengthp); +static HANDLE __stdcall utf16_create_file(const char *name, DWORD access, DWORD share_mode, PSECURITY_ATTRIBUTES security_attributes, DWORD creation_disposition, DWORD file_attributes, HANDLE template_file); +static void utf16_make_tempname(char *buf, size_t len, const char *name, zip_uint32_t i); +static char *utf16_strdup(const char *string); + +zip_win32_file_operations_t ops_utf16 = { + utf16_allocate_tempname, + utf16_create_file, + DeleteFileW, + GetFileAttributesW, + GetFileAttributesExW, + utf16_make_tempname, + MoveFileExW, + SetFileAttributesW, + utf16_strdup +}; + +ZIP_EXTERN zip_source_t * +zip_source_win32w(zip_t *za, const wchar_t *fname, zip_uint64_t start, zip_int64_t len) { + if (za == NULL) + return NULL; + + return zip_source_win32w_create(fname, start, len, &za->error); +} + + +ZIP_EXTERN zip_source_t * +zip_source_win32w_create(const wchar_t *fname, zip_uint64_t start, zip_int64_t length, zip_error_t *error) { + if (fname == NULL || length < -1) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + + return zip_source_file_common_new((const char *)fname, NULL, start, length, NULL, &_zip_source_file_win32_named_ops, &ops_utf16, error); +} + + +static char * +utf16_allocate_tempname(const char *name, size_t extra_chars, size_t *lengthp) { + *lengthp = wcslen((const wchar_t *)name) + extra_chars; + return (char *)malloc(*lengthp * sizeof(wchar_t)); +} + + +static HANDLE __stdcall +utf16_create_file(const char *name, DWORD access, DWORD share_mode, PSECURITY_ATTRIBUTES security_attributes, DWORD creation_disposition, DWORD file_attributes, HANDLE template_file) { +#ifdef MS_UWP +#if 0 + CREATEFILE2_EXTENDED_PARAMETERS extParams = {0}; + extParams.dwFileAttributes = file_attributes; + extParams.dwFileFlags = FILE_FLAG_RANDOM_ACCESS; + extParams.dwSecurityQosFlags = SECURITY_ANONYMOUS; + extParams.dwSize = sizeof(extParams); + extParams.hTemplateFile = template_file; + extParams.lpSecurityAttributes = security_attributes; + + return CreateFile2((const wchar_t *)name, access, share_mode, creation_disposition, &extParams); +#else + return CreateFile2((const wchar_t *)name, access, share_mode, creation_disposition, NULL); +#endif +#else + return CreateFileW((const wchar_t *)name, access, share_mode, security_attributes, creation_disposition, file_attributes, template_file); +#endif +} + + +static void +utf16_make_tempname(char *buf, size_t len, const char *name, zip_uint32_t i) { + _snwprintf((wchar_t *)buf, len, L"%s.%08x", (const wchar_t *)name, i); +} + + +static char * +utf16_strdup(const char *string) { + return (char *)_wcsdup((const wchar_t *)string); +} diff --git a/ext/libzip/zip_source_file_win32_utf8.c b/ext/libzip/zip_source_file_win32_utf8.c new file mode 100644 index 0000000000..4f258bc411 --- /dev/null +++ b/ext/libzip/zip_source_file_win32_utf8.c @@ -0,0 +1,73 @@ +/* + zip_source_file_win32_ansi.c -- source for Windows file opened by UTF-8 name + Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "zip_source_file_win32.h" + +ZIP_EXTERN zip_source_t * +zip_source_file(zip_t *za, const char *fname, zip_uint64_t start, zip_int64_t len) { + if (za == NULL) { + return NULL; + } + + return zip_source_file_create(fname, start, len, &za->error); +} + + +ZIP_EXTERN zip_source_t * +zip_source_file_create(const char *fname, zip_uint64_t start, zip_int64_t length, zip_error_t *error) { + int size; + wchar_t *wfname; + zip_source_t *source; + + if (fname == NULL || length < -1) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + /* Convert fname from UTF-8 to Windows-friendly UTF-16. */ + size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, fname, -1, NULL, 0); + if (size == 0) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + if ((wfname = (wchar_t *)malloc(sizeof(wchar_t) * size)) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, fname, -1, wfname, size); + + source = zip_source_win32w_create(wfname, start, length, error); + + free(wfname); + return source; +} diff --git a/ext/libzip/zip_source_filep.c b/ext/libzip/zip_source_filep.c deleted file mode 100644 index e81a9df193..0000000000 --- a/ext/libzip/zip_source_filep.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - zip_source_filep.c -- create data source from FILE * - Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner - - This file is part of libzip, a library to manipulate ZIP archives. - The authors can be contacted at - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - 3. The names of the authors may not be used to endorse or promote - products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS - OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifdef _MSC_VER -#pragma warning (disable:4996) -#pragma warning (disable:4244) -#endif - -#include -#include -#include -#include -#include - -#include "zipint.h" - -struct read_file { - char *fname; /* name of file to copy from */ - FILE *f; /* file to copy from */ - off_t off; /* start offset of */ - off_t len; /* lengt of data to copy */ - off_t remain; /* bytes remaining to be copied */ - int e[2]; /* error codes */ -}; - -static ssize_t read_file(void *state, void *data, size_t len, - enum zip_source_cmd cmd); - - - -ZIP_EXTERN struct zip_source * -zip_source_filep(struct zip *za, FILE *file, off_t start, off_t len) -{ - if (za == NULL) - return NULL; - - if (file == NULL || start < 0 || len < -1) { - _zip_error_set(&za->error, ZIP_ER_INVAL, 0); - return NULL; - } - - return _zip_source_file_or_p(za, NULL, file, start, len); -} - - - -struct zip_source * -_zip_source_file_or_p(struct zip *za, const char *fname, FILE *file, - off_t start, off_t len) -{ - struct read_file *f; - struct zip_source *zs; - - if (file == NULL && fname == NULL) { - _zip_error_set(&za->error, ZIP_ER_INVAL, 0); - return NULL; - } - - if ((f=(struct read_file *)malloc(sizeof(struct read_file))) == NULL) { - _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); - return NULL; - } - - f->fname = NULL; - if (fname) { - if ((f->fname=strdup(fname)) == NULL) { - _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); - free(f); - return NULL; - } - } - f->f = file; - f->off = start; - f->len = (len ? len : -1); - - if ((zs=zip_source_function(za, read_file, f)) == NULL) { - free(f); - return NULL; - } - - return zs; -} - - - -static ssize_t -read_file(void *state, void *data, size_t len, enum zip_source_cmd cmd) -{ - struct read_file *z; - char *buf; - ssize_t i; - size_t n; - - z = (struct read_file *)state; - buf = (char *)data; - - switch (cmd) { - case ZIP_SOURCE_OPEN: - if (z->fname) { - if ((z->f=fopen(z->fname, "rb")) == NULL) { - z->e[0] = ZIP_ER_OPEN; - z->e[1] = errno; - return -1; - } - } - - if (fseeko(z->f, z->off, SEEK_SET) < 0) { - z->e[0] = ZIP_ER_SEEK; - z->e[1] = errno; - return -1; - } - z->remain = z->len; - return 0; - - case ZIP_SOURCE_READ: - if (z->remain != -1) - n = len > z->remain ? z->remain : len; - else - n = len; - - if ((i=fread(buf, 1, n, z->f)) < 0) { - z->e[0] = ZIP_ER_READ; - z->e[1] = errno; - return -1; - } - - if (z->remain != -1) - z->remain -= (off_t)i; - - return i; - - case ZIP_SOURCE_CLOSE: - if (z->fname) { - fclose(z->f); - z->f = NULL; - } - return 0; - - case ZIP_SOURCE_STAT: - { - struct zip_stat *st; - struct stat fst; - int err; - - if (len < sizeof(*st)) - return -1; - - if (z->f) - err = fstat(fileno(z->f), &fst); - else - err = stat(z->fname, &fst); - - if (err != 0) { - z->e[0] = ZIP_ER_READ; /* best match */ - z->e[1] = errno; - return -1; - } - - st = (struct zip_stat *)data; - - zip_stat_init(st); - st->mtime = fst.st_mtime; - if (z->len != -1) - st->size = z->len; - else if ((fst.st_mode&S_IFMT) == S_IFREG) - st->size = fst.st_size; - - return sizeof(*st); - } - - case ZIP_SOURCE_ERROR: - if (len < sizeof(int)*2) - return -1; - - memcpy(data, z->e, sizeof(int)*2); - return sizeof(int)*2; - - case ZIP_SOURCE_FREE: - free(z->fname); - if (z->f) - fclose(z->f); - free(z); - return 0; - - default: - ; - } - - return -1; -} diff --git a/ext/libzip/zip_source_free.c b/ext/libzip/zip_source_free.c index 7a1632fbf9..2f10ef8f1b 100644 --- a/ext/libzip/zip_source_free.c +++ b/ext/libzip/zip_source_free.c @@ -1,6 +1,6 @@ /* zip_source_free.c -- free zip data source - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,21 +31,41 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include "zipint.h" - ZIP_EXTERN void -zip_source_free(struct zip_source *source) -{ - if (source == NULL) +zip_source_free(zip_source_t *src) { + if (src == NULL) return; - (void)source->f(source->ud, NULL, 0, ZIP_SOURCE_FREE); + if (src->refcount > 0) { + src->refcount--; + } + if (src->refcount > 0) { + return; + } - free(source); + if (ZIP_SOURCE_IS_OPEN_READING(src)) { + src->open_count = 1; /* force close */ + zip_source_close(src); + } + if (ZIP_SOURCE_IS_OPEN_WRITING(src)) { + zip_source_rollback_write(src); + } + + if (src->source_archive && !src->source_closed) { + _zip_deregister_source(src->source_archive, src); + } + + (void)_zip_source_call(src, NULL, 0, ZIP_SOURCE_FREE); + + if (src->src) { + zip_source_free(src->src); + } + + free(src); } diff --git a/ext/libzip/zip_source_function.c b/ext/libzip/zip_source_function.c index 91b183cb6f..d5c1b5b01f 100644 --- a/ext/libzip/zip_source_function.c +++ b/ext/libzip/zip_source_function.c @@ -1,6 +1,6 @@ /* zip_source_function.c -- create zip data source from callback function - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,29 +31,67 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include "zipint.h" - -ZIP_EXTERN struct zip_source * -zip_source_function(struct zip *za, zip_source_callback zcb, void *ud) -{ - struct zip_source *zs; - - if (za == NULL) - return NULL; - - if ((zs=(struct zip_source *)malloc(sizeof(*zs))) == NULL) { - _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); +ZIP_EXTERN zip_source_t * +zip_source_function(zip_t *za, zip_source_callback zcb, void *ud) { + if (za == NULL) { return NULL; } - zs->f = zcb; + return zip_source_function_create(zcb, ud, &za->error); +} + + +ZIP_EXTERN zip_source_t * +zip_source_function_create(zip_source_callback zcb, void *ud, zip_error_t *error) { + zip_source_t *zs; + + if ((zs = _zip_source_new(error)) == NULL) + return NULL; + + zs->cb.f = zcb; zs->ud = ud; - + + zs->supports = zcb(ud, NULL, 0, ZIP_SOURCE_SUPPORTS); + if (zs->supports < 0) { + zs->supports = ZIP_SOURCE_SUPPORTS_READABLE; + } + return zs; } + + +ZIP_EXTERN void +zip_source_keep(zip_source_t *src) { + src->refcount++; +} + + +zip_source_t * +_zip_source_new(zip_error_t *error) { + zip_source_t *src; + + if ((src = (zip_source_t *)malloc(sizeof(*src))) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + src->src = NULL; + src->cb.f = NULL; + src->ud = NULL; + src->open_count = 0; + src->write_state = ZIP_SOURCE_WRITE_CLOSED; + src->source_closed = false; + src->source_archive = NULL; + src->refcount = 1; + zip_error_init(&src->error); + src->eof = false; + src->had_read_error = false; + + return src; +} diff --git a/ext/libzip/zip_source_get_file_attributes.c b/ext/libzip/zip_source_get_file_attributes.c new file mode 100644 index 0000000000..acbede1de6 --- /dev/null +++ b/ext/libzip/zip_source_get_file_attributes.c @@ -0,0 +1,104 @@ +/* + zip_source_get_file_attributes.c -- get attributes for file from source + Copyright (C) 2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "zipint.h" + +ZIP_EXTERN void +zip_file_attributes_init(zip_file_attributes_t *attributes) { + attributes->valid = 0; + attributes->version = 1; +} + +int +zip_source_get_file_attributes(zip_source_t *src, zip_file_attributes_t *attributes) { + if (src->source_closed) { + return -1; + } + if (attributes == NULL) { + zip_error_set(&src->error, ZIP_ER_INVAL, 0); + return -1; + } + + zip_file_attributes_init(attributes); + + if (src->supports & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_GET_FILE_ATTRIBUTES)) { + if (_zip_source_call(src, attributes, sizeof(*attributes), ZIP_SOURCE_GET_FILE_ATTRIBUTES) < 0) { + return -1; + } + } + + if (ZIP_SOURCE_IS_LAYERED(src)) { + zip_file_attributes_t lower_attributes; + + if (zip_source_get_file_attributes(src->src, &lower_attributes) < 0) { + _zip_error_set_from_source(&src->error, src->src); + return -1; + } + + if ((lower_attributes.valid & ZIP_FILE_ATTRIBUTES_HOST_SYSTEM) && (attributes->valid & ZIP_FILE_ATTRIBUTES_HOST_SYSTEM) == 0) { + attributes->host_system = lower_attributes.host_system; + attributes->valid |= ZIP_FILE_ATTRIBUTES_HOST_SYSTEM; + } + if ((lower_attributes.valid & ZIP_FILE_ATTRIBUTES_ASCII) && (attributes->valid & ZIP_FILE_ATTRIBUTES_ASCII) == 0) { + attributes->ascii = lower_attributes.ascii; + attributes->valid |= ZIP_FILE_ATTRIBUTES_ASCII; + } + if ((lower_attributes.valid & ZIP_FILE_ATTRIBUTES_VERSION_NEEDED)) { + if (attributes->valid & ZIP_FILE_ATTRIBUTES_VERSION_NEEDED) { + attributes->version_needed = ZIP_MAX(lower_attributes.version_needed, attributes->version_needed); + } + else { + attributes->version_needed = lower_attributes.version_needed; + attributes->valid |= ZIP_FILE_ATTRIBUTES_VERSION_NEEDED; + } + } + if ((lower_attributes.valid & ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES) && (attributes->valid & ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES) == 0) { + attributes->external_file_attributes = lower_attributes.external_file_attributes; + attributes->valid |= ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES; + } + if ((lower_attributes.valid & ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS)) { + if (attributes->valid & ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS) { + attributes->general_purpose_bit_flags &= ~lower_attributes.general_purpose_bit_mask; + attributes->general_purpose_bit_flags |= lower_attributes.general_purpose_bit_flags & lower_attributes.general_purpose_bit_mask; + attributes->general_purpose_bit_mask |= lower_attributes.general_purpose_bit_mask; + } + else { + attributes->valid |= ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS; + attributes->general_purpose_bit_flags = lower_attributes.general_purpose_bit_flags; + attributes->general_purpose_bit_mask = lower_attributes.general_purpose_bit_mask; + } + } + } + + return 0; +} diff --git a/ext/libzip/zip_source_is_deleted.c b/ext/libzip/zip_source_is_deleted.c new file mode 100644 index 0000000000..edd10ca221 --- /dev/null +++ b/ext/libzip/zip_source_is_deleted.c @@ -0,0 +1,41 @@ +/* + zip_source_is_deleted.c -- was archive was removed? + Copyright (C) 2014-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + + +ZIP_EXTERN int +zip_source_is_deleted(zip_source_t *src) { + return src->write_state == ZIP_SOURCE_WRITE_REMOVED; +} diff --git a/ext/libzip/zip_source_layered.c b/ext/libzip/zip_source_layered.c new file mode 100644 index 0000000000..d02bc8529d --- /dev/null +++ b/ext/libzip/zip_source_layered.c @@ -0,0 +1,67 @@ +/* + zip_source_layered.c -- create layered source + Copyright (C) 2009-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include + +#include "zipint.h" + + +zip_source_t * +zip_source_layered(zip_t *za, zip_source_t *src, zip_source_layered_callback cb, void *ud) { + if (za == NULL) + return NULL; + + return zip_source_layered_create(src, cb, ud, &za->error); +} + + +zip_source_t * +zip_source_layered_create(zip_source_t *src, zip_source_layered_callback cb, void *ud, zip_error_t *error) { + zip_source_t *zs; + + if ((zs = _zip_source_new(error)) == NULL) + return NULL; + + zip_source_keep(src); + zs->src = src; + zs->cb.l = cb; + zs->ud = ud; + + zs->supports = cb(src, ud, NULL, 0, ZIP_SOURCE_SUPPORTS); + if (zs->supports < 0) { + zs->supports = ZIP_SOURCE_SUPPORTS_READABLE; + } + + return zs; +} diff --git a/ext/libzip/zip_source_open.c b/ext/libzip/zip_source_open.c new file mode 100644 index 0000000000..414c677a17 --- /dev/null +++ b/ext/libzip/zip_source_open.c @@ -0,0 +1,75 @@ +/* + zip_source_open.c -- open zip_source (prepare for reading) + Copyright (C) 2009-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + +ZIP_EXTERN int +zip_source_open(zip_source_t *src) { + if (src->source_closed) { + return -1; + } + if (src->write_state == ZIP_SOURCE_WRITE_REMOVED) { + zip_error_set(&src->error, ZIP_ER_DELETED, 0); + return -1; + } + + if (ZIP_SOURCE_IS_OPEN_READING(src)) { + if ((zip_source_supports(src) & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SEEK)) == 0) { + zip_error_set(&src->error, ZIP_ER_INUSE, 0); + return -1; + } + } + else { + if (ZIP_SOURCE_IS_LAYERED(src)) { + if (zip_source_open(src->src) < 0) { + _zip_error_set_from_source(&src->error, src->src); + return -1; + } + } + + if (_zip_source_call(src, NULL, 0, ZIP_SOURCE_OPEN) < 0) { + if (ZIP_SOURCE_IS_LAYERED(src)) { + zip_source_close(src->src); + } + return -1; + } + } + + src->eof = false; + src->had_read_error = false; + _zip_error_clear(&src->error); + src->open_count++; + + return 0; +} diff --git a/ext/libzip/zip_source_pkware_decode.c b/ext/libzip/zip_source_pkware_decode.c new file mode 100644 index 0000000000..d5489a500f --- /dev/null +++ b/ext/libzip/zip_source_pkware_decode.c @@ -0,0 +1,221 @@ +/* + zip_source_pkware_decode.c -- Traditional PKWARE decryption routines + Copyright (C) 2009-2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include +#include + +#include "zipint.h" + +struct trad_pkware { + char *password; + zip_pkware_keys_t keys; + zip_error_t error; +}; + + +static int decrypt_header(zip_source_t *, struct trad_pkware *); +static zip_int64_t pkware_decrypt(zip_source_t *, void *, void *, zip_uint64_t, zip_source_cmd_t); +static struct trad_pkware *trad_pkware_new(const char *password, zip_error_t *error); +static void trad_pkware_free(struct trad_pkware *); + + +zip_source_t * +zip_source_pkware_decode(zip_t *za, zip_source_t *src, zip_uint16_t em, int flags, const char *password) { + struct trad_pkware *ctx; + zip_source_t *s2; + + if (password == NULL || src == NULL || em != ZIP_EM_TRAD_PKWARE) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + if (flags & ZIP_CODEC_ENCODE) { + zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0); + return NULL; + } + + if ((ctx = trad_pkware_new(password, &za->error)) == NULL) { + return NULL; + } + + if ((s2 = zip_source_layered(za, src, pkware_decrypt, ctx)) == NULL) { + trad_pkware_free(ctx); + return NULL; + } + + return s2; +} + + +static int +decrypt_header(zip_source_t *src, struct trad_pkware *ctx) { + zip_uint8_t header[ZIP_CRYPTO_PKWARE_HEADERLEN]; + struct zip_stat st; + zip_int64_t n; + + if ((n = zip_source_read(src, header, ZIP_CRYPTO_PKWARE_HEADERLEN)) < 0) { + _zip_error_set_from_source(&ctx->error, src); + return -1; + } + + if (n != ZIP_CRYPTO_PKWARE_HEADERLEN) { + zip_error_set(&ctx->error, ZIP_ER_EOF, 0); + return -1; + } + + _zip_pkware_decrypt(&ctx->keys, header, header, ZIP_CRYPTO_PKWARE_HEADERLEN); + + if (zip_source_stat(src, &st)) { + /* stat failed, skip password validation */ + return 0; + } + + /* password verification - two ways: + * mtime - InfoZIP way, to avoid computing complete CRC before encrypting data + * CRC - old PKWare way + */ + + bool ok = false; + + if (st.valid & ZIP_STAT_MTIME) { + unsigned short dostime, dosdate; + _zip_u2d_time(st.mtime, &dostime, &dosdate); + if (header[ZIP_CRYPTO_PKWARE_HEADERLEN - 1] == dostime >> 8) { + ok = true; + } + } + + if (st.valid & ZIP_STAT_CRC) { + if (header[ZIP_CRYPTO_PKWARE_HEADERLEN - 1] == st.crc >> 24) { + ok = true; + } + } + + if (!ok && ((st.valid & (ZIP_STAT_MTIME | ZIP_STAT_CRC)) != 0)) { + zip_error_set(&ctx->error, ZIP_ER_WRONGPASSWD, 0); + return -1; + } + + return 0; +} + + +static zip_int64_t +pkware_decrypt(zip_source_t *src, void *ud, void *data, zip_uint64_t len, zip_source_cmd_t cmd) { + struct trad_pkware *ctx; + zip_int64_t n; + + ctx = (struct trad_pkware *)ud; + + switch (cmd) { + case ZIP_SOURCE_OPEN: + _zip_pkware_keys_reset(&ctx->keys); + _zip_pkware_decrypt(&ctx->keys, NULL, (const zip_uint8_t *)ctx->password, strlen(ctx->password)); + if (decrypt_header(src, ctx) < 0) { + return -1; + } + return 0; + + case ZIP_SOURCE_READ: + if ((n = zip_source_read(src, data, len)) < 0) { + _zip_error_set_from_source(&ctx->error, src); + return -1; + } + + _zip_pkware_decrypt(&ctx->keys, (zip_uint8_t *)data, (zip_uint8_t *)data, (zip_uint64_t)n); + return n; + + case ZIP_SOURCE_CLOSE: + return 0; + + case ZIP_SOURCE_STAT: { + zip_stat_t *st; + + st = (zip_stat_t *)data; + + st->encryption_method = ZIP_EM_NONE; + st->valid |= ZIP_STAT_ENCRYPTION_METHOD; + if (st->valid & ZIP_STAT_COMP_SIZE) { + st->comp_size -= ZIP_CRYPTO_PKWARE_HEADERLEN; + } + + return 0; + } + + case ZIP_SOURCE_SUPPORTS: + return zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, -1); + + case ZIP_SOURCE_ERROR: + return zip_error_to_data(&ctx->error, data, len); + + case ZIP_SOURCE_FREE: + trad_pkware_free(ctx); + return 0; + + default: + zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); + return -1; + } +} + + +static struct trad_pkware * +trad_pkware_new(const char *password, zip_error_t *error) { + struct trad_pkware *ctx; + + if ((ctx = (struct trad_pkware *)malloc(sizeof(*ctx))) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + if ((ctx->password = strdup(password)) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + free(ctx); + return NULL; + } + + zip_error_init(&ctx->error); + + return ctx; +} + + +static void +trad_pkware_free(struct trad_pkware *ctx) { + if (ctx == NULL) { + return; + } + + free(ctx->password); + free(ctx); +} diff --git a/ext/libzip/zip_source_pkware_encode.c b/ext/libzip/zip_source_pkware_encode.c new file mode 100644 index 0000000000..1e8f42c39b --- /dev/null +++ b/ext/libzip/zip_source_pkware_encode.c @@ -0,0 +1,249 @@ +/* + zip_source_pkware_encode.c -- Traditional PKWARE encryption routines + Copyright (C) 2009-2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include +#include + +#include "zipint.h" + +struct trad_pkware { + char *password; + zip_pkware_keys_t keys; + zip_buffer_t *buffer; + bool eof; + zip_error_t error; +}; + + +static int encrypt_header(zip_source_t *, struct trad_pkware *); +static zip_int64_t pkware_encrypt(zip_source_t *, void *, void *, zip_uint64_t, zip_source_cmd_t); +static void trad_pkware_free(struct trad_pkware *); +static struct trad_pkware *trad_pkware_new(const char *password, zip_error_t *error); + + +zip_source_t * +zip_source_pkware_encode(zip_t *za, zip_source_t *src, zip_uint16_t em, int flags, const char *password) { + struct trad_pkware *ctx; + zip_source_t *s2; + + if (password == NULL || src == NULL || em != ZIP_EM_TRAD_PKWARE) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + if (!(flags & ZIP_CODEC_ENCODE)) { + zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0); + return NULL; + } + + if ((ctx = trad_pkware_new(password, &za->error)) == NULL) { + return NULL; + } + + if ((s2 = zip_source_layered(za, src, pkware_encrypt, ctx)) == NULL) { + trad_pkware_free(ctx); + return NULL; + } + + return s2; +} + + +static int +encrypt_header(zip_source_t *src, struct trad_pkware *ctx) { + struct zip_stat st; + unsigned short dostime, dosdate; + zip_uint8_t *header; + + if (zip_source_stat(src, &st) != 0) { + _zip_error_set_from_source(&ctx->error, src); + return -1; + } + + _zip_u2d_time(st.mtime, &dostime, &dosdate); + + if ((ctx->buffer = _zip_buffer_new(NULL, ZIP_CRYPTO_PKWARE_HEADERLEN)) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); + return -1; + } + + header = _zip_buffer_data(ctx->buffer); + + /* generate header from random bytes and mtime + see appnote.iz, XIII. Decryption, Step 2, last paragraph */ + if (!zip_secure_random(header, ZIP_CRYPTO_PKWARE_HEADERLEN - 1)) { + zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0); + _zip_buffer_free(ctx->buffer); + ctx->buffer = NULL; + return -1; + } + header[ZIP_CRYPTO_PKWARE_HEADERLEN - 1] = (zip_uint8_t)((dostime >> 8) & 0xff); + + _zip_pkware_encrypt(&ctx->keys, header, header, ZIP_CRYPTO_PKWARE_HEADERLEN); + + return 0; +} + + +static zip_int64_t +pkware_encrypt(zip_source_t *src, void *ud, void *data, zip_uint64_t length, zip_source_cmd_t cmd) { + struct trad_pkware *ctx; + zip_int64_t n; + zip_uint64_t buffer_n; + + ctx = (struct trad_pkware *)ud; + + switch (cmd) { + case ZIP_SOURCE_OPEN: + ctx->eof = false; + + /* initialize keys */ + _zip_pkware_keys_reset(&ctx->keys); + _zip_pkware_encrypt(&ctx->keys, NULL, (const zip_uint8_t *)ctx->password, strlen(ctx->password)); + + if (encrypt_header(src, ctx) < 0) { + return -1; + } + return 0; + + case ZIP_SOURCE_READ: + buffer_n = 0; + + if (ctx->buffer) { + /* write header values to data */ + buffer_n = _zip_buffer_read(ctx->buffer, data, length); + data = (zip_uint8_t *)data + buffer_n; + length -= buffer_n; + + if (_zip_buffer_eof(ctx->buffer)) { + _zip_buffer_free(ctx->buffer); + ctx->buffer = NULL; + } + } + + if (ctx->eof) { + return (zip_int64_t)buffer_n; + } + + if ((n = zip_source_read(src, data, length)) < 0) { + _zip_error_set_from_source(&ctx->error, src); + return -1; + } + + _zip_pkware_encrypt(&ctx->keys, (zip_uint8_t *)data, (zip_uint8_t *)data, (zip_uint64_t)n); + + if ((zip_uint64_t)n < length) { + ctx->eof = true; + } + + return (zip_int64_t)buffer_n + n; + + case ZIP_SOURCE_CLOSE: + _zip_buffer_free(ctx->buffer); + ctx->buffer = NULL; + return 0; + + case ZIP_SOURCE_STAT: { + zip_stat_t *st; + + st = (zip_stat_t *)data; + st->encryption_method = ZIP_EM_TRAD_PKWARE; + st->valid |= ZIP_STAT_ENCRYPTION_METHOD; + if (st->valid & ZIP_STAT_COMP_SIZE) { + st->comp_size += ZIP_CRYPTO_PKWARE_HEADERLEN; + } + + return 0; + } + + case ZIP_SOURCE_GET_FILE_ATTRIBUTES: { + zip_file_attributes_t *attributes = (zip_file_attributes_t *)data; + if (length < sizeof(*attributes)) { + zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); + return -1; + } + attributes->valid |= ZIP_FILE_ATTRIBUTES_VERSION_NEEDED; + attributes->version_needed = 20; + + return 0; + } + + case ZIP_SOURCE_SUPPORTS: + return zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_GET_FILE_ATTRIBUTES, -1); + + case ZIP_SOURCE_ERROR: + return zip_error_to_data(&ctx->error, data, length); + + case ZIP_SOURCE_FREE: + trad_pkware_free(ctx); + return 0; + + default: + zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); + return -1; + } +} + + +static struct trad_pkware * +trad_pkware_new(const char *password, zip_error_t *error) { + struct trad_pkware *ctx; + + if ((ctx = (struct trad_pkware *)malloc(sizeof(*ctx))) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + if ((ctx->password = strdup(password)) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + free(ctx); + return NULL; + } + ctx->buffer = NULL; + zip_error_init(&ctx->error); + + return ctx; +} + + +static void +trad_pkware_free(struct trad_pkware *ctx) { + if (ctx == NULL) { + return; + } + + free(ctx->password); + _zip_buffer_free(ctx->buffer); + zip_error_fini(&ctx->error); + free(ctx); +} diff --git a/ext/libzip/zip_source_read.c b/ext/libzip/zip_source_read.c new file mode 100644 index 0000000000..33088cb117 --- /dev/null +++ b/ext/libzip/zip_source_read.c @@ -0,0 +1,90 @@ +/* + zip_source_read.c -- read data from zip_source + Copyright (C) 2009-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + + +zip_int64_t +zip_source_read(zip_source_t *src, void *data, zip_uint64_t len) { + zip_uint64_t bytes_read; + zip_int64_t n; + + if (src->source_closed) { + return -1; + } + if (!ZIP_SOURCE_IS_OPEN_READING(src) || len > ZIP_INT64_MAX || (len > 0 && data == NULL)) { + zip_error_set(&src->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (src->had_read_error) { + return -1; + } + + if (_zip_source_eof(src)) { + return 0; + } + + if (len == 0) { + return 0; + } + + bytes_read = 0; + while (bytes_read < len) { + if ((n = _zip_source_call(src, (zip_uint8_t *)data + bytes_read, len - bytes_read, ZIP_SOURCE_READ)) < 0) { + src->had_read_error = true; + if (bytes_read == 0) { + return -1; + } + else { + return (zip_int64_t)bytes_read; + } + } + + if (n == 0) { + src->eof = 1; + break; + } + + bytes_read += (zip_uint64_t)n; + } + + return (zip_int64_t)bytes_read; +} + + +bool +_zip_source_eof(zip_source_t *src) { + return src->eof; +} diff --git a/ext/libzip/zip_source_remove.c b/ext/libzip/zip_source_remove.c new file mode 100644 index 0000000000..14eddb8f58 --- /dev/null +++ b/ext/libzip/zip_source_remove.c @@ -0,0 +1,60 @@ +/* + zip_source_remove.c -- remove empty archive + Copyright (C) 2014-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "zipint.h" + + +int +zip_source_remove(zip_source_t *src) { + if (src->write_state == ZIP_SOURCE_WRITE_REMOVED) { + return 0; + } + + if (ZIP_SOURCE_IS_OPEN_READING(src)) { + if (zip_source_close(src) < 0) { + return -1; + } + } + if (src->write_state != ZIP_SOURCE_WRITE_CLOSED) { + zip_source_rollback_write(src); + } + + if (_zip_source_call(src, NULL, 0, ZIP_SOURCE_REMOVE) < 0) { + return -1; + } + + src->write_state = ZIP_SOURCE_WRITE_REMOVED; + + return 0; +} diff --git a/ext/libzip/zip_source_rollback_write.c b/ext/libzip/zip_source_rollback_write.c new file mode 100644 index 0000000000..e8259bff2c --- /dev/null +++ b/ext/libzip/zip_source_rollback_write.c @@ -0,0 +1,46 @@ +/* + zip_source_rollback_write.c -- discard changes + Copyright (C) 2014-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + + +ZIP_EXTERN void +zip_source_rollback_write(zip_source_t *src) { + if (src->write_state != ZIP_SOURCE_WRITE_OPEN && src->write_state != ZIP_SOURCE_WRITE_FAILED) { + return; + } + + _zip_source_call(src, NULL, 0, ZIP_SOURCE_ROLLBACK_WRITE); + src->write_state = ZIP_SOURCE_WRITE_CLOSED; +} diff --git a/ext/libzip/zip_source_seek.c b/ext/libzip/zip_source_seek.c new file mode 100644 index 0000000000..163daf1b66 --- /dev/null +++ b/ext/libzip/zip_source_seek.c @@ -0,0 +1,95 @@ +/* + zip_source_seek.c -- seek to offset + Copyright (C) 2014-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + + +ZIP_EXTERN int +zip_source_seek(zip_source_t *src, zip_int64_t offset, int whence) { + zip_source_args_seek_t args; + + if (src->source_closed) { + return -1; + } + if (!ZIP_SOURCE_IS_OPEN_READING(src) || (whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END)) { + zip_error_set(&src->error, ZIP_ER_INVAL, 0); + return -1; + } + + args.offset = offset; + args.whence = whence; + + if (_zip_source_call(src, &args, sizeof(args), ZIP_SOURCE_SEEK) < 0) { + return -1; + } + + src->eof = 0; + return 0; +} + + +zip_int64_t +zip_source_seek_compute_offset(zip_uint64_t offset, zip_uint64_t length, void *data, zip_uint64_t data_length, zip_error_t *error) { + zip_int64_t new_offset; + zip_source_args_seek_t *args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, data_length, error); + + if (args == NULL) { + return -1; + } + + switch (args->whence) { + case SEEK_CUR: + new_offset = (zip_int64_t)offset + args->offset; + break; + + case SEEK_END: + new_offset = (zip_int64_t)length + args->offset; + break; + + case SEEK_SET: + new_offset = args->offset; + break; + + default: + zip_error_set(error, ZIP_ER_INVAL, 0); + return -1; + } + + if (new_offset < 0 || (zip_uint64_t)new_offset > length) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return -1; + } + + return new_offset; +} diff --git a/ext/libzip/zip_source_seek_write.c b/ext/libzip/zip_source_seek_write.c new file mode 100644 index 0000000000..6b0aa78e53 --- /dev/null +++ b/ext/libzip/zip_source_seek_write.c @@ -0,0 +1,51 @@ +/* + zip_source_seek_write.c -- seek to offset for writing + Copyright (C) 2014-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + + +ZIP_EXTERN int +zip_source_seek_write(zip_source_t *src, zip_int64_t offset, int whence) { + zip_source_args_seek_t args; + + if (!ZIP_SOURCE_IS_OPEN_WRITING(src) || (whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END)) { + zip_error_set(&src->error, ZIP_ER_INVAL, 0); + return -1; + } + + args.offset = offset; + args.whence = whence; + + return (_zip_source_call(src, &args, sizeof(args), ZIP_SOURCE_SEEK_WRITE) < 0 ? -1 : 0); +} diff --git a/ext/libzip/zip_source_stat.c b/ext/libzip/zip_source_stat.c new file mode 100644 index 0000000000..3421d2cbd5 --- /dev/null +++ b/ext/libzip/zip_source_stat.c @@ -0,0 +1,62 @@ +/* + zip_source_stat.c -- get meta information from zip_source + Copyright (C) 2009-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + + +ZIP_EXTERN int +zip_source_stat(zip_source_t *src, zip_stat_t *st) { + if (src->source_closed) { + return -1; + } + if (st == NULL) { + zip_error_set(&src->error, ZIP_ER_INVAL, 0); + return -1; + } + + zip_stat_init(st); + + if (ZIP_SOURCE_IS_LAYERED(src)) { + if (zip_source_stat(src->src, st) < 0) { + _zip_error_set_from_source(&src->error, src->src); + return -1; + } + } + + if (_zip_source_call(src, st, sizeof(*st), ZIP_SOURCE_STAT) < 0) { + return -1; + } + + return 0; +} diff --git a/ext/libzip/zip_source_supports.c b/ext/libzip/zip_source_supports.c new file mode 100644 index 0000000000..8471559821 --- /dev/null +++ b/ext/libzip/zip_source_supports.c @@ -0,0 +1,65 @@ +/* + zip_source_supports.c -- check for supported functions + Copyright (C) 2014-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include + +#include "zipint.h" + + +zip_int64_t +zip_source_supports(zip_source_t *src) { + return src->supports; +} + + +ZIP_EXTERN zip_int64_t +zip_source_make_command_bitmap(zip_source_cmd_t cmd0, ...) { + zip_int64_t bitmap; + va_list ap; + + bitmap = ZIP_SOURCE_MAKE_COMMAND_BITMASK(cmd0); + + + va_start(ap, cmd0); + for (;;) { + int cmd = va_arg(ap, int); + if (cmd < 0) { + break; + } + bitmap |= ZIP_SOURCE_MAKE_COMMAND_BITMASK(cmd); + } + va_end(ap); + + return bitmap; +} diff --git a/ext/libzip/zip_source_tell.c b/ext/libzip/zip_source_tell.c new file mode 100644 index 0000000000..772e2524a8 --- /dev/null +++ b/ext/libzip/zip_source_tell.c @@ -0,0 +1,49 @@ +/* + zip_source_tell.c -- report current offset + Copyright (C) 2014-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + + +ZIP_EXTERN zip_int64_t +zip_source_tell(zip_source_t *src) { + if (src->source_closed) { + return -1; + } + if (!ZIP_SOURCE_IS_OPEN_READING(src)) { + zip_error_set(&src->error, ZIP_ER_INVAL, 0); + return -1; + } + + return _zip_source_call(src, NULL, 0, ZIP_SOURCE_TELL); +} diff --git a/ext/libzip/zip_source_tell_write.c b/ext/libzip/zip_source_tell_write.c new file mode 100644 index 0000000000..96ad7e2673 --- /dev/null +++ b/ext/libzip/zip_source_tell_write.c @@ -0,0 +1,46 @@ +/* + zip_source_tell_write.c -- report current offset for writing + Copyright (C) 2014-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + + +ZIP_EXTERN zip_int64_t +zip_source_tell_write(zip_source_t *src) { + if (!ZIP_SOURCE_IS_OPEN_WRITING(src)) { + zip_error_set(&src->error, ZIP_ER_INVAL, 0); + return -1; + } + + return _zip_source_call(src, NULL, 0, ZIP_SOURCE_TELL_WRITE); +} diff --git a/ext/libzip/zip_source_window.c b/ext/libzip/zip_source_window.c new file mode 100644 index 0000000000..d996015da4 --- /dev/null +++ b/ext/libzip/zip_source_window.c @@ -0,0 +1,294 @@ +/* + zip_source_window.c -- return part of lower source + Copyright (C) 2012-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include +#include + +#include "zipint.h" + +struct window { + zip_uint64_t start; /* where in file we start reading */ + zip_uint64_t end; /* where in file we stop reading */ + + /* if not NULL, read file data for this file */ + zip_t *source_archive; + zip_uint64_t source_index; + + zip_uint64_t offset; /* offset in src for next read */ + + zip_stat_t stat; + zip_file_attributes_t attributes; + zip_error_t error; + zip_int64_t supports; + bool needs_seek; +}; + +static zip_int64_t window_read(zip_source_t *, void *, void *, zip_uint64_t, zip_source_cmd_t); + + +zip_source_t * +zip_source_window(zip_t *za, zip_source_t *src, zip_uint64_t start, zip_uint64_t len) { + return _zip_source_window_new(src, start, len, NULL, 0, NULL, 0, &za->error); +} + + +zip_source_t * +_zip_source_window_new(zip_source_t *src, zip_uint64_t start, zip_uint64_t length, zip_stat_t *st, zip_file_attributes_t *attributes, zip_t *source_archive, zip_uint64_t source_index, zip_error_t *error) { + struct window *ctx; + + if (src == NULL || start + length < start || (source_archive == NULL && source_index != 0)) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((ctx = (struct window *)malloc(sizeof(*ctx))) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + ctx->start = start; + ctx->end = start + length; + zip_stat_init(&ctx->stat); + if (attributes != NULL) { + memcpy(&ctx->attributes, attributes, sizeof(ctx->attributes)); + } + else { + zip_file_attributes_init(&ctx->attributes); + } + ctx->source_archive = source_archive; + ctx->source_index = source_index; + zip_error_init(&ctx->error); + ctx->supports = (zip_source_supports(src) & ZIP_SOURCE_SUPPORTS_SEEKABLE) | (zip_source_make_command_bitmap(ZIP_SOURCE_GET_FILE_ATTRIBUTES, ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1)); + ctx->needs_seek = (ctx->supports & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SEEK)) ? true : false; + + if (st) { + if (_zip_stat_merge(&ctx->stat, st, error) < 0) { + free(ctx); + return NULL; + } + } + + return zip_source_layered_create(src, window_read, ctx, error); +} + + +int +_zip_source_set_source_archive(zip_source_t *src, zip_t *za) { + src->source_archive = za; + return _zip_register_source(za, src); +} + + +/* called by zip_discard to avoid operating on file from closed archive */ +void +_zip_source_invalidate(zip_source_t *src) { + src->source_closed = 1; + + if (zip_error_code_zip(&src->error) == ZIP_ER_OK) { + zip_error_set(&src->error, ZIP_ER_ZIPCLOSED, 0); + } +} + + +static zip_int64_t +window_read(zip_source_t *src, void *_ctx, void *data, zip_uint64_t len, zip_source_cmd_t cmd) { + struct window *ctx; + zip_int64_t ret; + zip_uint64_t n, i; + + ctx = (struct window *)_ctx; + + switch (cmd) { + case ZIP_SOURCE_CLOSE: + return 0; + + case ZIP_SOURCE_ERROR: + return zip_error_to_data(&ctx->error, data, len); + + case ZIP_SOURCE_FREE: + free(ctx); + return 0; + + case ZIP_SOURCE_OPEN: + if (ctx->source_archive) { + zip_uint64_t offset; + + if ((offset = _zip_file_get_offset(ctx->source_archive, ctx->source_index, &ctx->error)) == 0) { + return -1; + } + if (ctx->end + offset < ctx->end) { + /* zip archive data claims end of data past zip64 limits */ + zip_error_set(&ctx->error, ZIP_ER_INCONS, 0); + return -1; + } + ctx->start += offset; + ctx->end += offset; + ctx->source_archive = NULL; + } + + if (!ctx->needs_seek) { + DEFINE_BYTE_ARRAY(b, BUFSIZE); + + if (!byte_array_init(b, BUFSIZE)) { + zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); + return -1; + } + + for (n = 0; n < ctx->start; n += (zip_uint64_t)ret) { + i = (ctx->start - n > BUFSIZE ? BUFSIZE : ctx->start - n); + if ((ret = zip_source_read(src, b, i)) < 0) { + _zip_error_set_from_source(&ctx->error, src); + byte_array_fini(b); + return -1; + } + if (ret == 0) { + zip_error_set(&ctx->error, ZIP_ER_EOF, 0); + byte_array_fini(b); + return -1; + } + } + + byte_array_fini(b); + } + + ctx->offset = ctx->start; + return 0; + + case ZIP_SOURCE_READ: + if (len > ctx->end - ctx->offset) + len = ctx->end - ctx->offset; + + if (len == 0) + return 0; + + if (ctx->needs_seek) { + if (zip_source_seek(src, (zip_int64_t)ctx->offset, SEEK_SET) < 0) { + _zip_error_set_from_source(&ctx->error, src); + return -1; + } + } + + if ((ret = zip_source_read(src, data, len)) < 0) { + zip_error_set(&ctx->error, ZIP_ER_EOF, 0); + return -1; + } + + ctx->offset += (zip_uint64_t)ret; + + if (ret == 0) { + if (ctx->offset < ctx->end) { + zip_error_set(&ctx->error, ZIP_ER_EOF, 0); + return -1; + } + } + return ret; + + case ZIP_SOURCE_SEEK: { + zip_int64_t new_offset = zip_source_seek_compute_offset(ctx->offset - ctx->start, ctx->end - ctx->start, data, len, &ctx->error); + + if (new_offset < 0) { + return -1; + } + + ctx->offset = (zip_uint64_t)new_offset + ctx->start; + return 0; + } + + case ZIP_SOURCE_STAT: { + zip_stat_t *st; + + st = (zip_stat_t *)data; + + if (_zip_stat_merge(st, &ctx->stat, &ctx->error) < 0) { + return -1; + } + return 0; + } + + case ZIP_SOURCE_GET_FILE_ATTRIBUTES: + if (len < sizeof(ctx->attributes)) { + zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); + return -1; + } + + memcpy(data, &ctx->attributes, sizeof(ctx->attributes)); + return sizeof(ctx->attributes); + + case ZIP_SOURCE_SUPPORTS: + return ctx->supports; + + case ZIP_SOURCE_TELL: + return (zip_int64_t)(ctx->offset - ctx->start); + + default: + zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0); + return -1; + } +} + + +void +_zip_deregister_source(zip_t *za, zip_source_t *src) { + unsigned int i; + + for (i = 0; i < za->nopen_source; i++) { + if (za->open_source[i] == src) { + za->open_source[i] = za->open_source[za->nopen_source - 1]; + za->nopen_source--; + break; + } + } +} + + +int +_zip_register_source(zip_t *za, zip_source_t *src) { + zip_source_t **open_source; + + if (za->nopen_source + 1 >= za->nopen_source_alloc) { + unsigned int n; + n = za->nopen_source_alloc + 10; + open_source = (zip_source_t **)realloc(za->open_source, n * sizeof(zip_source_t *)); + if (open_source == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + za->nopen_source_alloc = n; + za->open_source = open_source; + } + + za->open_source[za->nopen_source++] = src; + + return 0; +} diff --git a/ext/libzip/zip_source_write.c b/ext/libzip/zip_source_write.c new file mode 100644 index 0000000000..73fc3a318e --- /dev/null +++ b/ext/libzip/zip_source_write.c @@ -0,0 +1,46 @@ +/* + zip_source_write.c -- start a new file for writing + Copyright (C) 2014-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + + +ZIP_EXTERN zip_int64_t +zip_source_write(zip_source_t *src, const void *data, zip_uint64_t length) { + if (!ZIP_SOURCE_IS_OPEN_WRITING(src) || length > ZIP_INT64_MAX) { + zip_error_set(&src->error, ZIP_ER_INVAL, 0); + return -1; + } + + return _zip_source_call(src, (void *)data, length, ZIP_SOURCE_WRITE); +} diff --git a/ext/libzip/zip_source_zip.c b/ext/libzip/zip_source_zip.c index e2e128e0f6..8a7feaa489 100644 --- a/ext/libzip/zip_source_zip.c +++ b/ext/libzip/zip_source_zip.c @@ -1,6 +1,6 @@ /* zip_source_zip.c -- create data source from zip file - Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,161 +31,26 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef _MSC_VER -#pragma warning (disable:4996) -#pragma warning (disable:4244) -#endif #include -#include #include "zipint.h" -struct read_zip { - struct zip_file *zf; - struct zip_stat st; - off_t off, len; -}; -static ssize_t read_zip(void *st, void *data, size_t len, - enum zip_source_cmd cmd); - - - -ZIP_EXTERN struct zip_source * -zip_source_zip(struct zip *za, struct zip *srcza, int srcidx, int flags, - off_t start, off_t len) -{ - struct zip_error error; - struct zip_source *zs; - struct read_zip *p; - - /* XXX: ZIP_FL_RECOMPRESS */ - - if (za == NULL) - return NULL; - - if (srcza == NULL || start < 0 || len < -1 || srcidx < 0 || srcidx >= srcza->nentry) { - _zip_error_set(&za->error, ZIP_ER_INVAL, 0); +ZIP_EXTERN zip_source_t * +zip_source_zip(zip_t *za, zip_t *srcza, zip_uint64_t srcidx, zip_flags_t flags, zip_uint64_t start, zip_int64_t len) { + if (len < -1) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); return NULL; } - if ((flags & ZIP_FL_UNCHANGED) == 0 - && ZIP_ENTRY_DATA_CHANGED(srcza->entry+srcidx)) { - _zip_error_set(&za->error, ZIP_ER_CHANGED, 0); - return NULL; - } + if (len == -1) + len = 0; - if (len == 0) - len = -1; - - if (start == 0 && len == -1 && (flags & ZIP_FL_RECOMPRESS) == 0) + if (start == 0 && len == 0) flags |= ZIP_FL_COMPRESSED; else flags &= ~ZIP_FL_COMPRESSED; - if ((p=(struct read_zip *)malloc(sizeof(*p))) == NULL) { - _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); - return NULL; - } - - _zip_error_copy(&error, &srcza->error); - - if (zip_stat_index(srcza, srcidx, flags, &p->st) < 0 - || (p->zf=zip_fopen_index(srcza, srcidx, flags)) == NULL) { - free(p); - _zip_error_copy(&za->error, &srcza->error); - _zip_error_copy(&srcza->error, &error); - - return NULL; - } - p->off = start; - p->len = len; - - if ((flags & ZIP_FL_COMPRESSED) == 0) { - p->st.size = p->st.comp_size = len; - p->st.comp_method = ZIP_CM_STORE; - p->st.crc = 0; - } - - if ((zs=zip_source_function(za, read_zip, p)) == NULL) { - free(p); - return NULL; - } - - return zs; -} - - - -static ssize_t -read_zip(void *state, void *data, size_t len, enum zip_source_cmd cmd) -{ - struct read_zip *z; - char b[8192], *buf; - ssize_t i, n; - - z = (struct read_zip *)state; - buf = (char *)data; - - switch (cmd) { - case ZIP_SOURCE_OPEN: - for (n=0; noff; n+= i) { - i = (z->off-n > sizeof(b) ? sizeof(b) : z->off-n); - if ((i=zip_fread(z->zf, b, i)) < 0) { - zip_fclose(z->zf); - z->zf = NULL; - return -1; - } - } - return 0; - - case ZIP_SOURCE_READ: - if (z->len != -1) - n = len > z->len ? z->len : len; - else - n = len; - - - if ((i=zip_fread(z->zf, buf, n)) < 0) - return -1; - - if (z->len != -1) - z->len -= i; - - return i; - - case ZIP_SOURCE_CLOSE: - return 0; - - case ZIP_SOURCE_STAT: - if (len < sizeof(z->st)) - return -1; - len = sizeof(z->st); - - memcpy(data, &z->st, len); - return len; - - case ZIP_SOURCE_ERROR: - { - int *e; - - if (len < sizeof(int)*2) - return -1; - - e = (int *)data; - zip_file_error_get(z->zf, e, e+1); - } - return sizeof(int)*2; - - case ZIP_SOURCE_FREE: - zip_fclose(z->zf); - free(z); - return 0; - - default: - ; - } - - return -1; + return _zip_source_zip_new(za, srcza, srcidx, flags, start, (zip_uint64_t)len, NULL); } diff --git a/ext/libzip/zip_source_zip_new.c b/ext/libzip/zip_source_zip_new.c new file mode 100644 index 0000000000..6d547f0eb8 --- /dev/null +++ b/ext/libzip/zip_source_zip_new.c @@ -0,0 +1,191 @@ +/* + zip_source_zip_new.c -- prepare data structures for zip_fopen/zip_source_zip + Copyright (C) 2012-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include + +#include "zipint.h" + +static void _zip_file_attributes_from_dirent(zip_file_attributes_t *attributes, zip_dirent_t *de); + +zip_source_t * +_zip_source_zip_new(zip_t *za, zip_t *srcza, zip_uint64_t srcidx, zip_flags_t flags, zip_uint64_t start, zip_uint64_t len, const char *password) { + zip_source_t *src, *s2; + zip_stat_t st; + zip_file_attributes_t attributes; + zip_dirent_t *de; + bool partial_data, needs_crc, needs_decrypt, needs_decompress; + + if (za == NULL) { + return NULL; + } + + if (srcza == NULL || srcidx >= srcza->nentry) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((flags & ZIP_FL_UNCHANGED) == 0 && (ZIP_ENTRY_DATA_CHANGED(srcza->entry + srcidx) || srcza->entry[srcidx].deleted)) { + zip_error_set(&za->error, ZIP_ER_CHANGED, 0); + return NULL; + } + + if (zip_stat_index(srcza, srcidx, flags | ZIP_FL_UNCHANGED, &st) < 0) { + zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); + return NULL; + } + + if (flags & ZIP_FL_ENCRYPTED) { + flags |= ZIP_FL_COMPRESSED; + } + + if ((start > 0 || len > 0) && (flags & ZIP_FL_COMPRESSED)) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + /* overflow or past end of file */ + if ((start > 0 || len > 0) && (start + len < start || start + len > st.size)) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + if (len == 0) { + len = st.size - start; + } + + partial_data = len < st.size; + needs_decrypt = ((flags & ZIP_FL_ENCRYPTED) == 0) && (st.encryption_method != ZIP_EM_NONE); + needs_decompress = ((flags & ZIP_FL_COMPRESSED) == 0) && (st.comp_method != ZIP_CM_STORE); + /* when reading the whole file, check for CRC errors */ + needs_crc = ((flags & ZIP_FL_COMPRESSED) == 0 || st.comp_method == ZIP_CM_STORE) && !partial_data; + + if (needs_decrypt) { + if (password == NULL) { + password = za->default_password; + } + if (password == NULL) { + zip_error_set(&za->error, ZIP_ER_NOPASSWD, 0); + return NULL; + } + } + + if ((de = _zip_get_dirent(srcza, srcidx, flags, &za->error)) == NULL) { + return NULL; + } + _zip_file_attributes_from_dirent(&attributes, de); + + if (st.comp_size == 0) { + return zip_source_buffer_with_attributes(za, NULL, 0, 0, &attributes); + } + + if (partial_data && !needs_decrypt && !needs_decompress) { + struct zip_stat st2; + + st2.size = len; + st2.comp_size = len; + st2.comp_method = ZIP_CM_STORE; + st2.mtime = st.mtime; + st2.valid = ZIP_STAT_SIZE | ZIP_STAT_COMP_SIZE | ZIP_STAT_COMP_METHOD | ZIP_STAT_MTIME; + + if ((src = _zip_source_window_new(srcza->src, start, len, &st2, &attributes, srcza, srcidx, &za->error)) == NULL) { + return NULL; + } + } + else { + if ((src = _zip_source_window_new(srcza->src, 0, st.comp_size, &st, &attributes, srcza, srcidx, &za->error)) == NULL) { + return NULL; + } + } + + if (_zip_source_set_source_archive(src, srcza) < 0) { + zip_source_free(src); + return NULL; + } + + /* creating a layered source calls zip_keep() on the lower layer, so we free it */ + + if (needs_decrypt) { + zip_encryption_implementation enc_impl; + + if ((enc_impl = _zip_get_encryption_implementation(st.encryption_method, ZIP_CODEC_DECODE)) == NULL) { + zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0); + return NULL; + } + + s2 = enc_impl(za, src, st.encryption_method, 0, password); + zip_source_free(src); + if (s2 == NULL) { + return NULL; + } + src = s2; + } + if (needs_decompress) { + s2 = zip_source_decompress(za, src, st.comp_method); + zip_source_free(src); + if (s2 == NULL) { + return NULL; + } + src = s2; + } + if (needs_crc) { + s2 = zip_source_crc(za, src, 1); + zip_source_free(src); + if (s2 == NULL) { + return NULL; + } + src = s2; + } + + if (partial_data && (needs_decrypt || needs_decompress)) { + s2 = zip_source_window(za, src, start, len); + zip_source_free(src); + if (s2 == NULL) { + return NULL; + } + src = s2; + } + + return src; +} + +static void +_zip_file_attributes_from_dirent(zip_file_attributes_t *attributes, zip_dirent_t *de) { + zip_file_attributes_init(attributes); + attributes->valid = ZIP_FILE_ATTRIBUTES_ASCII | ZIP_FILE_ATTRIBUTES_HOST_SYSTEM | ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES | ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS; + attributes->ascii = de->int_attrib & 1; + attributes->host_system = de->version_madeby >> 8; + attributes->external_file_attributes = de->ext_attrib; + attributes->general_purpose_bit_flags = de->bitflags; + attributes->general_purpose_bit_mask = ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK; +} diff --git a/ext/libzip/zip_stat.c b/ext/libzip/zip_stat.c index df1c0b50b9..341c6f7701 100644 --- a/ext/libzip/zip_stat.c +++ b/ext/libzip/zip_stat.c @@ -1,6 +1,6 @@ /* zip_stat.c -- get information about file by name - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,19 +31,16 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "zipint.h" - ZIP_EXTERN int -zip_stat(struct zip *za, const char *fname, int flags, struct zip_stat *st) -{ - int idx; +zip_stat(zip_t *za, const char *fname, zip_flags_t flags, zip_stat_t *st) { + zip_int64_t idx; - if ((idx=zip_name_locate(za, fname, flags)) < 0) + if ((idx = zip_name_locate(za, fname, flags)) < 0) return -1; - return zip_stat_index(za, idx, flags, st); + return zip_stat_index(za, (zip_uint64_t)idx, flags, st); } diff --git a/ext/libzip/zip_stat_index.c b/ext/libzip/zip_stat_index.c index 60558e432b..7799325712 100644 --- a/ext/libzip/zip_stat_index.c +++ b/ext/libzip/zip_stat_index.c @@ -1,6 +1,6 @@ /* zip_stat_index.c -- get information about file by index - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,60 +31,50 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "zipint.h" - ZIP_EXTERN int -zip_stat_index(struct zip *za, int index, int flags, struct zip_stat *st) -{ +zip_stat_index(zip_t *za, zip_uint64_t index, zip_flags_t flags, zip_stat_t *st) { const char *name; - - if (index < 0 || index >= za->nentry) { - _zip_error_set(&za->error, ZIP_ER_INVAL, 0); - return -1; - } + zip_dirent_t *de; - if ((name=zip_get_name(za, index, flags)) == NULL) + if ((de = _zip_get_dirent(za, index, flags, NULL)) == NULL) return -1; - - if ((flags & ZIP_FL_UNCHANGED) == 0 - && ZIP_ENTRY_DATA_CHANGED(za->entry+index)) { - if (za->entry[index].source->f(za->entry[index].source->ud, - st, sizeof(*st), ZIP_SOURCE_STAT) < 0) { - _zip_error_set(&za->error, ZIP_ER_CHANGED, 0); + if ((name = zip_get_name(za, index, flags)) == NULL) + return -1; + + + if ((flags & ZIP_FL_UNCHANGED) == 0 && ZIP_ENTRY_DATA_CHANGED(za->entry + index)) { + zip_entry_t *entry = za->entry + index; + + if (zip_source_stat(entry->source, st) < 0) { + zip_error_set(&za->error, ZIP_ER_CHANGED, 0); return -1; } + + if (entry->changes->changed & ZIP_DIRENT_LAST_MOD) { + st->mtime = de->last_mod; + st->valid |= ZIP_STAT_MTIME; + } } else { - if (za->cdir == NULL || index >= za->cdir->nentry) { - _zip_error_set(&za->error, ZIP_ER_INVAL, 0); - return -1; - } - - st->crc = za->cdir->entry[index].crc; - st->size = za->cdir->entry[index].uncomp_size; - st->mtime = za->cdir->entry[index].last_mod; - st->comp_size = za->cdir->entry[index].comp_size; - st->comp_method = za->cdir->entry[index].comp_method; - if (za->cdir->entry[index].bitflags & ZIP_GPBF_ENCRYPTED) { - if (za->cdir->entry[index].bitflags & ZIP_GPBF_STRONG_ENCRYPTION) { - /* XXX */ - st->encryption_method = ZIP_EM_UNKNOWN; - } - else - st->encryption_method = ZIP_EM_TRAD_PKWARE; - } - else - st->encryption_method = ZIP_EM_NONE; - /* st->bitflags = za->cdir->entry[index].bitflags; */ + zip_stat_init(st); + + st->crc = de->crc; + st->size = de->uncomp_size; + st->mtime = de->last_mod; + st->comp_size = de->comp_size; + st->comp_method = (zip_uint16_t)de->comp_method; + st->encryption_method = de->encryption_method; + st->valid = (de->crc_valid ? ZIP_STAT_CRC : 0) | ZIP_STAT_SIZE | ZIP_STAT_MTIME | ZIP_STAT_COMP_SIZE | ZIP_STAT_COMP_METHOD | ZIP_STAT_ENCRYPTION_METHOD; } st->index = index; st->name = name; - + st->valid |= ZIP_STAT_INDEX | ZIP_STAT_NAME; + return 0; } diff --git a/ext/libzip/zip_stat_init.c b/ext/libzip/zip_stat_init.c index 133ad304aa..6d0903d6c0 100644 --- a/ext/libzip/zip_stat_init.c +++ b/ext/libzip/zip_stat_init.c @@ -1,6 +1,6 @@ /* zip_stat_init.c -- initialize struct zip_stat. - Copyright (C) 2006-2007 Dieter Baron and Thomas Klausner + Copyright (C) 2006-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,21 +31,53 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - +#include #include "zipint.h" - ZIP_EXTERN void -zip_stat_init(struct zip_stat *st) -{ +zip_stat_init(zip_stat_t *st) { + st->valid = 0; st->name = NULL; - st->index = -1; + st->index = ZIP_UINT64_MAX; st->crc = 0; st->mtime = (time_t)-1; - st->size = -1; - st->comp_size = -1; + st->size = 0; + st->comp_size = 0; st->comp_method = ZIP_CM_STORE; st->encryption_method = ZIP_EM_NONE; } + + +int +_zip_stat_merge(zip_stat_t *dst, const zip_stat_t *src, zip_error_t *error) { + /* name is not merged, since zip_stat_t doesn't own it, and src may not be valid as long as dst */ + if (src->valid & ZIP_STAT_INDEX) { + dst->index = src->index; + } + if (src->valid & ZIP_STAT_SIZE) { + dst->size = src->size; + } + if (src->valid & ZIP_STAT_COMP_SIZE) { + dst->comp_size = src->comp_size; + } + if (src->valid & ZIP_STAT_MTIME) { + dst->mtime = src->mtime; + } + if (src->valid & ZIP_STAT_CRC) { + dst->crc = src->crc; + } + if (src->valid & ZIP_STAT_COMP_METHOD) { + dst->comp_method = src->comp_method; + } + if (src->valid & ZIP_STAT_ENCRYPTION_METHOD) { + dst->encryption_method = src->encryption_method; + } + if (src->valid & ZIP_STAT_FLAGS) { + dst->flags = src->flags; + } + dst->valid |= src->valid; + + return 0; +} diff --git a/ext/libzip/zip_strerror.c b/ext/libzip/zip_strerror.c index 9ebee144f9..906c7f7602 100644 --- a/ext/libzip/zip_strerror.c +++ b/ext/libzip/zip_strerror.c @@ -1,6 +1,6 @@ /* zip_sterror.c -- get string representation of zip error - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,14 +31,11 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "zipint.h" - ZIP_EXTERN const char * -zip_strerror(struct zip *za) -{ - return _zip_error_strerror(&za->error); +zip_strerror(zip_t *za) { + return zip_error_strerror(&za->error); } diff --git a/ext/libzip/zip_string.c b/ext/libzip/zip_string.c new file mode 100644 index 0000000000..122721d84b --- /dev/null +++ b/ext/libzip/zip_string.c @@ -0,0 +1,178 @@ +/* + zip_string.c -- string handling (with encoding) + Copyright (C) 2012-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include +#include +#include + +#include "zipint.h" + +zip_uint32_t +_zip_string_crc32(const zip_string_t *s) { + zip_uint32_t crc; + + crc = (zip_uint32_t)crc32(0L, Z_NULL, 0); + + if (s != NULL) + crc = (zip_uint32_t)crc32(crc, s->raw, s->length); + + return crc; +} + + +int +_zip_string_equal(const zip_string_t *a, const zip_string_t *b) { + if (a == NULL || b == NULL) + return a == b; + + if (a->length != b->length) + return 0; + + /* TODO: encoding */ + + return (memcmp(a->raw, b->raw, a->length) == 0); +} + + +void +_zip_string_free(zip_string_t *s) { + if (s == NULL) + return; + + free(s->raw); + free(s->converted); + free(s); +} + + +const zip_uint8_t * +_zip_string_get(zip_string_t *string, zip_uint32_t *lenp, zip_flags_t flags, zip_error_t *error) { + static const zip_uint8_t empty[1] = ""; + + if (string == NULL) { + if (lenp) + *lenp = 0; + return empty; + } + + if ((flags & ZIP_FL_ENC_RAW) == 0) { + /* start guessing */ + if (string->encoding == ZIP_ENCODING_UNKNOWN) + _zip_guess_encoding(string, ZIP_ENCODING_UNKNOWN); + + if (((flags & ZIP_FL_ENC_STRICT) && string->encoding != ZIP_ENCODING_ASCII && string->encoding != ZIP_ENCODING_UTF8_KNOWN) || (string->encoding == ZIP_ENCODING_CP437)) { + if (string->converted == NULL) { + if ((string->converted = _zip_cp437_to_utf8(string->raw, string->length, &string->converted_length, error)) == NULL) + return NULL; + } + if (lenp) + *lenp = string->converted_length; + return string->converted; + } + } + + if (lenp) + *lenp = string->length; + return string->raw; +} + + +zip_uint16_t +_zip_string_length(const zip_string_t *s) { + if (s == NULL) + return 0; + + return s->length; +} + + +zip_string_t * +_zip_string_new(const zip_uint8_t *raw, zip_uint16_t length, zip_flags_t flags, zip_error_t *error) { + zip_string_t *s; + zip_encoding_type_t expected_encoding; + + if (length == 0) + return NULL; + + switch (flags & ZIP_FL_ENCODING_ALL) { + case ZIP_FL_ENC_GUESS: + expected_encoding = ZIP_ENCODING_UNKNOWN; + break; + case ZIP_FL_ENC_UTF_8: + expected_encoding = ZIP_ENCODING_UTF8_KNOWN; + break; + case ZIP_FL_ENC_CP437: + expected_encoding = ZIP_ENCODING_CP437; + break; + default: + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((s = (zip_string_t *)malloc(sizeof(*s))) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + if ((s->raw = (zip_uint8_t *)malloc((size_t)length + 1)) == NULL) { + free(s); + return NULL; + } + + memcpy(s->raw, raw, length); + s->raw[length] = '\0'; + s->length = length; + s->encoding = ZIP_ENCODING_UNKNOWN; + s->converted = NULL; + s->converted_length = 0; + + if (expected_encoding != ZIP_ENCODING_UNKNOWN) { + if (_zip_guess_encoding(s, expected_encoding) == ZIP_ENCODING_ERROR) { + _zip_string_free(s); + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + } + + return s; +} + + +int +_zip_string_write(zip_t *za, const zip_string_t *s) { + if (s == NULL) + return 0; + + return _zip_write(za, s->raw, s->length); +} diff --git a/ext/libzip/zip_unchange.c b/ext/libzip/zip_unchange.c index ed0d4de65b..1a78e2c354 100644 --- a/ext/libzip/zip_unchange.c +++ b/ext/libzip/zip_unchange.c @@ -1,6 +1,6 @@ /* zip_unchange.c -- undo changes to file in zip archive - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,52 +31,63 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include "zipint.h" - ZIP_EXTERN int -zip_unchange(struct zip *za, int idx) -{ +zip_unchange(zip_t *za, zip_uint64_t idx) { return _zip_unchange(za, idx, 0); } - int -_zip_unchange(struct zip *za, int idx, int allow_duplicates) -{ - int i; - - if (idx < 0 || idx >= za->nentry) { - _zip_error_set(&za->error, ZIP_ER_INVAL, 0); +_zip_unchange(zip_t *za, zip_uint64_t idx, int allow_duplicates) { + zip_int64_t i; + const char *orig_name, *changed_name; + + if (idx >= za->nentry) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); return -1; } - if (za->entry[idx].ch_filename) { - if (!allow_duplicates) { - i = _zip_name_locate(za, - _zip_get_name(za, idx, ZIP_FL_UNCHANGED, NULL), - 0, NULL); - if (i != -1 && i != idx) { - _zip_error_set(&za->error, ZIP_ER_EXISTS, 0); + if (!allow_duplicates && za->entry[idx].changes && (za->entry[idx].changes->changed & ZIP_DIRENT_FILENAME)) { + if (za->entry[idx].orig != NULL) { + if ((orig_name = _zip_get_name(za, idx, ZIP_FL_UNCHANGED, &za->error)) == NULL) { + return -1; + } + + i = _zip_name_locate(za, orig_name, 0, NULL); + if (i >= 0 && (zip_uint64_t)i != idx) { + zip_error_set(&za->error, ZIP_ER_EXISTS, 0); return -1; } } + else { + orig_name = NULL; + } - free(za->entry[idx].ch_filename); - za->entry[idx].ch_filename = NULL; + if ((changed_name = _zip_get_name(za, idx, 0, &za->error)) == NULL) { + return -1; + } + + if (orig_name) { + if (_zip_hash_add(za->names, (const zip_uint8_t *)orig_name, idx, 0, &za->error) == false) { + return -1; + } + } + if (_zip_hash_delete(za->names, (const zip_uint8_t *)changed_name, &za->error) == false) { + _zip_hash_delete(za->names, (const zip_uint8_t *)orig_name, NULL); + return -1; + } } - free(za->entry[idx].ch_comment); - za->entry[idx].ch_comment = NULL; - za->entry[idx].ch_comment_len = -1; + _zip_dirent_free(za->entry[idx].changes); + za->entry[idx].changes = NULL; - _zip_unchange_data(za->entry+idx); + _zip_unchange_data(za->entry + idx); return 0; } diff --git a/ext/libzip/zip_unchange_all.c b/ext/libzip/zip_unchange_all.c index 54a7a0d2e1..2ae742f87f 100644 --- a/ext/libzip/zip_unchange_all.c +++ b/ext/libzip/zip_unchange_all.c @@ -1,6 +1,6 @@ /* zip_unchange.c -- undo changes to all files in zip archive - Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,21 +31,21 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - - -#include #include "zipint.h" - ZIP_EXTERN int -zip_unchange_all(struct zip *za) -{ - int ret, i; +zip_unchange_all(zip_t *za) { + int ret; + zip_uint64_t i; + + if (!_zip_hash_revert(za->names, &za->error)) { + return -1; + } ret = 0; - for (i=0; inentry; i++) + for (i = 0; i < za->nentry; i++) ret |= _zip_unchange(za, i, 1); ret |= zip_unchange_archive(za); diff --git a/ext/libzip/zip_unchange_archive.c b/ext/libzip/zip_unchange_archive.c index 8f9c024c36..bd223ef57d 100644 --- a/ext/libzip/zip_unchange_archive.c +++ b/ext/libzip/zip_unchange_archive.c @@ -1,6 +1,6 @@ /* zip_unchange_archive.c -- undo global changes to ZIP archive - Copyright (C) 2006-2008 Dieter Baron and Thomas Klausner + Copyright (C) 2006-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -17,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,20 +31,19 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include "zipint.h" - ZIP_EXTERN int -zip_unchange_archive(struct zip *za) -{ - free(za->ch_comment); - za->ch_comment = NULL; - za->ch_comment_len = -1; +zip_unchange_archive(zip_t *za) { + if (za->comment_changed) { + _zip_string_free(za->comment_changes); + za->comment_changes = NULL; + za->comment_changed = 0; + } za->ch_flags = za->flags; diff --git a/ext/libzip/zip_unchange_data.c b/ext/libzip/zip_unchange_data.c index 6fe89f4fb2..74a429c91f 100644 --- a/ext/libzip/zip_unchange_data.c +++ b/ext/libzip/zip_unchange_data.c @@ -1,8 +1,6 @@ /* - $NiH: zip_unchange_data.c,v 1.14 2004/11/30 23:02:47 wiz Exp $ - zip_unchange_data.c -- undo helper function - Copyright (C) 1999, 2004 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -19,7 +17,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -33,21 +31,23 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - - -#include #include "zipint.h" void -_zip_unchange_data(struct zip_entry *ze) -{ +_zip_unchange_data(zip_entry_t *ze) { if (ze->source) { - (void)ze->source->f(ze->source->ud, NULL, 0, ZIP_SOURCE_FREE); - free(ze->source); + zip_source_free(ze->source); ze->source = NULL; } - - ze->state = ze->ch_filename ? ZIP_ST_RENAMED : ZIP_ST_UNCHANGED; -} + if (ze->changes != NULL && (ze->changes->changed & ZIP_DIRENT_COMP_METHOD) && ze->changes->comp_method == ZIP_CM_REPLACED_DEFAULT) { + ze->changes->changed &= ~ZIP_DIRENT_COMP_METHOD; + if (ze->changes->changed == 0) { + _zip_dirent_free(ze->changes); + ze->changes = NULL; + } + } + + ze->deleted = 0; +} diff --git a/ext/libzip/zip_utf-8.c b/ext/libzip/zip_utf-8.c new file mode 100644 index 0000000000..099db9bf26 --- /dev/null +++ b/ext/libzip/zip_utf-8.c @@ -0,0 +1,226 @@ +/* + zip_utf-8.c -- UTF-8 support functions for libzip + Copyright (C) 2011-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + +#include + + +static const zip_uint16_t _cp437_to_unicode[256] = { + /* 0x00 - 0x0F */ + 0x0000, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, 0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C, + + /* 0x10 - 0x1F */ + 0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8, 0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC, + + /* 0x20 - 0x2F */ + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + + /* 0x30 - 0x3F */ + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + + /* 0x40 - 0x4F */ + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + + /* 0x50 - 0x5F */ + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + + /* 0x60 - 0x6F */ + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + + /* 0x70 - 0x7F */ + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x2302, + + /* 0x80 - 0x8F */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + + /* 0x90 - 0x9F */ + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, + + /* 0xA0 - 0xAF */ + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + + /* 0xB0 - 0xBF */ + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + + /* 0xC0 - 0xCF */ + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + + /* 0xD0 - 0xDF */ + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + + /* 0xE0 - 0xEF */ + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + + /* 0xF0 - 0xFF */ + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0}; + +#define UTF_8_LEN_2_MASK 0xe0 +#define UTF_8_LEN_2_MATCH 0xc0 +#define UTF_8_LEN_3_MASK 0xf0 +#define UTF_8_LEN_3_MATCH 0xe0 +#define UTF_8_LEN_4_MASK 0xf8 +#define UTF_8_LEN_4_MATCH 0xf0 +#define UTF_8_CONTINUE_MASK 0xc0 +#define UTF_8_CONTINUE_MATCH 0x80 + + +zip_encoding_type_t +_zip_guess_encoding(zip_string_t *str, zip_encoding_type_t expected_encoding) { + zip_encoding_type_t enc; + const zip_uint8_t *name; + zip_uint32_t i, j, ulen; + + if (str == NULL) + return ZIP_ENCODING_ASCII; + + name = str->raw; + + if (str->encoding != ZIP_ENCODING_UNKNOWN) + enc = str->encoding; + else { + enc = ZIP_ENCODING_ASCII; + for (i = 0; i < str->length; i++) { + if ((name[i] > 31 && name[i] < 128) || name[i] == '\r' || name[i] == '\n' || name[i] == '\t') + continue; + + enc = ZIP_ENCODING_UTF8_GUESSED; + if ((name[i] & UTF_8_LEN_2_MASK) == UTF_8_LEN_2_MATCH) + ulen = 1; + else if ((name[i] & UTF_8_LEN_3_MASK) == UTF_8_LEN_3_MATCH) + ulen = 2; + else if ((name[i] & UTF_8_LEN_4_MASK) == UTF_8_LEN_4_MATCH) + ulen = 3; + else { + enc = ZIP_ENCODING_CP437; + break; + } + + if (i + ulen >= str->length) { + enc = ZIP_ENCODING_CP437; + break; + } + + for (j = 1; j <= ulen; j++) { + if ((name[i + j] & UTF_8_CONTINUE_MASK) != UTF_8_CONTINUE_MATCH) { + enc = ZIP_ENCODING_CP437; + goto done; + } + } + i += ulen; + } + } + +done: + str->encoding = enc; + + if (expected_encoding != ZIP_ENCODING_UNKNOWN) { + if (expected_encoding == ZIP_ENCODING_UTF8_KNOWN && enc == ZIP_ENCODING_UTF8_GUESSED) + str->encoding = enc = ZIP_ENCODING_UTF8_KNOWN; + + if (expected_encoding != enc && enc != ZIP_ENCODING_ASCII) + return ZIP_ENCODING_ERROR; + } + + return enc; +} + + +static zip_uint32_t +_zip_unicode_to_utf8_len(zip_uint32_t codepoint) { + if (codepoint < 0x0080) + return 1; + if (codepoint < 0x0800) + return 2; + if (codepoint < 0x10000) + return 3; + return 4; +} + + +static zip_uint32_t +_zip_unicode_to_utf8(zip_uint32_t codepoint, zip_uint8_t *buf) { + if (codepoint < 0x0080) { + buf[0] = codepoint & 0xff; + return 1; + } + if (codepoint < 0x0800) { + buf[0] = (zip_uint8_t)(UTF_8_LEN_2_MATCH | ((codepoint >> 6) & 0x1f)); + buf[1] = (zip_uint8_t)(UTF_8_CONTINUE_MATCH | (codepoint & 0x3f)); + return 2; + } + if (codepoint < 0x10000) { + buf[0] = (zip_uint8_t)(UTF_8_LEN_3_MATCH | ((codepoint >> 12) & 0x0f)); + buf[1] = (zip_uint8_t)(UTF_8_CONTINUE_MATCH | ((codepoint >> 6) & 0x3f)); + buf[2] = (zip_uint8_t)(UTF_8_CONTINUE_MATCH | (codepoint & 0x3f)); + return 3; + } + buf[0] = (zip_uint8_t)(UTF_8_LEN_4_MATCH | ((codepoint >> 18) & 0x07)); + buf[1] = (zip_uint8_t)(UTF_8_CONTINUE_MATCH | ((codepoint >> 12) & 0x3f)); + buf[2] = (zip_uint8_t)(UTF_8_CONTINUE_MATCH | ((codepoint >> 6) & 0x3f)); + buf[3] = (zip_uint8_t)(UTF_8_CONTINUE_MATCH | (codepoint & 0x3f)); + return 4; +} + + +zip_uint8_t * +_zip_cp437_to_utf8(const zip_uint8_t *const _cp437buf, zip_uint32_t len, zip_uint32_t *utf8_lenp, zip_error_t *error) { + zip_uint8_t *cp437buf = (zip_uint8_t *)_cp437buf; + zip_uint8_t *utf8buf; + zip_uint32_t buflen, i, offset; + + if (len == 0) { + if (utf8_lenp) + *utf8_lenp = 0; + return NULL; + } + + buflen = 1; + for (i = 0; i < len; i++) + buflen += _zip_unicode_to_utf8_len(_cp437_to_unicode[cp437buf[i]]); + + if ((utf8buf = (zip_uint8_t *)malloc(buflen)) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + offset = 0; + for (i = 0; i < len; i++) + offset += _zip_unicode_to_utf8(_cp437_to_unicode[cp437buf[i]], utf8buf + offset); + + utf8buf[buflen - 1] = 0; + if (utf8_lenp) + *utf8_lenp = buflen - 1; + return utf8buf; +} diff --git a/ext/libzip/zipconf.h b/ext/libzip/zipconf.h new file mode 100644 index 0000000000..e07637e06f --- /dev/null +++ b/ext/libzip/zipconf.h @@ -0,0 +1,48 @@ +#ifndef _HAD_ZIPCONF_H +#define _HAD_ZIPCONF_H + +/* + zipconf.h -- platform specific include file + + This file was generated automatically by CMake + based on ../cmake-zipconf.h.in. + */ + +#define LIBZIP_VERSION "1.7.3" +#define LIBZIP_VERSION_MAJOR 1 +#define LIBZIP_VERSION_MINOR 7 +#define LIBZIP_VERSION_MICRO 3 + +#define ZIP_STATIC + +#define _Nullable +#define _Nonnull + +#include + +typedef int8_t zip_int8_t; +typedef uint8_t zip_uint8_t; +typedef int16_t zip_int16_t; +typedef uint16_t zip_uint16_t; +typedef int32_t zip_int32_t; +typedef uint32_t zip_uint32_t; +typedef int64_t zip_int64_t; +typedef uint64_t zip_uint64_t; + +#define ZIP_INT8_MIN (-ZIP_INT8_MAX-1) +#define ZIP_INT8_MAX 0x7f +#define ZIP_UINT8_MAX 0xff + +#define ZIP_INT16_MIN (-ZIP_INT16_MAX-1) +#define ZIP_INT16_MAX 0x7fff +#define ZIP_UINT16_MAX 0xffff + +#define ZIP_INT32_MIN (-ZIP_INT32_MAX-1L) +#define ZIP_INT32_MAX 0x7fffffffL +#define ZIP_UINT32_MAX 0xffffffffLU + +#define ZIP_INT64_MIN (-ZIP_INT64_MAX-1LL) +#define ZIP_INT64_MAX 0x7fffffffffffffffLL +#define ZIP_UINT64_MAX 0xffffffffffffffffULL + +#endif /* zipconf.h */ diff --git a/ext/libzip/zipint.h b/ext/libzip/zipint.h index 46c7335fee..99c1806133 100644 --- a/ext/libzip/zipint.h +++ b/ext/libzip/zipint.h @@ -3,7 +3,7 @@ /* zipint.h -- internal declarations. - Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -20,7 +20,7 @@ 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -34,256 +34,568 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include +#include "config.h" +#include "compat.h" -// Symbian is nuts -#if !defined(_WIN32) && defined(UNICODE) -#undef UNICODE +#ifdef ZIP_ALLOCATE_BUFFER +#include #endif -#ifdef _MSC_VER -#define ZIP_EXTERN -#define fseeko fseek -#define ftello ftell -#if _MSC_VER < 1900 -#define snprintf _snprintf -#endif -#ifndef strcasecmp -#define strcasecmp _stricmp -#endif -#define umask(x) 0; -#define chmod(...) ; -typedef int mode_t; -// __declspec(dllimport) +#ifndef _ZIP_COMPILING_DEPRECATED +#define ZIP_DISABLE_DEPRECATED #endif #include "zip.h" -#include "config.h" - -#ifndef HAVE_MKSTEMP -#ifdef UNICODE -int _zip_mkstemp(wchar_t *); -#else -int _zip_mkstemp(char *); -#endif -#define mkstemp _zip_mkstemp -#endif - -#ifdef HAVE_MOVEFILEEXA -#include -#define _zip_rename(s, t) \ - (!MoveFileEx((s), (t), \ - MOVEFILE_COPY_ALLOWED|MOVEFILE_REPLACE_EXISTING)) -#else -#define _zip_rename rename -#endif - -#ifndef HAVE_FSEEKO -#define fseeko(s, o, w) (fseek((s), (long int)(o), (w))) -#endif -#ifndef HAVE_FTELLO -#define ftello(s) ((long)ftell((s))) -#endif - - #define CENTRAL_MAGIC "PK\1\2" -#define LOCAL_MAGIC "PK\3\4" -#define EOCD_MAGIC "PK\5\6" -#define DATADES_MAGIC "PK\7\8" -#define TORRENT_SIG "TORRENTZIPPED-" -#define TORRENT_SIG_LEN 14 -#define TORRENT_CRC_LEN 8 -#define TORRENT_MEM_LEVEL 8 -#define CDENTRYSIZE 46u -#define LENTRYSIZE 30 -#define MAXCOMLEN 65536 -#define EOCDLEN 22 -#define CDBUFSIZE (MAXCOMLEN+EOCDLEN) -#define BUFSIZE 8192 +#define LOCAL_MAGIC "PK\3\4" +#define EOCD_MAGIC "PK\5\6" +#define DATADES_MAGIC "PK\7\10" +#define EOCD64LOC_MAGIC "PK\6\7" +#define EOCD64_MAGIC "PK\6\6" +#define CDENTRYSIZE 46u +#define LENTRYSIZE 30 +#define MAXCOMLEN 65536 +#define MAXEXTLEN 65536 +#define EOCDLEN 22 +#define EOCD64LOCLEN 20 +#define EOCD64LEN 56 +#define CDBUFSIZE (MAXCOMLEN + EOCDLEN + EOCD64LOCLEN) +#define BUFSIZE 8192 +#define EFZIP64SIZE 28 +#define EF_WINZIP_AES_SIZE 7 +#define MAX_DATA_DESCRIPTOR_LENGTH 24 - +#define ZIP_CRYPTO_PKWARE_HEADERLEN 12 -/* state of change of a file in zip archive */ +#define ZIP_CM_REPLACED_DEFAULT (-2) +#define ZIP_CM_WINZIP_AES 99 /* Winzip AES encrypted */ -enum zip_state { ZIP_ST_UNCHANGED, ZIP_ST_DELETED, ZIP_ST_REPLACED, - ZIP_ST_ADDED, ZIP_ST_RENAMED }; +#define WINZIP_AES_PASSWORD_VERIFY_LENGTH 2 +#define WINZIP_AES_MAX_HEADER_LENGTH (16 + WINZIP_AES_PASSWORD_VERIFY_LENGTH) +#define AES_BLOCK_SIZE 16 +#define HMAC_LENGTH 10 +#define SHA1_LENGTH 20 +#define SALT_LENGTH(method) ((method) == ZIP_EM_AES_128 ? 8 : ((method) == ZIP_EM_AES_192 ? 12 : 16)) -/* constants for struct zip_file's member flags */ +#define ZIP_CM_IS_DEFAULT(x) ((x) == ZIP_CM_DEFAULT || (x) == ZIP_CM_REPLACED_DEFAULT) +#define ZIP_CM_ACTUAL(x) ((zip_uint16_t)(ZIP_CM_IS_DEFAULT(x) ? ZIP_CM_DEFLATE : (x))) -#define ZIP_ZF_EOF 1 /* EOF reached */ -#define ZIP_ZF_DECOMP 2 /* decompress data */ -#define ZIP_ZF_CRC 4 /* compute and compare CRC */ +#define ZIP_EF_UTF_8_COMMENT 0x6375 +#define ZIP_EF_UTF_8_NAME 0x7075 +#define ZIP_EF_WINZIP_AES 0x9901 +#define ZIP_EF_ZIP64 0x0001 + +#define ZIP_EF_IS_INTERNAL(id) ((id) == ZIP_EF_UTF_8_COMMENT || (id) == ZIP_EF_UTF_8_NAME || (id) == ZIP_EF_WINZIP_AES || (id) == ZIP_EF_ZIP64) + +/* according to unzip-6.0's zipinfo.c, this corresponds to a regular file with rw permissions for everyone */ +#define ZIP_EXT_ATTRIB_DEFAULT (0100666u << 16) +/* according to unzip-6.0's zipinfo.c, this corresponds to a directory with rwx permissions for everyone */ +#define ZIP_EXT_ATTRIB_DEFAULT_DIR (0040777u << 16) + +#define ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK 0x0836 + +#define ZIP_MAX(a, b) ((a) > (b) ? (a) : (b)) +#define ZIP_MIN(a, b) ((a) < (b) ? (a) : (b)) + +/* This section contains API that won't materialize like this. It's + placed in the internal section, pending cleanup. */ + +/* flags for compression and encryption sources */ + +#define ZIP_CODEC_DECODE 0 /* decompress/decrypt (encode flag not set) */ +#define ZIP_CODEC_ENCODE 1 /* compress/encrypt */ + +typedef zip_source_t *(*zip_encryption_implementation)(zip_t *, zip_source_t *, zip_uint16_t, int, const char *); + +zip_encryption_implementation _zip_get_encryption_implementation(zip_uint16_t method, int operation); + +/* clang-format off */ +enum zip_compression_status { + ZIP_COMPRESSION_OK, + ZIP_COMPRESSION_END, + ZIP_COMPRESSION_ERROR, + ZIP_COMPRESSION_NEED_DATA +}; +/* clang-format on */ +typedef enum zip_compression_status zip_compression_status_t; + +struct zip_compression_algorithm { + /* called once to create new context */ + void *(*allocate)(zip_uint16_t method, int compression_flags, zip_error_t *error); + /* called once to free context */ + void (*deallocate)(void *ctx); + + /* get compression specific general purpose bitflags */ + zip_uint16_t (*general_purpose_bit_flags)(void *ctx); + /* minimum version needed when using this algorithm */ + zip_uint8_t version_needed; + + /* start processing */ + bool (*start)(void *ctx); + /* stop processing */ + bool (*end)(void *ctx); + + /* provide new input data, remains valid until next call to input or end */ + bool (*input)(void *ctx, zip_uint8_t *data, zip_uint64_t length); + + /* all input data has been provided */ + void (*end_of_input)(void *ctx); + + /* process input data, writing to data, which has room for length bytes, update length to number of bytes written */ + zip_compression_status_t (*process)(void *ctx, zip_uint8_t *data, zip_uint64_t *length); +}; +typedef struct zip_compression_algorithm zip_compression_algorithm_t; + +extern zip_compression_algorithm_t zip_algorithm_bzip2_compress; +extern zip_compression_algorithm_t zip_algorithm_bzip2_decompress; +extern zip_compression_algorithm_t zip_algorithm_deflate_compress; +extern zip_compression_algorithm_t zip_algorithm_deflate_decompress; +extern zip_compression_algorithm_t zip_algorithm_xz_compress; +extern zip_compression_algorithm_t zip_algorithm_xz_decompress; + + +/* This API is not final yet, but we need it internally, so it's private for now. */ + +const zip_uint8_t *zip_get_extra_field_by_id(zip_t *, int, int, zip_uint16_t, int, zip_uint16_t *); + +/* This section contains API that is of limited use until support for + user-supplied compression/encryption implementation is finished. + Thus we will keep it private for now. */ + +typedef zip_int64_t (*zip_source_layered_callback)(zip_source_t *, void *, void *, zip_uint64_t, enum zip_source_cmd); +zip_source_t *zip_source_compress(zip_t *za, zip_source_t *src, zip_int32_t cm, int compression_flags); +zip_source_t *zip_source_crc(zip_t *, zip_source_t *, int); +zip_source_t *zip_source_decompress(zip_t *za, zip_source_t *src, zip_int32_t cm); +zip_source_t *zip_source_layered(zip_t *, zip_source_t *, zip_source_layered_callback, void *); +zip_source_t *zip_source_layered_create(zip_source_t *src, zip_source_layered_callback cb, void *ud, zip_error_t *error); +zip_source_t *zip_source_pkware_decode(zip_t *, zip_source_t *, zip_uint16_t, int, const char *); +zip_source_t *zip_source_pkware_encode(zip_t *, zip_source_t *, zip_uint16_t, int, const char *); +int zip_source_remove(zip_source_t *); +zip_int64_t zip_source_supports(zip_source_t *src); +zip_source_t *zip_source_window(zip_t *, zip_source_t *, zip_uint64_t, zip_uint64_t); +zip_source_t *zip_source_winzip_aes_decode(zip_t *, zip_source_t *, zip_uint16_t, int, const char *); +zip_source_t *zip_source_winzip_aes_encode(zip_t *, zip_source_t *, zip_uint16_t, int, const char *); +zip_source_t *zip_source_buffer_with_attributes(zip_t *za, const void *data, zip_uint64_t len, int freep, zip_file_attributes_t *attributes); + +/* error source for layered sources */ + +enum zip_les { ZIP_LES_NONE, ZIP_LES_UPPER, ZIP_LES_LOWER, ZIP_LES_INVAL }; /* directory entry: general purpose bit flags */ -#define ZIP_GPBF_ENCRYPTED 0x0001 /* is encrypted */ -#define ZIP_GPBF_DATA_DESCRIPTOR 0x0008 /* crc/size after file data */ -#define ZIP_GPBF_STRONG_ENCRYPTION 0x0040 /* uses strong encryption */ +#define ZIP_GPBF_ENCRYPTED 0x0001u /* is encrypted */ +#define ZIP_GPBF_DATA_DESCRIPTOR 0x0008u /* crc/size after file data */ +#define ZIP_GPBF_STRONG_ENCRYPTION 0x0040u /* uses strong encryption */ +#define ZIP_GPBF_ENCODING_UTF_8 0x0800u /* file name encoding is UTF-8 */ -/* error information */ -struct zip_error { - int zip_err; /* libzip error code (ZIP_ER_*) */ - int sys_err; /* copy of errno (E*) or zlib error code */ - char *str; /* string representation or NULL */ +/* extra fields */ +#define ZIP_EF_LOCAL ZIP_FL_LOCAL /* include in local header */ +#define ZIP_EF_CENTRAL ZIP_FL_CENTRAL /* include in central directory */ +#define ZIP_EF_BOTH (ZIP_EF_LOCAL | ZIP_EF_CENTRAL) /* include in both */ + +#define ZIP_FL_FORCE_ZIP64 1024 /* force zip64 extra field (_zip_dirent_write) */ + +#define ZIP_FL_ENCODING_ALL (ZIP_FL_ENC_GUESS | ZIP_FL_ENC_CP437 | ZIP_FL_ENC_UTF_8) + + +/* encoding type */ +enum zip_encoding_type { + ZIP_ENCODING_UNKNOWN, /* not yet analyzed */ + ZIP_ENCODING_ASCII, /* plain ASCII */ + ZIP_ENCODING_UTF8_KNOWN, /* is UTF-8 */ + ZIP_ENCODING_UTF8_GUESSED, /* possibly UTF-8 */ + ZIP_ENCODING_CP437, /* Code Page 437 */ + ZIP_ENCODING_ERROR /* should be UTF-8 but isn't */ }; +typedef enum zip_encoding_type zip_encoding_type_t; + +struct zip_hash; +struct zip_progress; + +typedef struct zip_cdir zip_cdir_t; +typedef struct zip_dirent zip_dirent_t; +typedef struct zip_entry zip_entry_t; +typedef struct zip_extra_field zip_extra_field_t; +typedef struct zip_string zip_string_t; +typedef struct zip_buffer zip_buffer_t; +typedef struct zip_hash zip_hash_t; +typedef struct zip_progress zip_progress_t; + /* zip archive, part of API */ struct zip { -#ifdef UNICODE - wchar_t *zn; /* file name */ -#else - char *zn; /* file name */ -#endif - FILE *zp; /* file */ - struct zip_error error; /* error information */ + zip_source_t *src; /* data source for archive */ + unsigned int open_flags; /* flags passed to zip_open */ + zip_error_t error; /* error information */ - unsigned int flags; /* archive global flags */ - unsigned int ch_flags; /* changed archive global flags */ + unsigned int flags; /* archive global flags */ + unsigned int ch_flags; /* changed archive global flags */ - struct zip_cdir *cdir; /* central directory */ - char *ch_comment; /* changed archive comment */ - int ch_comment_len; /* length of changed zip archive - * comment, -1 if unchanged */ - int nentry; /* number of entries */ - int nentry_alloc; /* number of entries allocated */ - struct zip_entry *entry; /* entries */ - int nfile; /* number of opened files within archive */ - int nfile_alloc; /* number of files allocated */ - struct zip_file **file; /* opened files within archive */ + char *default_password; /* password used when no other supplied */ + + zip_string_t *comment_orig; /* archive comment */ + zip_string_t *comment_changes; /* changed archive comment */ + bool comment_changed; /* whether archive comment was changed */ + + zip_uint64_t nentry; /* number of entries */ + zip_uint64_t nentry_alloc; /* number of entries allocated */ + zip_entry_t *entry; /* entries */ + + unsigned int nopen_source; /* number of open sources using archive */ + unsigned int nopen_source_alloc; /* number of sources allocated */ + zip_source_t **open_source; /* open sources using archive */ + + zip_hash_t *names; /* hash table for name lookup */ + + zip_progress_t *progress; /* progress callback for zip_close() */ }; /* file in zip archive, part of API */ struct zip_file { - struct zip *za; /* zip archive containing this file */ - struct zip_error error; /* error information */ - int flags; /* -1: eof, >0: error */ - - int method; /* compression method */ - off_t fpos; /* position within zip file (fread/fwrite) */ - unsigned long bytes_left; /* number of bytes left to read */ - unsigned long cbytes_left; /* number of bytes of compressed data left */ - - unsigned long crc; /* CRC so far */ - unsigned long crc_orig; /* CRC recorded in archive */ - - char *buffer; - z_stream *zstr; + zip_t *za; /* zip archive containing this file */ + zip_error_t error; /* error information */ + bool eof; + zip_source_t *src; /* data source */ }; /* zip archive directory entry (central or local) */ +#define ZIP_DIRENT_COMP_METHOD 0x0001u +#define ZIP_DIRENT_FILENAME 0x0002u +#define ZIP_DIRENT_COMMENT 0x0004u +#define ZIP_DIRENT_EXTRA_FIELD 0x0008u +#define ZIP_DIRENT_ATTRIBUTES 0x0010u +#define ZIP_DIRENT_LAST_MOD 0x0020u +#define ZIP_DIRENT_ENCRYPTION_METHOD 0x0040u +#define ZIP_DIRENT_PASSWORD 0x0080u +#define ZIP_DIRENT_ALL ZIP_UINT32_MAX + struct zip_dirent { - unsigned short version_madeby; /* (c) version of creator */ - unsigned short version_needed; /* (cl) version needed to extract */ - unsigned short bitflags; /* (cl) general purpose bit flag */ - unsigned short comp_method; /* (cl) compression method used */ - time_t last_mod; /* (cl) time of last modification */ - unsigned int crc; /* (cl) CRC-32 of uncompressed data */ - unsigned int comp_size; /* (cl) size of commpressed data */ - unsigned int uncomp_size; /* (cl) size of uncommpressed data */ - char *filename; /* (cl) file name (NUL-terminated) */ - unsigned short filename_len; /* (cl) length of filename (w/o NUL) */ - char *extrafield; /* (cl) extra field */ - unsigned short extrafield_len; /* (cl) length of extra field */ - char *comment; /* (c) file comment */ - unsigned short comment_len; /* (c) length of file comment */ - unsigned short disk_number; /* (c) disk number start */ - unsigned short int_attrib; /* (c) internal file attributes */ - unsigned int ext_attrib; /* (c) external file attributes */ - unsigned int offset; /* (c) offset of local header */ + zip_uint32_t changed; + bool local_extra_fields_read; /* whether we already read in local header extra fields */ + bool cloned; /* whether this instance is cloned, and thus shares non-changed strings */ + + bool crc_valid; /* if CRC is valid (sometimes not for encrypted archives) */ + + zip_uint16_t version_madeby; /* (c) version of creator */ + zip_uint16_t version_needed; /* (cl) version needed to extract */ + zip_uint16_t bitflags; /* (cl) general purpose bit flag */ + zip_int32_t comp_method; /* (cl) compression method used (uint16 and ZIP_CM_DEFAULT (-1)) */ + time_t last_mod; /* (cl) time of last modification */ + zip_uint32_t crc; /* (cl) CRC-32 of uncompressed data */ + zip_uint64_t comp_size; /* (cl) size of compressed data */ + zip_uint64_t uncomp_size; /* (cl) size of uncompressed data */ + zip_string_t *filename; /* (cl) file name (NUL-terminated) */ + zip_extra_field_t *extra_fields; /* (cl) extra fields, parsed */ + zip_string_t *comment; /* (c) file comment */ + zip_uint32_t disk_number; /* (c) disk number start */ + zip_uint16_t int_attrib; /* (c) internal file attributes */ + zip_uint32_t ext_attrib; /* (c) external file attributes */ + zip_uint64_t offset; /* (c) offset of local header */ + + zip_uint16_t compression_level; /* level of compression to use (never valid in orig) */ + zip_uint16_t encryption_method; /* encryption method, computed from other fields */ + char *password; /* file specific encryption password */ }; /* zip archive central directory */ struct zip_cdir { - struct zip_dirent *entry; /* directory entries */ - int nentry; /* number of entries */ + zip_entry_t *entry; /* directory entries */ + zip_uint64_t nentry; /* number of entries */ + zip_uint64_t nentry_alloc; /* number of entries allocated */ - unsigned int size; /* size of central direcotry */ - unsigned int offset; /* offset of central directory in file */ - char *comment; /* zip archive comment */ - unsigned short comment_len; /* length of zip archive comment */ + zip_uint64_t size; /* size of central directory */ + zip_uint64_t offset; /* offset of central directory in file */ + zip_string_t *comment; /* zip archive comment */ + bool is_zip64; /* central directory in zip64 format */ }; - +struct zip_extra_field { + zip_extra_field_t *next; + zip_flags_t flags; /* in local/central header */ + zip_uint16_t id; /* header id */ + zip_uint16_t size; /* data size */ + zip_uint8_t *data; +}; + +enum zip_source_write_state { + ZIP_SOURCE_WRITE_CLOSED, /* write is not in progress */ + ZIP_SOURCE_WRITE_OPEN, /* write is in progress */ + ZIP_SOURCE_WRITE_FAILED, /* commit failed, only rollback allowed */ + ZIP_SOURCE_WRITE_REMOVED /* file was removed */ +}; +typedef enum zip_source_write_state zip_source_write_state_t; struct zip_source { - zip_source_callback f; + zip_source_t *src; + union { + zip_source_callback f; + zip_source_layered_callback l; + } cb; void *ud; + zip_error_t error; + zip_int64_t supports; /* supported commands */ + unsigned int open_count; /* number of times source was opened (directly or as lower layer) */ + zip_source_write_state_t write_state; /* whether source is open for writing */ + bool source_closed; /* set if source archive is closed */ + zip_t *source_archive; /* zip archive we're reading from, NULL if not from archive */ + unsigned int refcount; + bool eof; /* EOF reached */ + bool had_read_error; /* a previous ZIP_SOURCE_READ reported an error */ }; +#define ZIP_SOURCE_IS_OPEN_READING(src) ((src)->open_count > 0) +#define ZIP_SOURCE_IS_OPEN_WRITING(src) ((src)->write_state == ZIP_SOURCE_WRITE_OPEN) +#define ZIP_SOURCE_IS_LAYERED(src) ((src)->src != NULL) + /* entry in zip archive directory */ struct zip_entry { - enum zip_state state; - struct zip_source *source; - char *ch_filename; - char *ch_comment; - int ch_comment_len; + zip_dirent_t *orig; + zip_dirent_t *changes; + zip_source_t *source; + bool deleted; }; - -extern const char * const _zip_err_str[]; +/* file or archive comment, or filename */ + +struct zip_string { + zip_uint8_t *raw; /* raw string */ + zip_uint16_t length; /* length of raw string */ + enum zip_encoding_type encoding; /* autorecognized encoding */ + zip_uint8_t *converted; /* autoconverted string */ + zip_uint32_t converted_length; /* length of converted */ +}; + + +/* byte array */ + +/* For performance, we usually keep 8k byte arrays on the stack. + However, there are (embedded) systems with a stack size of 12k; + for those, use malloc()/free() */ + +#ifdef ZIP_ALLOCATE_BUFFER +#define DEFINE_BYTE_ARRAY(buf, size) zip_uint8_t *buf +#define byte_array_init(buf, size) (((buf) = (zip_uint8_t *)malloc(size)) != NULL) +#define byte_array_fini(buf) (free(buf)) +#else +#define DEFINE_BYTE_ARRAY(buf, size) zip_uint8_t buf[size] +#define byte_array_init(buf, size) (1) +#define byte_array_fini(buf) ((void)0) +#endif + + +/* bounds checked access to memory buffer */ + +struct zip_buffer { + bool ok; + bool free_data; + + zip_uint8_t *data; + zip_uint64_t size; + zip_uint64_t offset; +}; + +/* which files to write in which order */ + +struct zip_filelist { + zip_uint64_t idx; + /* TODO const char *name; */ +}; + +typedef struct zip_filelist zip_filelist_t; + +struct _zip_winzip_aes; +typedef struct _zip_winzip_aes zip_winzip_aes_t; + +struct _zip_pkware_keys { + zip_uint32_t key[3]; +}; +typedef struct _zip_pkware_keys zip_pkware_keys_t; + +extern const char *const _zip_err_str[]; extern const int _zip_nerr_str; extern const int _zip_err_type[]; - +#define ZIP_MAX(a, b) ((a) > (b) ? (a) : (b)) +#define ZIP_MIN(a, b) ((a) < (b) ? (a) : (b)) -#define ZIP_ENTRY_DATA_CHANGED(x) \ - ((x)->state == ZIP_ST_REPLACED \ - || (x)->state == ZIP_ST_ADDED) +#define ZIP_ENTRY_CHANGED(e, f) ((e)->changes && ((e)->changes->changed & (f))) +#define ZIP_ENTRY_DATA_CHANGED(x) ((x)->source != NULL) +#define ZIP_ENTRY_HAS_CHANGES(e) (ZIP_ENTRY_DATA_CHANGED(e) || (e)->deleted || ZIP_ENTRY_CHANGED((e), ZIP_DIRENT_ALL)) - +#define ZIP_IS_RDONLY(za) ((za)->ch_flags & ZIP_AFL_RDONLY) -int _zip_cdir_compute_crc(struct zip *, uLong *); -void _zip_cdir_free(struct zip_cdir *); -int _zip_cdir_grow(struct zip_cdir *, int, struct zip_error *); -struct zip_cdir *_zip_cdir_new(int, struct zip_error *); -int _zip_cdir_write(struct zip_cdir *, FILE *, struct zip_error *); -void _zip_dirent_finalize(struct zip_dirent *); -void _zip_dirent_init(struct zip_dirent *); -int _zip_dirent_read(struct zip_dirent *, FILE *, unsigned char **, - unsigned int *, int, struct zip_error *); -void _zip_dirent_torrent_normalize(struct zip_dirent *); -int _zip_dirent_write(struct zip_dirent *, FILE *, int, struct zip_error *); +#ifdef HAVE_EXPLICIT_MEMSET +#define _zip_crypto_clear(b, l) explicit_memset((b), 0, (l)) +#else +#ifdef HAVE_EXPLICIT_BZERO +#define _zip_crypto_clear(b, l) explicit_bzero((b), (l)) +#else +#include +#define _zip_crypto_clear(b, l) memset((b), 0, (l)) +#endif +#endif -void _zip_entry_free(struct zip_entry *); -void _zip_entry_init(struct zip *, int); -struct zip_entry *_zip_entry_new(struct zip *); -void _zip_error_clear(struct zip_error *); -void _zip_error_copy(struct zip_error *, struct zip_error *); -void _zip_error_fini(struct zip_error *); -void _zip_error_get(struct zip_error *, int *, int *); -void _zip_error_init(struct zip_error *); -void _zip_error_set(struct zip_error *, int, int); -const char *_zip_error_strerror(struct zip_error *); +zip_int64_t _zip_add_entry(zip_t *); -int _zip_file_fillbuf(void *, size_t, struct zip_file *); -unsigned int _zip_file_get_offset(struct zip *, int); +zip_uint8_t *_zip_buffer_data(zip_buffer_t *buffer); +bool _zip_buffer_eof(zip_buffer_t *buffer); +void _zip_buffer_free(zip_buffer_t *buffer); +zip_uint8_t *_zip_buffer_get(zip_buffer_t *buffer, zip_uint64_t length); +zip_uint16_t _zip_buffer_get_16(zip_buffer_t *buffer); +zip_uint32_t _zip_buffer_get_32(zip_buffer_t *buffer); +zip_uint64_t _zip_buffer_get_64(zip_buffer_t *buffer); +zip_uint8_t _zip_buffer_get_8(zip_buffer_t *buffer); +zip_uint64_t _zip_buffer_left(zip_buffer_t *buffer); +zip_buffer_t *_zip_buffer_new(zip_uint8_t *data, zip_uint64_t size); +zip_buffer_t *_zip_buffer_new_from_source(zip_source_t *src, zip_uint64_t size, zip_uint8_t *buf, zip_error_t *error); +zip_uint64_t _zip_buffer_offset(zip_buffer_t *buffer); +bool _zip_buffer_ok(zip_buffer_t *buffer); +zip_uint8_t *_zip_buffer_peek(zip_buffer_t *buffer, zip_uint64_t length); +int _zip_buffer_put(zip_buffer_t *buffer, const void *src, size_t length); +int _zip_buffer_put_16(zip_buffer_t *buffer, zip_uint16_t i); +int _zip_buffer_put_32(zip_buffer_t *buffer, zip_uint32_t i); +int _zip_buffer_put_64(zip_buffer_t *buffer, zip_uint64_t i); +int _zip_buffer_put_8(zip_buffer_t *buffer, zip_uint8_t i); +zip_uint64_t _zip_buffer_read(zip_buffer_t *buffer, zip_uint8_t *data, zip_uint64_t length); +int _zip_buffer_skip(zip_buffer_t *buffer, zip_uint64_t length); +int _zip_buffer_set_offset(zip_buffer_t *buffer, zip_uint64_t offset); +zip_uint64_t _zip_buffer_size(zip_buffer_t *buffer); -int _zip_filerange_crc(FILE *, off_t, off_t, uLong *, struct zip_error *); +void _zip_cdir_free(zip_cdir_t *); +bool _zip_cdir_grow(zip_cdir_t *cd, zip_uint64_t additional_entries, zip_error_t *error); +zip_cdir_t *_zip_cdir_new(zip_uint64_t, zip_error_t *); +zip_int64_t _zip_cdir_write(zip_t *za, const zip_filelist_t *filelist, zip_uint64_t survivors); +time_t _zip_d2u_time(zip_uint16_t, zip_uint16_t); +void _zip_deregister_source(zip_t *za, zip_source_t *src); -struct zip_source *_zip_source_file_or_p(struct zip *, const char *, FILE *, - off_t, off_t); +void _zip_dirent_apply_attributes(zip_dirent_t *, zip_file_attributes_t *, bool, zip_uint32_t); +zip_dirent_t *_zip_dirent_clone(const zip_dirent_t *); +void _zip_dirent_free(zip_dirent_t *); +void _zip_dirent_finalize(zip_dirent_t *); +void _zip_dirent_init(zip_dirent_t *); +bool _zip_dirent_needs_zip64(const zip_dirent_t *, zip_flags_t); +zip_dirent_t *_zip_dirent_new(void); +zip_int64_t _zip_dirent_read(zip_dirent_t *zde, zip_source_t *src, zip_buffer_t *buffer, bool local, zip_error_t *error); +void _zip_dirent_set_version_needed(zip_dirent_t *de, bool force_zip64); +zip_int32_t _zip_dirent_size(zip_source_t *src, zip_uint16_t, zip_error_t *); +int _zip_dirent_write(zip_t *za, zip_dirent_t *dirent, zip_flags_t flags); -void _zip_free(struct zip *); -const char *_zip_get_name(struct zip *, int, int, struct zip_error *); -int _zip_local_header_read(struct zip *, int); -void *_zip_memdup(const void *, size_t, struct zip_error *); -int _zip_name_locate(struct zip *, const char *, int, struct zip_error *); -struct zip *_zip_new(struct zip_error *); -unsigned short _zip_read2(unsigned char **); -unsigned int _zip_read4(unsigned char **); -int _zip_replace(struct zip *, int, const char *, struct zip_source *); -int _zip_set_name(struct zip *, int, const char *); -int _zip_unchange(struct zip *, int, int); -void _zip_unchange_data(struct zip_entry *); +zip_extra_field_t *_zip_ef_clone(const zip_extra_field_t *, zip_error_t *); +zip_extra_field_t *_zip_ef_delete_by_id(zip_extra_field_t *, zip_uint16_t, zip_uint16_t, zip_flags_t); +void _zip_ef_free(zip_extra_field_t *); +const zip_uint8_t *_zip_ef_get_by_id(const zip_extra_field_t *, zip_uint16_t *, zip_uint16_t, zip_uint16_t, zip_flags_t, zip_error_t *); +zip_extra_field_t *_zip_ef_merge(zip_extra_field_t *, zip_extra_field_t *); +zip_extra_field_t *_zip_ef_new(zip_uint16_t, zip_uint16_t, const zip_uint8_t *, zip_flags_t); +bool _zip_ef_parse(const zip_uint8_t *, zip_uint16_t, zip_flags_t, zip_extra_field_t **, zip_error_t *); +zip_extra_field_t *_zip_ef_remove_internal(zip_extra_field_t *); +zip_uint16_t _zip_ef_size(const zip_extra_field_t *, zip_flags_t); +int _zip_ef_write(zip_t *za, const zip_extra_field_t *ef, zip_flags_t flags); + +void _zip_entry_finalize(zip_entry_t *); +void _zip_entry_init(zip_entry_t *); + +void _zip_error_clear(zip_error_t *); +void _zip_error_get(const zip_error_t *, int *, int *); + +void _zip_error_copy(zip_error_t *dst, const zip_error_t *src); +void _zip_error_set_from_source(zip_error_t *, zip_source_t *); + +const zip_uint8_t *_zip_extract_extra_field_by_id(zip_error_t *, zip_uint16_t, int, const zip_uint8_t *, zip_uint16_t, zip_uint16_t *); + +int _zip_file_extra_field_prepare_for_change(zip_t *, zip_uint64_t); +int _zip_file_fillbuf(void *, size_t, zip_file_t *); +zip_uint64_t _zip_file_get_end(const zip_t *za, zip_uint64_t index, zip_error_t *error); +zip_uint64_t _zip_file_get_offset(const zip_t *, zip_uint64_t, zip_error_t *); + +zip_dirent_t *_zip_get_dirent(zip_t *, zip_uint64_t, zip_flags_t, zip_error_t *); + +enum zip_encoding_type _zip_guess_encoding(zip_string_t *, enum zip_encoding_type); +zip_uint8_t *_zip_cp437_to_utf8(const zip_uint8_t *const, zip_uint32_t, zip_uint32_t *, zip_error_t *); + +bool _zip_hash_add(zip_hash_t *hash, const zip_uint8_t *name, zip_uint64_t index, zip_flags_t flags, zip_error_t *error); +bool _zip_hash_delete(zip_hash_t *hash, const zip_uint8_t *key, zip_error_t *error); +void _zip_hash_free(zip_hash_t *hash); +zip_int64_t _zip_hash_lookup(zip_hash_t *hash, const zip_uint8_t *name, zip_flags_t flags, zip_error_t *error); +zip_hash_t *_zip_hash_new(zip_error_t *error); +bool _zip_hash_reserve_capacity(zip_hash_t *hash, zip_uint64_t capacity, zip_error_t *error); +bool _zip_hash_revert(zip_hash_t *hash, zip_error_t *error); + +int _zip_mkstempm(char *path, int mode); + +zip_t *_zip_open(zip_source_t *, unsigned int, zip_error_t *); + +void _zip_progress_end(zip_progress_t *progress); +void _zip_progress_free(zip_progress_t *progress); +int _zip_progress_start(zip_progress_t *progress); +int _zip_progress_subrange(zip_progress_t *progress, double start, double end); +int _zip_progress_update(zip_progress_t *progress, double value); + +/* this symbol is extern so it can be overridden for regression testing */ +ZIP_EXTERN bool zip_secure_random(zip_uint8_t *buffer, zip_uint16_t length); +zip_uint32_t zip_random_uint32(void); + +int _zip_read(zip_source_t *src, zip_uint8_t *data, zip_uint64_t length, zip_error_t *error); +int _zip_read_at_offset(zip_source_t *src, zip_uint64_t offset, unsigned char *b, size_t length, zip_error_t *error); +zip_uint8_t *_zip_read_data(zip_buffer_t *buffer, zip_source_t *src, size_t length, bool nulp, zip_error_t *error); +int _zip_read_local_ef(zip_t *, zip_uint64_t); +zip_string_t *_zip_read_string(zip_buffer_t *buffer, zip_source_t *src, zip_uint16_t length, bool nulp, zip_error_t *error); +int _zip_register_source(zip_t *za, zip_source_t *src); + +void _zip_set_open_error(int *zep, const zip_error_t *err, int ze); + +bool zip_source_accept_empty(zip_source_t *src); +zip_int64_t _zip_source_call(zip_source_t *src, void *data, zip_uint64_t length, zip_source_cmd_t command); +bool _zip_source_eof(zip_source_t *); +zip_source_t *_zip_source_file_or_p(const char *, FILE *, zip_uint64_t, zip_int64_t, const zip_stat_t *, zip_error_t *error); +bool _zip_source_had_error(zip_source_t *); +void _zip_source_invalidate(zip_source_t *src); +zip_source_t *_zip_source_new(zip_error_t *error); +int _zip_source_set_source_archive(zip_source_t *, zip_t *); +zip_source_t *_zip_source_window_new(zip_source_t *src, zip_uint64_t start, zip_uint64_t length, zip_stat_t *st, zip_file_attributes_t *attributes, zip_t *source_archive, zip_uint64_t source_index, zip_error_t *error); +zip_source_t *_zip_source_zip_new(zip_t *, zip_t *, zip_uint64_t, zip_flags_t, zip_uint64_t, zip_uint64_t, const char *); + +int _zip_stat_merge(zip_stat_t *dst, const zip_stat_t *src, zip_error_t *error); +int _zip_string_equal(const zip_string_t *, const zip_string_t *); +void _zip_string_free(zip_string_t *); +zip_uint32_t _zip_string_crc32(const zip_string_t *); +const zip_uint8_t *_zip_string_get(zip_string_t *, zip_uint32_t *, zip_flags_t, zip_error_t *); +zip_uint16_t _zip_string_length(const zip_string_t *); +zip_string_t *_zip_string_new(const zip_uint8_t *, zip_uint16_t, zip_flags_t, zip_error_t *); +int _zip_string_write(zip_t *za, const zip_string_t *string); +bool _zip_winzip_aes_decrypt(zip_winzip_aes_t *ctx, zip_uint8_t *data, zip_uint64_t length); +bool _zip_winzip_aes_encrypt(zip_winzip_aes_t *ctx, zip_uint8_t *data, zip_uint64_t length); +bool _zip_winzip_aes_finish(zip_winzip_aes_t *ctx, zip_uint8_t *hmac); +void _zip_winzip_aes_free(zip_winzip_aes_t *ctx); +zip_winzip_aes_t *_zip_winzip_aes_new(const zip_uint8_t *password, zip_uint64_t password_length, const zip_uint8_t *salt, zip_uint16_t key_size, zip_uint8_t *password_verify, zip_error_t *error); + +void _zip_pkware_encrypt(zip_pkware_keys_t *keys, zip_uint8_t *out, const zip_uint8_t *in, zip_uint64_t len); +void _zip_pkware_decrypt(zip_pkware_keys_t *keys, zip_uint8_t *out, const zip_uint8_t *in, zip_uint64_t len); +zip_pkware_keys_t *_zip_pkware_keys_new(zip_error_t *error); +void _zip_pkware_keys_free(zip_pkware_keys_t *keys); +void _zip_pkware_keys_reset(zip_pkware_keys_t *keys); + +int _zip_changed(const zip_t *, zip_uint64_t *); +const char *_zip_get_name(zip_t *, zip_uint64_t, zip_flags_t, zip_error_t *); +int _zip_local_header_read(zip_t *, int); +void *_zip_memdup(const void *, size_t, zip_error_t *); +zip_int64_t _zip_name_locate(zip_t *, const char *, zip_flags_t, zip_error_t *); +zip_t *_zip_new(zip_error_t *); + +zip_int64_t _zip_file_replace(zip_t *, zip_uint64_t, const char *, zip_source_t *, zip_flags_t); +int _zip_set_name(zip_t *, zip_uint64_t, const char *, zip_flags_t); +void _zip_u2d_time(time_t, zip_uint16_t *, zip_uint16_t *); +int _zip_unchange(zip_t *, zip_uint64_t, int); +void _zip_unchange_data(zip_entry_t *); +int _zip_write(zip_t *za, const void *data, zip_uint64_t length); #endif /* zipint.h */ diff --git a/ext/libzstd.vcxproj b/ext/libzstd.vcxproj new file mode 100644 index 0000000000..7cd642f51c --- /dev/null +++ b/ext/libzstd.vcxproj @@ -0,0 +1,406 @@ + + + + + Debug + ARM + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {8BFD8150-94D5-4BF9-8A50-7BD9929A0850} + Win32Proj + libzstd + libzstd_static + Unicode + StaticLibrary + $(SolutionDir)..\ext\zstd\bin\$(Platform)_$(Configuration)\ + $(SolutionDir)..\ext\zstd\bin\obj\$(RootNamespace)_$(Platform)_$(Configuration)\ + 10.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(DefaultPlatformToolset) + + + true + $(DefaultPlatformToolset) + + + true + $(DefaultPlatformToolset) + + + true + $(DefaultPlatformToolset) + + + false + true + $(DefaultPlatformToolset) + + + false + true + $(DefaultPlatformToolset) + + + false + true + $(DefaultPlatformToolset) + + + false + true + $(DefaultPlatformToolset) + + + + + + + + + + + + + + + + + + + true + $(IncludePath);$(SolutionDir)..\ext\zstd\lib;$(SolutionDir)..\ext\zstd\programs\legacy;$(SolutionDir)..\ext\zstd\lib\legacy;$(SolutionDir)..\ext\zstd\lib\common;$(SolutionDir)..\ext\zstd\lib\dictBuilder;$(UniversalCRT_IncludePath); + false + + + true + $(IncludePath);$(SolutionDir)..\ext\zstd\lib;$(SolutionDir)..\ext\zstd\programs\legacy;$(SolutionDir)..\ext\zstd\lib\legacy;$(SolutionDir)..\ext\zstd\lib\common;$(SolutionDir)..\ext\zstd\lib\dictBuilder;$(UniversalCRT_IncludePath); + false + + + false + $(IncludePath);$(SolutionDir)..\ext\zstd\lib;$(SolutionDir)..\ext\zstd\programs\legacy;$(SolutionDir)..\ext\zstd\lib\legacy;$(SolutionDir)..\ext\zstd\lib\common;$(SolutionDir)..\ext\zstd\lib\dictBuilder;$(UniversalCRT_IncludePath); + false + + + false + $(IncludePath);$(SolutionDir)..\ext\zstd\lib;$(SolutionDir)..\ext\zstd\programs\legacy;$(SolutionDir)..\ext\zstd\lib\legacy;$(SolutionDir)..\ext\zstd\lib\common;$(SolutionDir)..\ext\zstd\lib\dictBuilder;$(UniversalCRT_IncludePath); + false + + + + $(OutDir)$(TargetName).pdb + + + + + + + Level4 + ZSTD_MULTITHREAD=1;ZSTD_LEGACY_SUPPORT=5;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + true + ProgramDatabase + false + + + Windows + true + MachineX86 + + + + + + + Level4 + ZSTD_MULTITHREAD=1;ZSTD_LEGACY_SUPPORT=5;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + true + ProgramDatabase + false + + + Windows + true + + + + + + + Level4 + ZSTD_MULTITHREAD=1;ZSTD_LEGACY_SUPPORT=5;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + true + ProgramDatabase + false + + + Windows + true + + + + + + + Level4 + ZSTD_MULTITHREAD=1;ZSTD_LEGACY_SUPPORT=5;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + true + ProgramDatabase + false + + + Windows + true + + + + + Level4 + + + MaxSpeed + true + true + ZSTD_MULTITHREAD=1;ZSTD_LEGACY_SUPPORT=5;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + false + MultiThreaded + true + ProgramDatabase + AnySuitable + Speed + true + false + + + Windows + true + true + true + MachineX86 + + + + + Level4 + + + MaxSpeed + true + true + ZSTD_MULTITHREAD=1;ZSTD_LEGACY_SUPPORT=5;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + false + false + MultiThreaded + true + ProgramDatabase + false + true + AnySuitable + Speed + + + Windows + true + true + true + + + + + Level4 + + + MaxSpeed + true + true + ZSTD_MULTITHREAD=1;ZSTD_LEGACY_SUPPORT=5;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + false + false + MultiThreaded + true + ProgramDatabase + false + true + AnySuitable + Speed + + + Windows + true + true + true + + + + + Level4 + + + MaxSpeed + true + true + ZSTD_MULTITHREAD=1;ZSTD_LEGACY_SUPPORT=5;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + false + false + MultiThreaded + true + ProgramDatabase + false + true + AnySuitable + Speed + + + Windows + true + true + true + + + + + + \ No newline at end of file diff --git a/ext/miniupnp b/ext/miniupnp index 7e229ddd63..3a87be33e7 160000 --- a/ext/miniupnp +++ b/ext/miniupnp @@ -1 +1 @@ -Subproject commit 7e229ddd635933239583ab190d9b614bde018157 +Subproject commit 3a87be33e797ba947b2b2a5f8d087f6c3ff4d93e diff --git a/ext/miniupnp-build/Android.mk b/ext/miniupnp-build/Android.mk index 5412b339d4..de01487070 100644 --- a/ext/miniupnp-build/Android.mk +++ b/ext/miniupnp-build/Android.mk @@ -7,6 +7,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := libminiupnp-build LOCAL_ARM_MODE := arm LOCAL_SRC_FILES := \ + ../miniupnp/miniupnpc/addr_is_reserved.c \ ../miniupnp/miniupnpc/connecthostport.c \ ../miniupnp/miniupnpc/igd_desc_parse.c \ ../miniupnp/miniupnpc/minisoap.c \ diff --git a/ext/miniupnpc.vcxproj b/ext/miniupnpc.vcxproj index b8dd274e50..4181392fb3 100644 --- a/ext/miniupnpc.vcxproj +++ b/ext/miniupnpc.vcxproj @@ -35,6 +35,7 @@ + @@ -50,6 +51,7 @@ + diff --git a/ext/miniupnpc.vcxproj.filters b/ext/miniupnpc.vcxproj.filters index bda1ac6d7d..2106988bbf 100644 --- a/ext/miniupnpc.vcxproj.filters +++ b/ext/miniupnpc.vcxproj.filters @@ -14,6 +14,7 @@ + @@ -30,6 +31,7 @@ + diff --git a/ext/native/tools/atlastool.cpp b/ext/native/tools/atlastool.cpp index 29c866509a..2ca6af0fd7 100644 --- a/ext/native/tools/atlastool.cpp +++ b/ext/native/tools/atlastool.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "Common/Render/TextureAtlas.h" @@ -190,7 +191,7 @@ struct Image { memcpy(image_data + y * width() * 4, &dat[y][0], width() * 4); } FILE *f = fopen(zim_name, "wb"); - // SaveZIM takes ownership voer image_data, there's no leak. + // SaveZIM takes ownership over image_data, there's no leak. ::SaveZIM(f, width(), height(), width() * 4, zim_format | ZIM_DITHER, image_data); fclose(f); } @@ -853,6 +854,30 @@ void GetLocales(const char *locales, std::vector &ranges) std::sort(ranges.begin(), ranges.end()); } +static bool WriteCompressed(const void *src, size_t sz, size_t num, FILE *fp) { + size_t src_size = sz * num; + size_t compressed_size = ZSTD_compressBound(src_size); + uint8_t *compressed = new uint8_t[compressed_size]; + compressed_size = ZSTD_compress(compressed, compressed_size, src, src_size, 22); + if (ZSTD_isError(compressed_size)) { + delete[] compressed; + return false; + } + + uint32_t write_size = (uint32_t)compressed_size; + if (fwrite(&write_size, sizeof(uint32_t), 1, fp) != 1) { + delete[] compressed; + return false; + } + if (fwrite(compressed, 1, compressed_size, fp) != compressed_size) { + delete[] compressed; + return false; + } + + delete[] compressed; + return true; +} + int main(int argc, char **argv) { // initProgram(&argc, const_cast(&argv)); // /usr/share/fonts/truetype/msttcorefonts/Arial_Black.ttf @@ -962,10 +987,10 @@ int main(int argc, char **argv) { vector results = bucket.Resolve(image_width, dest); if (highcolor) { printf("Writing .ZIM %ix%i RGBA8888...\n", dest.width(), dest.height()); - dest.SaveZIM(image_name.c_str(), ZIM_RGBA8888 | ZIM_ZLIB_COMPRESSED); + dest.SaveZIM(image_name.c_str(), ZIM_RGBA8888 | ZIM_ZSTD_COMPRESSED); } else { printf("Writing .ZIM %ix%i RGBA4444...\n", dest.width(), dest.height()); - dest.SaveZIM(image_name.c_str(), ZIM_RGBA4444 | ZIM_ZLIB_COMPRESSED); + dest.SaveZIM(image_name.c_str(), ZIM_RGBA4444 | ZIM_ZSTD_COMPRESSED); } // Also save PNG for debugging. @@ -981,15 +1006,16 @@ int main(int argc, char **argv) { FILE *meta = fopen(meta_name.c_str(), "wb"); AtlasHeader header; header.magic = ATLAS_MAGIC; - header.version = 0; + header.version = 1; header.numFonts = (int)fonts.size(); header.numImages = (int)images.size(); fwrite(&header, 1, sizeof(header), meta); // For each image + AtlasImage *atalas_images = new AtlasImage[images.size()]; for (int i = 0; i < (int)images.size(); i++) { - AtlasImage atlas_image = images[i].ToAtlasImage((float)dest.width(), (float)dest.height(), results); - fwrite(&atlas_image, 1, sizeof(atlas_image), meta); + atalas_images[i] = images[i].ToAtlasImage((float)dest.width(), (float)dest.height(), results); } + WriteCompressed(atalas_images, sizeof(AtlasImage), images.size(), meta); // For each font for (int i = 0; i < (int)fonts.size(); i++) { auto &font = fonts[i]; @@ -997,9 +1023,9 @@ int main(int argc, char **argv) { AtlasFontHeader font_header = font.GetHeader(); fwrite(&font_header, 1, sizeof(font_header), meta); auto ranges = font.GetRanges(); - fwrite(ranges.data(), sizeof(AtlasCharRange), ranges.size(), meta); + WriteCompressed(ranges.data(), sizeof(AtlasCharRange), ranges.size(), meta); auto chars = font.GetChars((float)dest.width(), (float)dest.height(), results); - fwrite(chars.data(), sizeof(AtlasChar), chars.size(), meta); + WriteCompressed(chars.data(), sizeof(AtlasChar), chars.size(), meta); } fclose(meta); } diff --git a/ext/native/tools/atlastool/atlastool.vcxproj b/ext/native/tools/atlastool/atlastool.vcxproj index 08f36e1777..9fb9aabd9f 100644 --- a/ext/native/tools/atlastool/atlastool.vcxproj +++ b/ext/native/tools/atlastool/atlastool.vcxproj @@ -14,6 +14,9 @@ {3fcdbae2-5103-4350-9a8e-848ce9c73195} + + {8bfd8150-94d5-4bf9-8a50-7bd9929a0850} + {f761046e-6c38-4428-a5f1-38391a37bb34} @@ -92,7 +95,7 @@ true _CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true - ../../../../;../../ext;../../;../../../../ext;../prebuilt + ../../../../;../../ext;../../;../../../../ext;../prebuilt;../../../../ext/zstd/lib MultiThreaded false diff --git a/ext/native/tools/zimtool.cpp b/ext/native/tools/zimtool.cpp index 88f9e9da16..b9a24cb30f 100644 --- a/ext/native/tools/zimtool.cpp +++ b/ext/native/tools/zimtool.cpp @@ -14,7 +14,7 @@ const char *format_strings[4] = { "8888", "4444", "565", "ETC1" }; int formats[3] = { ZIM_RGBA8888, ZIM_RGBA4444, ZIM_RGB565 }; void printusage() { - fprintf(stderr, "Usage: zimtool infile.png outfile.zim [-f=FORMAT] [-m] [-g]\n"); + fprintf(stderr, "Usage: zimtool infile.png outfile.zim [-f=FORMAT] [-m] [-g] [-z[LEVEL]]\n"); fprintf(stderr, "Formats: 8888 4444 565 ETC1\n"); } @@ -40,6 +40,7 @@ int main(int argc, char **argv) { } int flags = 0; + int level = 0; bool format_set = false; for (int i = 3; i < argc; i++) { if (argv[i][0] != '-') { @@ -68,6 +69,16 @@ int main(int argc, char **argv) { } } break; + case 'z': + flags |= ZIM_ZSTD_COMPRESSED; + if (argv[i][2] != '\0') { + int pos = 2; + while (argv[i][pos] >= '0' && argv[i][pos] <= '9') { + level = level * 10 + argv[i][pos] - '0'; + pos++; + } + } + break; } } // TODO: make setting? @@ -87,7 +98,7 @@ int main(int argc, char **argv) { } FILE *f = fopen(FLAGS_outfile, "wb"); - SaveZIM(f, width, height, width * 4, flags, image_data); + SaveZIM(f, width, height, width * 4, flags, image_data, level); fclose(f); int in_file_size = filesize(FLAGS_infile); int out_file_size = filesize(FLAGS_outfile); diff --git a/ext/native/tools/zimtool/zimtool.vcxproj b/ext/native/tools/zimtool/zimtool.vcxproj index ee9eca5b90..d667a82ea8 100644 --- a/ext/native/tools/zimtool/zimtool.vcxproj +++ b/ext/native/tools/zimtool/zimtool.vcxproj @@ -14,6 +14,9 @@ {3fcdbae2-5103-4350-9a8e-848ce9c73195} + + {8bfd8150-94d5-4bf9-8a50-7bd9929a0850} + {f761046e-6c38-4428-a5f1-38391a37bb34} diff --git a/ext/zstd b/ext/zstd new file mode 160000 index 0000000000..a488ba114e --- /dev/null +++ b/ext/zstd @@ -0,0 +1 @@ +Subproject commit a488ba114ec17ea1054b9057c26a046fc122b3b6 diff --git a/headless/Compare.cpp b/headless/Compare.cpp index 5c96a7cce4..1b81867a67 100644 --- a/headless/Compare.cpp +++ b/headless/Compare.cpp @@ -19,11 +19,10 @@ #include #include #include -#include #include #include "headless/Compare.h" -#include "Common/ColorConv.h" +#include "Common/Data/Convert/ColorConv.h" #include "Common/Data/Format/PNGLoad.h" #include "Common/File/FileUtil.h" #include "Common/StringUtils.h" @@ -75,7 +74,7 @@ struct BufferedLineReader { const static int MAX_BUFFER = 5; const static int TEMP_BUFFER_SIZE = 32768; - BufferedLineReader(const std::string &data) : valid_(0), data_(data), pos_(0) { + BufferedLineReader(const std::string &data) : data_(data) { } void Fill() { @@ -135,7 +134,7 @@ struct BufferedLineReader { } protected: - BufferedLineReader() : valid_(0) { + BufferedLineReader() { } virtual bool HasMoreLines() { @@ -163,48 +162,23 @@ protected: return s.substr(0, p + 1); } - int valid_; + int valid_ = 0; std::string buffer_[MAX_BUFFER]; const std::string data_; - size_t pos_; + size_t pos_ = 0; }; -struct BufferedLineReaderFile : public BufferedLineReader { - BufferedLineReaderFile(std::ifstream &in) : BufferedLineReader(), in_(in) { +Path ExpectedScreenshotFromFilename(const Path &bootFilename) { + std::string extension = bootFilename.GetFileExtension(); + if (extension.empty()) { + return bootFilename.WithExtraExtension(".bmp"); } -protected: - virtual bool HasMoreLines() { - return !in_.eof(); - } - - virtual std::string ReadLine() { - char temp[TEMP_BUFFER_SIZE]; - in_.getline(temp, TEMP_BUFFER_SIZE); - return temp; - } - - std::ifstream &in_; -}; - -std::string ExpectedFromFilename(const std::string &bootFilename) { - size_t pos = bootFilename.find_last_of('.'); - if (pos == bootFilename.npos) { - return bootFilename + ".expected"; - } - return bootFilename.substr(0, pos) + ".expected"; -} - -std::string ExpectedScreenshotFromFilename(const std::string &bootFilename) { - size_t pos = bootFilename.find_last_of('.'); - if (pos == bootFilename.npos) { - return bootFilename + ".bmp"; - } // Let's use pngs as the default for ppdmp tests. - if (bootFilename.substr(pos) == ".ppdmp") { - return bootFilename.substr(0, pos) + ".png"; + if (extension == ".ppdmp") { + return bootFilename.WithReplacedExtension(".png"); } - return bootFilename.substr(0, pos) + ".expected.bmp"; + return bootFilename.WithReplacedExtension(".expected.bmp"); } static std::string ChopFront(std::string s, std::string front) @@ -228,14 +202,14 @@ static std::string ChopEnd(std::string s, std::string end) return s; } -std::string GetTestName(const std::string &bootFilename) +std::string GetTestName(const Path &bootFilename) { // Kinda ugly, trying to guesstimate the test name from filename... - return ChopEnd(ChopFront(ChopFront(bootFilename, "tests/"), "pspautotests/tests/"), ".prx"); + return ChopEnd(ChopFront(ChopFront(bootFilename.ToString(), "tests/"), "pspautotests/tests/"), ".prx"); } -bool CompareOutput(const std::string &bootFilename, const std::string &output, bool verbose) { - std::string expect_filename = ExpectedFromFilename(bootFilename); +bool CompareOutput(const Path &bootFilename, const std::string &output, bool verbose) { + Path expect_filename = bootFilename.WithReplacedExtension(".prx", ".expected"); std::unique_ptr expect_loader(ConstructFileLoader(expect_filename)); if (expect_loader->Exists()) { @@ -297,7 +271,7 @@ bool CompareOutput(const std::string &bootFilename, const std::string &output, b printf("%s", output.c_str()); printf("============== expected output:\n"); std::string fullExpected; - if (readFileToString(true, expect_filename.c_str(), fullExpected)) + if (File::ReadFileToString(true, expect_filename, fullExpected)) printf("%s", fullExpected.c_str()); printf("===============================\n"); } @@ -391,7 +365,7 @@ std::vector TranslateDebugBufferToCompare(const GPUDebugBuffer *buffer, u32 return data; } -double CompareScreenshot(const std::vector &pixels, u32 stride, u32 w, u32 h, const std::string& screenshotFilename, std::string &error) +double CompareScreenshot(const std::vector &pixels, u32 stride, u32 w, u32 h, const Path& screenshotFilename, std::string &error) { if (pixels.size() < stride * h) { @@ -407,7 +381,7 @@ double CompareScreenshot(const std::vector &pixels, u32 stride, u32 w, u32 if (loader->Exists()) { uint8_t header[2]; if (loader->ReadAt(0, 2, header) != 2) { - error = "Unable to read screenshot data: " + screenshotFilename; + error = "Unable to read screenshot data: " + screenshotFilename.ToVisualString(); return -1.0f; } @@ -416,7 +390,7 @@ double CompareScreenshot(const std::vector &pixels, u32 stride, u32 w, u32 asBitmap = true; // The bitmap header is 14 + 40 bytes. We could validate it but the test would fail either way. if (reference && loader->ReadAt(14 + 40, sizeof(u32), stride * h, reference) != stride * h) { - error = "Unable to read screenshot data: " + screenshotFilename; + error = "Unable to read screenshot data: " + screenshotFilename.ToVisualString(); return -1.0f; } } else { @@ -424,23 +398,23 @@ double CompareScreenshot(const std::vector &pixels, u32 stride, u32 w, u32 std::vector compressed; compressed.resize(loader->FileSize()); if (loader->ReadAt(0, compressed.size(), &compressed[0]) != compressed.size()) { - error = "Unable to read screenshot data: " + screenshotFilename; + error = "Unable to read screenshot data: " + screenshotFilename.ToVisualString(); return -1.0f; } int width, height; if (!pngLoadPtr(&compressed[0], compressed.size(), &width, &height, (unsigned char **)&reference)) { - error = "Unable to read screenshot data: " + screenshotFilename; + error = "Unable to read screenshot data: " + screenshotFilename.ToVisualString(); return -1.0f; } } } else { - error = "Unable to read screenshot: " + screenshotFilename; + error = "Unable to read screenshot: " + screenshotFilename.ToVisualString(); return -1.0f; } if (!reference) { - error = "Unable to allocate screenshot data: " + screenshotFilename; + error = "Unable to allocate screenshot data: " + screenshotFilename.ToVisualString(); return -1.0f; } diff --git a/headless/Compare.h b/headless/Compare.h index fb2da13e81..f1f30f5375 100644 --- a/headless/Compare.h +++ b/headless/Compare.h @@ -19,6 +19,7 @@ #include #include "Common/CommonTypes.h" +#include "Common/File/Path.h" struct GPUDebugBuffer; @@ -27,10 +28,10 @@ extern std::string currentTestName; void TeamCityPrint(const char *fmt, ...); void GitHubActionsPrint(const char *type, const char *fmt, ...); -std::string ExpectedFromFilename(const std::string &bootFilename); -std::string ExpectedScreenshotFromFilename(const std::string &bootFilename); -std::string GetTestName(const std::string &bootFilename); +Path ExpectedFromFilename(const Path &bootFilename); +Path ExpectedScreenshotFromFilename(const Path &bootFilename); +std::string GetTestName(const Path &bootFilename); -bool CompareOutput(const std::string &bootFilename, const std::string &output, bool verbose); +bool CompareOutput(const Path &bootFilename, const std::string &output, bool verbose); std::vector TranslateDebugBufferToCompare(const GPUDebugBuffer *buffer, u32 stride, u32 h); -double CompareScreenshot(const std::vector &pixels, u32 stride, u32 w, u32 h, const std::string& screenshotFilename, std::string &error); +double CompareScreenshot(const std::vector &pixels, u32 stride, u32 w, u32 h, const Path &screenshotFilename, std::string &error); diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 234b7b5ddb..eb6f1f5436 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -160,7 +160,7 @@ bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, bool std::string error_string; if (!PSP_Init(coreParameter, &error_string)) { - fprintf(stderr, "Failed to start %s. Error: %s\n", coreParameter.fileToStart.c_str(), error_string.c_str()); + fprintf(stderr, "Failed to start '%s'. Error: %s\n", coreParameter.fileToStart.c_str(), error_string.c_str()); printf("TESTERROR\n"); TeamCityPrint("testIgnored name='%s' message='PRX/ELF missing'", currentTestName.c_str()); GitHubActionsPrint("error", "PRX/ELF missing for %s", currentTestName.c_str()); @@ -245,9 +245,9 @@ int main(int argc, const char* argv[]) int debuggerPort = -1; std::vector testFilenames; - const char *mountIso = 0; - const char *mountRoot = 0; - const char *screenshotFilename = 0; + const char *mountIso = nullptr; + const char *mountRoot = nullptr; + const char *screenshotFilename = nullptr; float timeout = std::numeric_limits::infinity(); for (int i = 1; i < argc; i++) @@ -355,8 +355,8 @@ int main(int argc, const char* argv[]) coreParameter.gpuCore = glWorking ? gpuCore : GPUCORE_SOFTWARE; coreParameter.graphicsContext = graphicsContext; coreParameter.enableSound = false; - coreParameter.mountIso = mountIso ? mountIso : ""; - coreParameter.mountRoot = mountRoot ? mountRoot : ""; + coreParameter.mountIso = mountIso ? Path(std::string(mountIso)) : Path(); + coreParameter.mountRoot = mountRoot ? Path(std::string(mountRoot)) : Path(); coreParameter.startBreak = false; coreParameter.printfEmuLog = !autoCompare; coreParameter.headLess = true; @@ -405,34 +405,34 @@ int main(int argc, const char* argv[]) g_Config.iPSPModel = PSP_MODEL_SLIM; #ifdef _WIN32 - g_Config.internalDataDirectory = ""; + g_Config.internalDataDirectory.clear(); InitSysDirectories(); #endif #if !defined(__ANDROID__) && !defined(_WIN32) - g_Config.memStickDirectory = std::string(getenv("HOME")) + "/.ppsspp/"; + g_Config.memStickDirectory = Path(std::string(getenv("HOME"))) / ".ppsspp"; #endif // Try to find the flash0 directory. Often this is from a subdirectory. for (int i = 0; i < 4 && !File::Exists(g_Config.flash0Directory); ++i) { - if (File::Exists(g_Config.flash0Directory + "../assets/flash0/")) - g_Config.flash0Directory += "../assets/flash0/"; + if (File::Exists(g_Config.flash0Directory / ".." / "assets/flash0")) + g_Config.flash0Directory = g_Config.flash0Directory / ".." / "assets/flash0"; else - g_Config.flash0Directory += "../../flash0/"; + g_Config.flash0Directory = g_Config.flash0Directory / ".." / ".." / "flash0"; } // Or else, maybe in the executable's dir. if (!File::Exists(g_Config.flash0Directory)) - g_Config.flash0Directory = File::GetExeDirectory() + "assets/flash0/"; + g_Config.flash0Directory = File::GetExeDirectory() / "assets/flash0"; if (screenshotFilename != 0) - headlessHost->SetComparisonScreenshot(screenshotFilename); + headlessHost->SetComparisonScreenshot(Path(std::string(screenshotFilename))); #ifdef __ANDROID__ // For some reason the debugger installs it with this name? - if (File::Exists("/data/app/org.ppsspp.ppsspp-2.apk")) { + if (File::Exists(Path("/data/app/org.ppsspp.ppsspp-2.apk"))) { VFSRegister("", new ZipAssetReader("/data/app/org.ppsspp.ppsspp-2.apk", "assets/")); } - if (File::Exists("/data/app/org.ppsspp.ppsspp.apk")) { + if (File::Exists(Path("/data/app/org.ppsspp.ppsspp.apk"))) { VFSRegister("", new ZipAssetReader("/data/app/org.ppsspp.ppsspp.apk", "assets/")); } #endif @@ -446,13 +446,13 @@ int main(int argc, const char* argv[]) } if (stateToLoad != NULL) - SaveState::Load(stateToLoad, -1); + SaveState::Load(Path(stateToLoad), -1); std::vector failedTests; std::vector passedTests; for (size_t i = 0; i < testFilenames.size(); ++i) { - coreParameter.fileToStart = testFilenames[i]; + coreParameter.fileToStart = Path(testFilenames[i]); if (autoCompare) printf("%s:\n", coreParameter.fileToStart.c_str()); bool passed = RunAutoTest(headlessHost, coreParameter, autoCompare, verbose, timeout); diff --git a/headless/Headless.vcxproj b/headless/Headless.vcxproj index c5cfc506c3..8bca7efb04 100644 --- a/headless/Headless.vcxproj +++ b/headless/Headless.vcxproj @@ -497,6 +497,9 @@ {3baae095-e0ab-4b0e-b5df-ce39c8ae31de} + + {8bfd8150-94d5-4bf9-8a50-7bd9929a0850} + {457f45d2-556f-47bc-a31d-aff0d15beaed} @@ -513,4 +516,4 @@ - + \ No newline at end of file diff --git a/headless/SDLHeadlessHost.cpp b/headless/SDLHeadlessHost.cpp index b57d02a5cd..3f53c01b91 100644 --- a/headless/SDLHeadlessHost.cpp +++ b/headless/SDLHeadlessHost.cpp @@ -103,9 +103,9 @@ private: }; void SDLHeadlessHost::LoadNativeAssets() { - VFSRegister("", new DirectoryAssetReader("assets/")); - VFSRegister("", new DirectoryAssetReader("")); - VFSRegister("", new DirectoryAssetReader("../")); + VFSRegister("", new DirectoryAssetReader(Path("assets"))); + VFSRegister("", new DirectoryAssetReader(Path(""))); + VFSRegister("", new DirectoryAssetReader(Path(".."))); } bool GLDummyGraphicsContext::InitFromRenderThread(std::string *errorMessage) { diff --git a/headless/StubHost.cpp b/headless/StubHost.cpp index 27832dc2a0..e2331d1e48 100644 --- a/headless/StubHost.cpp +++ b/headless/StubHost.cpp @@ -71,7 +71,7 @@ void HeadlessHost::SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; - FILE *saved = File::OpenCFile("__testfailure.bmp", "wb"); + FILE *saved = File::OpenCFile(Path("__testfailure.bmp"), "wb"); if (saved) { fwrite(&header, sizeof(header), 1, saved); fwrite(pixels.data(), sizeof(u32), FRAME_STRIDE * FRAME_HEIGHT, saved); diff --git a/headless/StubHost.h b/headless/StubHost.h index 571444ce0b..c914f025b2 100644 --- a/headless/StubHost.h +++ b/headless/StubHost.h @@ -17,6 +17,8 @@ #pragma once +#include "Common/File/Path.h" + #include "Core/CoreParameter.h" #include "Core/Host.h" #include "Core/Debugger/SymbolMap.h" @@ -66,7 +68,7 @@ public: } } - virtual void SetComparisonScreenshot(const std::string &filename) { + virtual void SetComparisonScreenshot(const Path &filename) { comparisonScreenshot_ = filename; } @@ -80,7 +82,7 @@ public: protected: void SendOrCollectDebugOutput(const std::string &output); - std::string comparisonScreenshot_; + Path comparisonScreenshot_; std::string debugOutputBuffer_; GPUCore gpuCore_; GraphicsContext *gfx_ = nullptr; diff --git a/headless/WindowsHeadlessHost.cpp b/headless/WindowsHeadlessHost.cpp index 98ea0a4462..32624f000d 100644 --- a/headless/WindowsHeadlessHost.cpp +++ b/headless/WindowsHeadlessHost.cpp @@ -68,11 +68,11 @@ HWND CreateHiddenWindow() { void WindowsHeadlessHost::LoadNativeAssets() { - VFSRegister("", new DirectoryAssetReader("assets/")); - VFSRegister("", new DirectoryAssetReader("")); - VFSRegister("", new DirectoryAssetReader("../")); - VFSRegister("", new DirectoryAssetReader("../Windows/assets/")); - VFSRegister("", new DirectoryAssetReader("../Windows/")); + VFSRegister("", new DirectoryAssetReader(Path("assets"))); + VFSRegister("", new DirectoryAssetReader(Path(""))); + VFSRegister("", new DirectoryAssetReader(Path(".."))); + VFSRegister("", new DirectoryAssetReader(Path("../Windows/assets"))); + VFSRegister("", new DirectoryAssetReader(Path("../Windows"))); } void WindowsHeadlessHost::SendDebugOutput(const std::string &output) diff --git a/icons/hicolor/128x128/apps/ppsspp.png b/icons/hicolor/128x128/apps/ppsspp.png index a2e0385376..9ffcbdc1d8 100644 Binary files a/icons/hicolor/128x128/apps/ppsspp.png and b/icons/hicolor/128x128/apps/ppsspp.png differ diff --git a/icons/hicolor/16x16/apps/ppsspp.png b/icons/hicolor/16x16/apps/ppsspp.png index e626f220a2..19180887c7 100644 Binary files a/icons/hicolor/16x16/apps/ppsspp.png and b/icons/hicolor/16x16/apps/ppsspp.png differ diff --git a/icons/hicolor/24x24/apps/ppsspp.png b/icons/hicolor/24x24/apps/ppsspp.png index 77ced28ec8..1bcd19f387 100644 Binary files a/icons/hicolor/24x24/apps/ppsspp.png and b/icons/hicolor/24x24/apps/ppsspp.png differ diff --git a/icons/hicolor/256x256/apps/ppsspp.png b/icons/hicolor/256x256/apps/ppsspp.png index 016626cdf5..fe8fe9e400 100644 Binary files a/icons/hicolor/256x256/apps/ppsspp.png and b/icons/hicolor/256x256/apps/ppsspp.png differ diff --git a/icons/hicolor/32x32/apps/ppsspp.png b/icons/hicolor/32x32/apps/ppsspp.png index 7160b2bd83..2fc81f3e4a 100644 Binary files a/icons/hicolor/32x32/apps/ppsspp.png and b/icons/hicolor/32x32/apps/ppsspp.png differ diff --git a/icons/hicolor/48x48/apps/ppsspp.png b/icons/hicolor/48x48/apps/ppsspp.png index 037c440b60..ecbb26b043 100644 Binary files a/icons/hicolor/48x48/apps/ppsspp.png and b/icons/hicolor/48x48/apps/ppsspp.png differ diff --git a/icons/hicolor/512x512/apps/ppsspp.png b/icons/hicolor/512x512/apps/ppsspp.png index 7a8299b073..95df4df81a 100644 Binary files a/icons/hicolor/512x512/apps/ppsspp.png and b/icons/hicolor/512x512/apps/ppsspp.png differ diff --git a/icons/hicolor/64x64/apps/ppsspp.png b/icons/hicolor/64x64/apps/ppsspp.png index af835fe9a6..72e12c1353 100644 Binary files a/icons/hicolor/64x64/apps/ppsspp.png and b/icons/hicolor/64x64/apps/ppsspp.png differ diff --git a/icons/hicolor/96x96/apps/ppsspp.png b/icons/hicolor/96x96/apps/ppsspp.png index bd595c03ad..7f6551e1b8 100644 Binary files a/icons/hicolor/96x96/apps/ppsspp.png and b/icons/hicolor/96x96/apps/ppsspp.png differ diff --git a/icons/icon-114.png b/icons/icon-114.png index bef28a2504..458fc369ec 100755 Binary files a/icons/icon-114.png and b/icons/icon-114.png differ diff --git a/ios/ViewController.mm b/ios/ViewController.mm index a0d89b5959..55a13e58d8 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -386,8 +386,8 @@ static LocationHelper *locationHelper; } int ToTouchID(UITouch *uiTouch, bool allowAllocate) { - // Find the id for the touch. Avoid 0 (mouse.) - for (int localId = 1; localId < (int)ARRAY_SIZE(g_touches); ++localId) { + // Find the id for the touch. + for (int localId = 0; localId < (int)ARRAY_SIZE(g_touches); ++localId) { if (g_touches[localId] == uiTouch) { return localId; } @@ -395,7 +395,7 @@ int ToTouchID(UITouch *uiTouch, bool allowAllocate) { // Allocate a new one, perhaps? if (allowAllocate) { - for (int localId = 1; localId < (int)ARRAY_SIZE(g_touches); ++localId) { + for (int localId = 0; localId < (int)ARRAY_SIZE(g_touches); ++localId) { if (g_touches[localId] == 0) { g_touches[localId] = uiTouch; return localId; diff --git a/ios/assets/Default-568h@2x.png b/ios/assets/Default-568h@2x.png index 0891b7aabf..a6c7a8b98f 100644 Binary files a/ios/assets/Default-568h@2x.png and b/ios/assets/Default-568h@2x.png differ diff --git a/ios/assets/Default-568h@3x.png b/ios/assets/Default-568h@3x.png index 5d4cbebfcd..9a2e7bf439 100644 Binary files a/ios/assets/Default-568h@3x.png and b/ios/assets/Default-568h@3x.png differ diff --git a/ios/assets/Icon-72.png b/ios/assets/Icon-72.png index 4582e5461b..7d7eca138c 100644 Binary files a/ios/assets/Icon-72.png and b/ios/assets/Icon-72.png differ diff --git a/ios/assets/Icon-72@2x.png b/ios/assets/Icon-72@2x.png index 5c4bee6263..c9d9095aec 100644 Binary files a/ios/assets/Icon-72@2x.png and b/ios/assets/Icon-72@2x.png differ diff --git a/ios/assets/Icon-72@3x.png b/ios/assets/Icon-72@3x.png index 17d9efba5d..efdde28b01 100644 Binary files a/ios/assets/Icon-72@3x.png and b/ios/assets/Icon-72@3x.png differ diff --git a/ios/assets/Icon.png b/ios/assets/Icon.png index 804df46912..44625336c5 100644 Binary files a/ios/assets/Icon.png and b/ios/assets/Icon.png differ diff --git a/ios/assets/Icon@2x.png b/ios/assets/Icon@2x.png index bdb13ff5ed..9bcb28cc3d 100644 Binary files a/ios/assets/Icon@2x.png and b/ios/assets/Icon@2x.png differ diff --git a/ios/assets/Icon@3x.png b/ios/assets/Icon@3x.png index 967c610b9a..227eccb081 100644 Binary files a/ios/assets/Icon@3x.png and b/ios/assets/Icon@3x.png differ diff --git a/libretro/Makefile b/libretro/Makefile index 6cd3173449..5645e8c408 100644 --- a/libretro/Makefile +++ b/libretro/Makefile @@ -6,6 +6,8 @@ INCFLAGS := -I. COREFLAGS := CPUFLAGS := +PLATOFRM_EXT := + UNAME=$(shell uname -a) # Cross compile ? @@ -70,7 +72,7 @@ ifneq (,$(findstring unix,$(platform))) else GL_LIB := -lGL endif - PLATFORM_EXT := unix + PLATFORM_EXT = unix LDFLAGS += -lrt -ldl # Raspberry Pi @@ -82,7 +84,7 @@ else ifneq (,$(findstring rpi,$(platform))) GL_LIB := -lGLESv2 INCFLAGS += -I/opt/vc/include CPUFLAGS += -DARMv5_ONLY - PLATFORM_EXT := unix + PLATFORM_EXT = unix TARGET_ARCH = arm LDFLAGS += -lrt -ldl @@ -106,7 +108,7 @@ else ifneq (,$(findstring arm64,$(platform))) endif CPUFLAGS += -D__arm64__ -DARM64_ASM -D__NEON_OPT LDFLAGS += -lrt -ldl - PLATFORM_EXT := unix + PLATFORM_EXT = unix # i.MX6 else ifneq (,$(findstring imx6,$(platform))) @@ -116,7 +118,7 @@ else ifneq (,$(findstring imx6,$(platform))) GLES = 1 GL_LIB := -lGLESv2 CPUFLAGS += - PLATFORM_EXT := unix + PLATFORM_EXT = unix TARGET_ARCH = arm HAVE_NEON=1 FFMPEGINCFLAGS += -I$(FFMPEGDIR)/linux/$(TARGET_ARCH)/include @@ -127,21 +129,32 @@ else ifneq (,$(findstring imx6,$(platform))) # OS X else ifneq (,$(findstring osx,$(platform))) TARGET := $(TARGET_NAME)_libretro.dylib + MINVERSION := LDFLAGS += -dynamiclib OSXVER = `sw_vers -productVersion | cut -d. -f 2` OSX_LT_MAVERICKS = `(( $(OSXVER) <= 9 )) && echo "YES"` ifeq ($(OSX_LT_MAVERICKS),"YES") - LDFLAGS += -mmacosx-version-min=10.5 + MINVERSION += -mmacosx-version-min=10.5 endif - LDFLAGS += -stdlib=libc++ + LDFLAGS += $(MINVERSION) + LDFLAGS += -stdlib=libc++ fpic = -fPIC - FFMPEGINCFLAGS += -I$(FFMPEGDIR)/macosx/$(TARGET_ARCH)/include - FFMPEGLIBDIR := $(FFMPEGDIR)/macosx/$(TARGET_ARCH)/lib + FFMPEGINCFLAGS += -I$(FFMPEGDIR)/macosx/universal/include + FFMPEGLIBDIR := $(FFMPEGDIR)/macosx/universal/lib FFMPEGLDFLAGS += -liconv -L$(FFMPEGLIBDIR) -lavformat -lavcodec -lavutil -lswresample -lswscale PLATCFLAGS += -D__MACOSX__ GL_LIB := -framework OpenGL - PLATFORM_EXT := darwin + PLATFORM_EXT = darwin + + ifeq ($(CROSS_COMPILE),1) + TARGET_RULE = -target $(LIBRETRO_APPLE_PLATFORM) -isysroot $(LIBRETRO_APPLE_ISYSROOT) + PLATCFLAGS += $(TARGET_RULE) + LDFLAGS += $(TARGET_RULE) + endif + + PLATCFLAGS += $(ARCHFLAGS) + LDFLAGS += $(ARCHFLAGS) # iOS else ifneq (,$(findstring ios,$(platform))) @@ -150,6 +163,7 @@ else ifneq (,$(findstring ios,$(platform))) fpic = -fPIC GLES = 1 GL_LIB := -framework OpenGLES + MINVERSION := HAVE_NEON = 1 FFMPEGINCFLAGS += -I$(FFMPEGDIR)/ios/universal/include @@ -160,18 +174,49 @@ else ifneq (,$(findstring ios,$(platform))) IOSSDK := $(shell xcodebuild -version -sdk iphoneos Path) endif +ifeq ($(platform),ios-arm64) + CC = clang -arch arm64 -isysroot $(IOSSDK) + CXX = clang++ -arch arm64 -isysroot $(IOSSDK) +else CC = clang -arch armv7 -isysroot $(IOSSDK) CXX = clang++ -arch armv7 -isysroot $(IOSSDK) +endif OSXVER = `sw_vers -productVersion | cut -c 4` - ifneq ($(OSXVER),9) - CC += -miphoneos-version-min=5.0 - AS += -miphoneos-version-min=5.0 - CXX += -miphoneos-version-min=5.0 - PLATCFLAGS += -miphoneos-version-min=5.0 - endif + ifneq ($(OSXVER),9) + MINVERSION = -miphoneos-version-min=5.0 + else + MINVERSION = -miphoneos-version-min=8.0 + endif + PLATCFLAGS += $(MINVERSION) PLATCFLAGS += -DHAVE_POSIX_MEMALIGN CPUFLAGS += -DARMv5_ONLY -DARM - PLATFORM_EXT := unix + PLATFORM_EXT = darwin + TARGET_ARCH = arm + +else ifeq ($(platform), tvos-arm64) + TARGET := $(TARGET_NAME)_libretro_tvos.dylib + LDFLAGS += -dynamiclib -marm + fpic = -fPIC + GLES = 1 + GL_LIB := -framework OpenGLES + MINVERSION := + HAVE_NEON = 1 + + FFMPEGINCFLAGS += -I$(FFMPEGDIR)/ios/universal/include + FFMPEGLIBDIR := $(FFMPEGDIR)/ios/universal/lib + FFMPEGLDFLAGS += -L$(FFMPEGLIBDIR) -lavformat -lavcodec -lavutil -lswresample -lswscale + + ifeq ($(IOSSDK),) + IOSSDK := $(shell xcodebuild -version -sdk appletvos Path) + endif + + CC = clang -arch arm64 -isysroot $(IOSSDK) + CXX = clang++ -arch arm64 -isysroot $(IOSSDK) + OSXVER = `sw_vers -productVersion | cut -c 4` + PLATCFLAGS += $(MINVERSION) + PLATCFLAGS += -DHAVE_POSIX_MEMALIGN + CPUFLAGS += -DARMv5_ONLY -DARM + PLATFORM_EXT = darwin TARGET_ARCH = arm # Android @@ -196,7 +241,7 @@ else ifneq (,$(findstring android,$(platform))) FFMPEGLIBDIR := $(FFMPEGDIR)/android/armv7/lib FFMPEGLDFLAGS += -L$(FFMPEGLIBDIR) -lavformat -lavcodec -lavutil -lswresample -lswscale - PLATFORM_EXT := android + PLATFORM_EXT = android # QNX else ifeq ($(platform), qnx) @@ -216,7 +261,7 @@ else ifeq ($(platform), qnx) CPUFLAGS += -marm -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=softfp -D__arm__ -DARM_ASM -D__NEON_OPT CFLAGS += -D__QNX__ - PLATFORM_EXT := unix + PLATFORM_EXT = unix # ARM else ifneq (,$(findstring armv,$(platform))) @@ -264,7 +309,7 @@ else ifeq ($(platform), emscripten) -Daudio_convert_s16_to_float_C=mupen_audio_convert_s16_to_float_C -Daudio_convert_float_to_s16_C=mupen_audio_convert_float_to_s16_C \ -Daudio_convert_init_simd=mupen_audio_convert_init_simd -Drglgen_resolve_symbols_custom=mupen_rglgen_resolve_symbols_custom \ -Drglgen_resolve_symbols=mupen_rglgen_resolve_symbols - PLATFORM_EXT := unix + PLATFORM_EXT = unix # Windows MSVC 2017 all architectures else ifneq (,$(findstring windows_msvc2017,$(platform))) @@ -370,7 +415,7 @@ else ifneq (,$(findstring windows_msvc2017,$(platform))) TARGET := $(TARGET_NAME)_libretro.dll PSS_STYLE :=2 LDFLAGS += -DLL - PLATFORM_EXT := win32 + PLATFORM_EXT = win32 FFMPEGINCFLAGS += -I$(FFMPEGDIR)/Windows/$(TARGET_ARCH)/include FFMPEGLIBDIR := $(FFMPEGDIR)/Windows/$(TARGET_ARCH)/lib FFMPEGLDFLAGS += -LIBPATH:$(FFMPEGLIBDIR) @@ -481,7 +526,7 @@ else ifneq (,$(findstring windows_msvc2019,$(platform))) TARGET := $(TARGET_NAME)_libretro.dll PSS_STYLE :=2 LDFLAGS += -DLL - PLATFORM_EXT := win32 + PLATFORM_EXT = win32 FFMPEGINCFLAGS += -I$(FFMPEGDIR)/Windows/$(TARGET_ARCH)/include FFMPEGLIBDIR := $(FFMPEGDIR)/Windows/$(TARGET_ARCH)/lib FFMPEGLDFLAGS += -LIBPATH:$(FFMPEGLIBDIR) @@ -495,7 +540,7 @@ else ifneq (,$(findstring win,$(platform))) CXXFLAGS += -fpermissive -Wno-multichar -D_UNICODE -DUNICODE LDFLAGS += -shared -Wl,--no-undefined -static-libgcc -static-libstdc++ -Wl,--version-script=link.T -lwinmm -lgdi32 -lwsock32 -lws2_32 -ld3d9 -ld3dx9 GL_LIB := -lopengl32 - PLATFORM_EXT := win32 + PLATFORM_EXT = win32 FFMPEGINCFLAGS += -I$(FFMPEGDIR)/Windows/$(TARGET_ARCH)/include FFMPEGLDFLAGS += -lavformat -lavcodec -lavutil -lswresample -lswscale INCFLAGS += -include $(CORE_DIR)/Windows/mingw_defines.h diff --git a/libretro/Makefile.common b/libretro/Makefile.common index bb185a17a1..704993a645 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -6,6 +6,7 @@ GPUCOMMONDIR = $(CORE_DIR)/GPU/Common GPUDIR = $(CORE_DIR)/GPU EXTDIR = $(CORE_DIR)/ext NATIVEDIR = $(EXTDIR)/native +ZSTDDIR = $(EXTDIR)/zstd/lib SOURCES_CXX := SOURCES_C := @@ -18,11 +19,13 @@ INCFLAGS += \ -I$(EXTDIR)/glslang \ -I$(EXTDIR)/native \ -I$(EXTDIR)/zlib \ + -I$(ZSTDDIR) \ -I$(EXTDIR)/snappy \ -I$(FFMPEGDIR) \ $(FFMPEGINCFLAGS) \ -I$(EXTDIR)/cityhash \ -I$(EXTDIR)/armips \ + -I$(EXTDIR)/zstd/lib \ -I$(EXTDIR) \ -I$(NATIVEDIR) \ -I$(EXTDIR)/libpng17 \ @@ -34,60 +37,130 @@ INCFLAGS += \ ifeq ($(PLATFORM_EXT), android) INCFLAGS += -I$(EXTDIR)/libzip SOURCES_C += \ - $(EXTDIR)/libzip/mkstemp.c \ $(EXTDIR)/libzip/zip_add.c \ $(EXTDIR)/libzip/zip_add_dir.c \ + $(EXTDIR)/libzip/zip_add_entry.c \ + $(EXTDIR)/libzip/zip_algorithm_deflate.c \ + $(EXTDIR)/libzip/zip_buffer.c \ $(EXTDIR)/libzip/zip_close.c \ $(EXTDIR)/libzip/zip_delete.c \ + $(EXTDIR)/libzip/zip_dir_add.c \ $(EXTDIR)/libzip/zip_dirent.c \ - $(EXTDIR)/libzip/zip_entry_free.c \ - $(EXTDIR)/libzip/zip_entry_new.c \ - $(EXTDIR)/libzip/zip_err_str.c \ + $(EXTDIR)/libzip/zip_discard.c \ + $(EXTDIR)/libzip/zip_entry.c \ $(EXTDIR)/libzip/zip_error.c \ $(EXTDIR)/libzip/zip_error_clear.c \ $(EXTDIR)/libzip/zip_error_get.c \ $(EXTDIR)/libzip/zip_error_get_sys_type.c \ $(EXTDIR)/libzip/zip_error_strerror.c \ $(EXTDIR)/libzip/zip_error_to_str.c \ + $(EXTDIR)/libzip/zip_extra_field.c \ + $(EXTDIR)/libzip/zip_extra_field_api.c \ $(EXTDIR)/libzip/zip_fclose.c \ + $(EXTDIR)/libzip/zip_fdopen.c \ + $(EXTDIR)/libzip/zip_file_add.c \ $(EXTDIR)/libzip/zip_file_error_clear.c \ $(EXTDIR)/libzip/zip_file_error_get.c \ + $(EXTDIR)/libzip/zip_file_get_comment.c \ + $(EXTDIR)/libzip/zip_file_get_external_attributes.c \ $(EXTDIR)/libzip/zip_file_get_offset.c \ + $(EXTDIR)/libzip/zip_file_rename.c \ + $(EXTDIR)/libzip/zip_file_replace.c \ + $(EXTDIR)/libzip/zip_file_set_comment.c \ + $(EXTDIR)/libzip/zip_file_set_encryption.c \ + $(EXTDIR)/libzip/zip_file_set_external_attributes.c \ + $(EXTDIR)/libzip/zip_file_set_mtime.c \ $(EXTDIR)/libzip/zip_file_strerror.c \ - $(EXTDIR)/libzip/zip_filerange_crc.c \ $(EXTDIR)/libzip/zip_fopen.c \ + $(EXTDIR)/libzip/zip_fopen_encrypted.c \ $(EXTDIR)/libzip/zip_fopen_index.c \ + $(EXTDIR)/libzip/zip_fopen_index_encrypted.c \ $(EXTDIR)/libzip/zip_fread.c \ - $(EXTDIR)/libzip/zip_free.c \ + $(EXTDIR)/libzip/zip_fseek.c \ + $(EXTDIR)/libzip/zip_ftell.c \ $(EXTDIR)/libzip/zip_get_archive_comment.c \ $(EXTDIR)/libzip/zip_get_archive_flag.c \ + $(EXTDIR)/libzip/zip_get_encryption_implementation.c \ $(EXTDIR)/libzip/zip_get_file_comment.c \ $(EXTDIR)/libzip/zip_get_name.c \ + $(EXTDIR)/libzip/zip_get_num_entries.c \ $(EXTDIR)/libzip/zip_get_num_files.c \ + $(EXTDIR)/libzip/zip_hash.c \ + $(EXTDIR)/libzip/zip_io_util.c \ + $(EXTDIR)/libzip/zip_libzip_version.c \ $(EXTDIR)/libzip/zip_memdup.c \ $(EXTDIR)/libzip/zip_name_locate.c \ $(EXTDIR)/libzip/zip_new.c \ $(EXTDIR)/libzip/zip_open.c \ + $(EXTDIR)/libzip/zip_pkware.c \ + $(EXTDIR)/libzip/zip_progress.c \ $(EXTDIR)/libzip/zip_rename.c \ $(EXTDIR)/libzip/zip_replace.c \ $(EXTDIR)/libzip/zip_set_archive_comment.c \ $(EXTDIR)/libzip/zip_set_archive_flag.c \ + $(EXTDIR)/libzip/zip_set_default_password.c \ $(EXTDIR)/libzip/zip_set_file_comment.c \ + $(EXTDIR)/libzip/zip_set_file_compression.c \ $(EXTDIR)/libzip/zip_set_name.c \ + $(EXTDIR)/libzip/zip_source_accept_empty.c \ + $(EXTDIR)/libzip/zip_source_begin_write.c \ + $(EXTDIR)/libzip/zip_source_begin_write_cloning.c \ $(EXTDIR)/libzip/zip_source_buffer.c \ - $(EXTDIR)/libzip/zip_source_file.c \ - $(EXTDIR)/libzip/zip_source_filep.c \ + $(EXTDIR)/libzip/zip_source_call.c \ + $(EXTDIR)/libzip/zip_source_close.c \ + $(EXTDIR)/libzip/zip_source_commit_write.c \ + $(EXTDIR)/libzip/zip_source_compress.c \ + $(EXTDIR)/libzip/zip_source_crc.c \ + $(EXTDIR)/libzip/zip_source_error.c \ + $(EXTDIR)/libzip/zip_source_file_common.c \ + $(EXTDIR)/libzip/zip_source_file_stdio.c \ $(EXTDIR)/libzip/zip_source_free.c \ $(EXTDIR)/libzip/zip_source_function.c \ + $(EXTDIR)/libzip/zip_source_get_file_attributes.c \ + $(EXTDIR)/libzip/zip_source_is_deleted.c \ + $(EXTDIR)/libzip/zip_source_layered.c \ + $(EXTDIR)/libzip/zip_source_open.c \ + $(EXTDIR)/libzip/zip_source_pkware_decode.c \ + $(EXTDIR)/libzip/zip_source_pkware_encode.c \ + $(EXTDIR)/libzip/zip_source_read.c \ + $(EXTDIR)/libzip/zip_source_remove.c \ + $(EXTDIR)/libzip/zip_source_rollback_write.c \ + $(EXTDIR)/libzip/zip_source_seek.c \ + $(EXTDIR)/libzip/zip_source_seek_write.c \ + $(EXTDIR)/libzip/zip_source_stat.c \ + $(EXTDIR)/libzip/zip_source_supports.c \ + $(EXTDIR)/libzip/zip_source_tell.c \ + $(EXTDIR)/libzip/zip_source_tell_write.c \ + $(EXTDIR)/libzip/zip_source_window.c \ + $(EXTDIR)/libzip/zip_source_write.c \ $(EXTDIR)/libzip/zip_source_zip.c \ + $(EXTDIR)/libzip/zip_source_zip_new.c \ $(EXTDIR)/libzip/zip_stat.c \ $(EXTDIR)/libzip/zip_stat_index.c \ $(EXTDIR)/libzip/zip_stat_init.c \ $(EXTDIR)/libzip/zip_strerror.c \ + $(EXTDIR)/libzip/zip_string.c \ $(EXTDIR)/libzip/zip_unchange.c \ $(EXTDIR)/libzip/zip_unchange_all.c \ $(EXTDIR)/libzip/zip_unchange_archive.c \ - $(EXTDIR)/libzip/zip_unchange_data.c + $(EXTDIR)/libzip/zip_unchange_data.c \ + $(EXTDIR)/libzip/zip_utf-8.c \ + $(EXTDIR)/libzip/zip_err_str.c + + ifeq ($(PLATFORM_EXT), win32) + SOURCES_C += \ + $(EXTDIR)/libzip/zip_source_file_win32.c \ + $(EXTDIR)/libzip/zip_source_file_win32_named.c \ + $(EXTDIR)/libzip/zip_source_file_win32_utf16.c \ + $(EXTDIR)/libzip/zip_source_file_win32_utf8.c \ + $(EXTDIR)/libzip/zip_source_file_win32_ansi.c \ + $(EXTDIR)/libzip/zip_random_win32.c + else + SOURCES_C += \ + $(EXTDIR)/libzip/zip_mkstempm.c \ + $(EXTDIR)/libzip/zip_source_file_stdio_named.c \ + $(EXTDIR)/libzip/zip_random_unix.c + endif SOURCES_C +=\ $(COMMONDIR)/Math/fast/fast_math.c \ @@ -161,6 +234,7 @@ SOURCES_CXX += \ $(COMMONDIR)/File/VFS/VFS.cpp \ $(COMMONDIR)/File/VFS/AssetReader.cpp \ $(COMMONDIR)/File/DiskFree.cpp \ + $(COMMONDIR)/File/Path.cpp \ $(COMMONDIR)/File/PathBrowser.cpp \ $(COMMONDIR)/File/FileUtil.cpp \ $(COMMONDIR)/File/FileDescriptor.cpp \ @@ -194,6 +268,7 @@ SOURCES_CXX += \ $(COMMONDIR)/Net/HTTPClient.cpp \ $(COMMONDIR)/Net/HTTPHeaders.cpp \ $(COMMONDIR)/Net/HTTPServer.cpp \ + $(COMMONDIR)/Net/NetBuffer.cpp \ $(COMMONDIR)/Net/Resolve.cpp \ $(COMMONDIR)/Net/Sinks.cpp \ $(COMMONDIR)/Net/URL.cpp \ @@ -201,7 +276,6 @@ SOURCES_CXX += \ $(COMMONDIR)/Render/DrawBuffer.cpp \ $(COMMONDIR)/Render/TextureAtlas.cpp \ $(COMMONDIR)/Serialize/Serializer.cpp \ - $(COMMONDIR)/Thread/Executor.cpp \ $(COMMONDIR)/Thread/ThreadUtil.cpp \ $(COMMONDIR)/Thread/ThreadPool.cpp \ $(COMMONDIR)/Thread/PrioritizedWorkQueue.cpp \ @@ -214,13 +288,17 @@ SOURCES_CXX += \ $(COMMONDIR)/UI/View.cpp \ $(COMMONDIR)/UI/ViewGroup.cpp \ $(COMMONDIR)/System/Display.cpp \ + $(COMMONDIR)/ArmCPUDetect.cpp \ + $(COMMONDIR)/CPUDetect.cpp \ $(COMMONDIR)/Buffer.cpp \ $(COMMONDIR)/ConsoleListener.cpp \ $(COMMONDIR)/ExceptionHandlerSetup.cpp \ + $(COMMONDIR)/FakeCPUDetect.cpp \ $(COMMONDIR)/Log.cpp \ $(COMMONDIR)/LogManager.cpp \ $(COMMONDIR)/OSVersion.cpp \ $(COMMONDIR)/MemoryUtil.cpp \ + $(COMMONDIR)/MipsCPUDetect.cpp \ $(COMMONDIR)/SysError.cpp \ $(COMMONDIR)/StringUtils.cpp \ $(COMMONDIR)/TimeUtil.cpp @@ -245,7 +323,7 @@ SOURCES_CXX += \ $(GPUCOMMONDIR)/IndexGenerator.cpp \ $(GPUCOMMONDIR)/TextureDecoder.cpp \ $(GPUCOMMONDIR)/PostShader.cpp \ - $(COMMONDIR)/ColorConv.cpp \ + $(COMMONDIR)/Data/Convert/ColorConv.cpp \ $(GPUDIR)/Debugger/Breakpoints.cpp \ $(GPUDIR)/Debugger/Debugger.cpp \ $(GPUDIR)/Debugger/Playback.cpp \ @@ -336,6 +414,7 @@ SOURCES_CXX += $(COMMONDIR)/MemArenaAndroid.cpp \ $(EXTDIR)/glslang/glslang/OSDependent/Unix/ossource.cpp \ $(COMMONDIR)/Render/Text/draw_text_android.cpp else +COREFLAGS += -DVK_USE_PLATFORM_XLIB_KHR SOURCES_CXX += $(COMMONDIR)/MemArenaPosix.cpp \ $(EXTDIR)/glslang/glslang/OSDependent/Unix/ossource.cpp endif @@ -518,7 +597,6 @@ ifeq ($(WITH_DYNAREC),1) ifeq ($(TARGET_ARCH),arm) DYNAFLAGS += -D_ARCH_32 SOURCES_CXX += $(COMMONDIR)/ArmEmitter.cpp \ - $(COMMONDIR)/ArmCPUDetect.cpp \ $(COREDIR)/MIPS/ARM/ArmAsm.cpp \ $(COREDIR)/MIPS/ARM/ArmCompALU.cpp \ $(COREDIR)/MIPS/ARM/ArmCompBranch.cpp \ @@ -537,7 +615,7 @@ ifeq ($(WITH_DYNAREC),1) $(COREDIR)/MIPS/ARM/ArmCompVFPUNEON.cpp \ $(COREDIR)/MIPS/ARM/ArmCompVFPUNEONUtil.cpp \ $(COREDIR)/Util/AudioFormatNEON.cpp \ - $(COMMONDIR)/ColorConvNEON.cpp \ + $(COMMONDIR)/Data/Convert/ColorConvNEON.cpp \ $(GPUDIR)/Common/TextureDecoderNEON.cpp SOURCES_C += $(EXTDIR)/libpng17/arm/arm_init.c \ @@ -548,7 +626,6 @@ ifeq ($(WITH_DYNAREC),1) else ifeq ($(TARGET_ARCH),arm64) DYNAFLAGS += -D_ARCH_64 SOURCES_CXX += $(COMMONDIR)/Arm64Emitter.cpp \ - $(COMMONDIR)/ArmCPUDetect.cpp \ $(COREDIR)/MIPS/ARM64/Arm64Asm.cpp \ $(COREDIR)/MIPS/ARM64/Arm64CompALU.cpp \ $(COREDIR)/MIPS/ARM64/Arm64CompBranch.cpp \ @@ -567,7 +644,7 @@ ifeq ($(WITH_DYNAREC),1) $(COREDIR)/MIPS/ARM/ArmCompVFPUNEON.cpp \ $(COREDIR)/MIPS/ARM/ArmCompVFPUNEONUtil.cpp \ $(COREDIR)/Util/AudioFormatNEON.cpp \ - $(COMMONDIR)/ColorConvNEON.cpp \ + $(COMMONDIR)/Data/Convert/ColorConvNEON.cpp \ $(GPUDIR)/Common/TextureDecoderNEON.cpp SOURCES_C += $(EXTDIR)/libpng17/arm/arm_init.c \ @@ -583,7 +660,7 @@ ifeq ($(WITH_DYNAREC),1) CPUFLAGS += -D_M_IX86_FP else CPUFLAGS += -msse -msse2 -endif + endif ifeq ($(TARGET_ARCH),x86_64) CPUFLAGS += -D_M_X64 -D_ARCH_64 else @@ -597,7 +674,6 @@ endif $(COMMONDIR)/x64Analyzer.cpp \ $(COMMONDIR)/ABI.cpp \ $(COMMONDIR)/Thunk.cpp \ - $(COMMONDIR)/CPUDetect.cpp \ $(COREDIR)/MIPS/x86/CompReplace.cpp \ $(COREDIR)/MIPS/x86/CompBranch.cpp \ $(COREDIR)/MIPS/x86/Asm.cpp \ @@ -624,6 +700,7 @@ SOURCES_C += $(EXTDIR)/udis86/decode.c \ INCFLAGS += -I$(EXTDIR)/miniupnp-build SOURCES_C += \ + $(EXTDIR)/miniupnp/miniupnpc/addr_is_reserved.c \ $(EXTDIR)/miniupnp/miniupnpc/connecthostport.c \ $(EXTDIR)/miniupnp/miniupnpc/igd_desc_parse.c \ $(EXTDIR)/miniupnp/miniupnpc/minisoap.c \ @@ -654,11 +731,6 @@ SOURCES_CXX += \ $(LIBRETRODIR)/LibretroVulkanContext.cpp \ $(LIBRETRODIR)/libretro_vulkan.cpp -ifneq ($(PLATFORM_EXT), darwin) - ifeq ($(PLATFORM_EXT), unix) - COREFLAGS += -DVK_USE_PLATFORM_XLIB_KHR - endif -endif ifeq ($(PLATFORM_EXT), win32) SOURCES_CXX += \ @@ -703,6 +775,39 @@ SOURCES_CXX += \ $(LIBRETRODIR)/LibretroGLContext.cpp \ $(LIBRETRODIR)/LibretroGLCoreContext.cpp +SOURCES_C += $(ZSTDDIR)/common/debug.c \ + $(ZSTDDIR)/common/entropy_common.c \ + $(ZSTDDIR)/common/error_private.c \ + $(ZSTDDIR)/common/fse_decompress.c \ + $(ZSTDDIR)/common/pool.c \ + $(ZSTDDIR)/common/threading.c + +#SOURCES_C += $(ZSTDDIR)/common/xxhash.c + +SOURCES_C += \ + $(ZSTDDIR)/common/zstd_common.c \ + $(ZSTDDIR)/compress/fse_compress.c \ + $(ZSTDDIR)/compress/hist.c \ + $(ZSTDDIR)/compress/huf_compress.c \ + $(ZSTDDIR)/compress/zstd_compress.c \ + $(ZSTDDIR)/compress/zstd_compress_literals.c \ + $(ZSTDDIR)/compress/zstd_compress_sequences.c \ + $(ZSTDDIR)/compress/zstd_compress_superblock.c \ + $(ZSTDDIR)/compress/zstd_double_fast.c \ + $(ZSTDDIR)/compress/zstd_fast.c \ + $(ZSTDDIR)/compress/zstd_lazy.c \ + $(ZSTDDIR)/compress/zstd_ldm.c \ + $(ZSTDDIR)/compress/zstd_opt.c \ + $(ZSTDDIR)/compress/zstdmt_compress.c \ + $(ZSTDDIR)/decompress/huf_decompress.c \ + $(ZSTDDIR)/decompress/zstd_ddict.c \ + $(ZSTDDIR)/decompress/zstd_decompress.c \ + $(ZSTDDIR)/decompress/zstd_decompress_block.c \ + $(ZSTDDIR)/dictBuilder/cover.c \ + $(ZSTDDIR)/dictBuilder/divsufsort.c \ + $(ZSTDDIR)/dictBuilder/fastcover.c \ + $(ZSTDDIR)/dictBuilder/zdict.c + ifneq ($(STATIC_LINKING), 1) SOURCES_C += \ $(EXTDIR)/zlib/adler32.c \ diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index 9751924cb9..8eb098fea0 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -201,50 +201,66 @@ static RetroOption ppsspp_language("ppsspp_language", "Language", { { "auto static RetroOption ppsspp_rendering_mode("ppsspp_rendering_mode", "Rendering Mode", { { "buffered", FB_BUFFERED_MODE }, { "nonbuffered", FB_NON_BUFFERED_MODE } }); static RetroOption ppsspp_auto_frameskip("ppsspp_auto_frameskip", "Auto Frameskip", false); static RetroOption ppsspp_frameskip("ppsspp_frameskip", "Frameskip", 0, 10); -static RetroOption ppsspp_frameskiptype("ppsspp_frameskiptype", "Frameskip Type", 0, 10); +static RetroOption ppsspp_frameskiptype("ppsspp_frameskiptype", "Frameskip Type", { {"number of frames", 0}, {"percent of fps", 1} }); static RetroOption ppsspp_internal_resolution("ppsspp_internal_resolution", "Internal Resolution (restart)", 1, { "480x272", "960x544", "1440x816", "1920x1088", "2400x1360", "2880x1632", "3360x1904", "3840x2176", "4320x2448", "4800x2720" }); static RetroOption ppsspp_button_preference("ppsspp_button_preference", "Confirmation Button", { { "cross", PSP_SYSTEMPARAM_BUTTON_CROSS }, { "circle", PSP_SYSTEMPARAM_BUTTON_CIRCLE } }); static RetroOption ppsspp_fast_memory("ppsspp_fast_memory", "Fast Memory (Speedhack)", true); static RetroOption ppsspp_block_transfer_gpu("ppsspp_block_transfer_gpu", "Block Transfer GPU", true); -static RetroOption ppsspp_texture_scaling_level("ppsspp_texture_scaling_level", "Texture Scaling Level", { { "1", 1 }, { "2", 2 }, { "3", 3 }, { "4", 4 }, { "5", 5 }, { "0", 0 } }); +static RetroOption ppsspp_texture_scaling_level("ppsspp_texture_scaling_level", "Texture Scaling Level", { { "1", 1 }, { "2", 2 }, { "3", 3 }, { "4", 4 }, { "5", 5 }, { "auto", 0 } }); static RetroOption ppsspp_texture_scaling_type("ppsspp_texture_scaling_type", "Texture Scaling Type", { { "xbrz", TextureScalerCommon::XBRZ }, { "hybrid", TextureScalerCommon::HYBRID }, { "bicubic", TextureScalerCommon::BICUBIC }, { "hybrid_bicubic", TextureScalerCommon::HYBRID_BICUBIC } }); static RetroOption ppsspp_texture_filtering("ppsspp_texture_filtering", "Texture Filtering", { { "auto", 1 }, { "nearest", 2 }, { "linear", 3 }, { "linear(FMV)", 4 } }); static RetroOption ppsspp_texture_anisotropic_filtering("ppsspp_texture_anisotropic_filtering", "Anisotropic Filtering", { "off", "1x", "2x", "4x", "8x", "16x" }); -static RetroOption ppsspp_lower_resolution_for_effects("ppsspp_lower_resolution_for_effects", "Lower resolution for effects", { "off", "safe", "balanced", "aggressive" }); +static RetroOption ppsspp_lower_resolution_for_effects("ppsspp_lower_resolution_for_effects", "Lower resolution for effects", { {"off", 0}, {"safe", 1}, {"balanced", 2}, {"aggressive", 3} }); static RetroOption ppsspp_texture_deposterize("ppsspp_texture_deposterize", "Texture Deposterize", false); static RetroOption ppsspp_texture_replacement("ppsspp_texture_replacement", "Texture Replacement", false); static RetroOption ppsspp_gpu_hardware_transform("ppsspp_gpu_hardware_transform", "GPU Hardware T&L", true); -static RetroOption ppsspp_vertex_cache("ppsspp_vertex_cache", "Vertex Cache (Speedhack)", true); +static RetroOption ppsspp_vertex_cache("ppsspp_vertex_cache", "Vertex Cache (Speedhack)", false); static RetroOption ppsspp_cheats("ppsspp_cheats", "Internal Cheats Support", false); +static RetroOption ppsspp_io_threading("ppsspp_io_threading", "I/O on thread (experimental)", true); static RetroOption ppsspp_io_timing_method("ppsspp_io_timing_method", "IO Timing Method", { { "Fast", IOTimingMethods::IOTIMING_FAST }, { "Host", IOTimingMethods::IOTIMING_HOST }, { "Simulate UMD delays", IOTimingMethods::IOTIMING_REALISTIC } }); static RetroOption ppsspp_frame_duplication("ppsspp_frame_duplication", "Duplicate frames in 30hz games", false); +static RetroOption ppsspp_software_skinning("ppsspp_software_skinning", "Software Skinning", true); +static RetroOption ppsspp_ignore_bad_memory_access("ppsspp_ignore_bad_memory_access", "Ignore bad memory accesses", true); +static RetroOption ppsspp_lazy_texture_caching("ppsspp_lazy_texture_caching", "Lazy texture caching (speedup)", false); +static RetroOption ppsspp_retain_changed_textures("ppsspp_retain_changed_textures", "Retain changed textures (speedup, mem hog)", false); +static RetroOption ppsspp_force_lag_sync("ppsspp_force_lag_sync", "Force real clock sync (slower, less lag)", false); +static RetroOption ppsspp_spline_quality("ppsspp_spline_quality", "Spline/Bezier curves quality", { {"low", 0}, {"medium", 1}, {"high", 2} }); +static RetroOption ppsspp_disable_slow_framebuffer_effects("ppsspp_disable_slow_framebuffer_effects", "Disable slower effects (speedup)", false); void retro_set_environment(retro_environment_t cb) { std::vector vars; + vars.push_back(ppsspp_internal_resolution.GetOptions()); vars.push_back(ppsspp_cpu_core.GetOptions()); vars.push_back(ppsspp_locked_cpu_speed.GetOptions()); vars.push_back(ppsspp_language.GetOptions()); + vars.push_back(ppsspp_button_preference.GetOptions()); vars.push_back(ppsspp_rendering_mode.GetOptions()); + vars.push_back(ppsspp_gpu_hardware_transform.GetOptions()); + vars.push_back(ppsspp_texture_anisotropic_filtering.GetOptions()); + vars.push_back(ppsspp_spline_quality.GetOptions()); vars.push_back(ppsspp_auto_frameskip.GetOptions()); vars.push_back(ppsspp_frameskip.GetOptions()); vars.push_back(ppsspp_frameskiptype.GetOptions()); - vars.push_back(ppsspp_internal_resolution.GetOptions()); - vars.push_back(ppsspp_button_preference.GetOptions()); + vars.push_back(ppsspp_frame_duplication.GetOptions()); + vars.push_back(ppsspp_vertex_cache.GetOptions()); vars.push_back(ppsspp_fast_memory.GetOptions()); vars.push_back(ppsspp_block_transfer_gpu.GetOptions()); + vars.push_back(ppsspp_software_skinning.GetOptions()); + vars.push_back(ppsspp_lazy_texture_caching.GetOptions()); + vars.push_back(ppsspp_retain_changed_textures.GetOptions()); + vars.push_back(ppsspp_force_lag_sync.GetOptions()); + vars.push_back(ppsspp_disable_slow_framebuffer_effects.GetOptions()); + vars.push_back(ppsspp_lower_resolution_for_effects.GetOptions()); vars.push_back(ppsspp_texture_scaling_level.GetOptions()); vars.push_back(ppsspp_texture_scaling_type.GetOptions()); vars.push_back(ppsspp_texture_filtering.GetOptions()); - vars.push_back(ppsspp_texture_anisotropic_filtering.GetOptions()); vars.push_back(ppsspp_texture_deposterize.GetOptions()); vars.push_back(ppsspp_texture_replacement.GetOptions()); - vars.push_back(ppsspp_gpu_hardware_transform.GetOptions()); - vars.push_back(ppsspp_vertex_cache.GetOptions()); - vars.push_back(ppsspp_cheats.GetOptions()); + vars.push_back(ppsspp_io_threading.GetOptions()); vars.push_back(ppsspp_io_timing_method.GetOptions()); - vars.push_back(ppsspp_lower_resolution_for_effects.GetOptions()); - vars.push_back(ppsspp_frame_duplication.GetOptions()); + vars.push_back(ppsspp_ignore_bad_memory_access.GetOptions()); + vars.push_back(ppsspp_cheats.GetOptions()); vars.push_back({}); environ_cb = cb; @@ -313,9 +329,17 @@ static void check_variables(CoreParameter &coreParam) ppsspp_locked_cpu_speed.Update(&g_Config.iLockedCPUSpeed); ppsspp_rendering_mode.Update(&g_Config.iRenderingMode); ppsspp_cpu_core.Update((CPUCore *)&g_Config.iCpuCore); + ppsspp_io_threading.Update(&g_Config.bSeparateIOThread); ppsspp_io_timing_method.Update((IOTimingMethods *)&g_Config.iIOTimingMethod); ppsspp_lower_resolution_for_effects.Update(&g_Config.iBloomHack); ppsspp_frame_duplication.Update(&g_Config.bRenderDuplicateFrames); + ppsspp_software_skinning.Update(&g_Config.bSoftwareSkinning); + ppsspp_ignore_bad_memory_access.Update(&g_Config.bIgnoreBadMemAccess); + ppsspp_lazy_texture_caching.Update(&g_Config.bTextureBackoffCache); + ppsspp_retain_changed_textures.Update(&g_Config.bTextureSecondaryCache); + ppsspp_force_lag_sync.Update(&g_Config.bForceLagSync); + ppsspp_spline_quality.Update(&g_Config.iSplineBezierQuality); + ppsspp_disable_slow_framebuffer_effects.Update(&g_Config.bDisableSlowFramebufEffects); ppsspp_language.Update(&g_Config.iLanguage); if (g_Config.iLanguage < 0) @@ -364,6 +388,12 @@ void retro_init(void) g_Config.iCwCheatRefreshRate = 60; g_Config.iMemStickSizeGB = 16; g_Config.bFuncReplacements = true; + g_Config.bEncryptSave = true; + g_Config.bHighQualityDepth = true; + g_Config.bLoadPlugins = true; + g_Config.bFragmentTestCache = true; + g_Config.bSavedataUpgrade= true; + g_Config.bSeparateSASThread = true; g_Config.iFirmwareVersion = PSP_DEFAULT_FIRMWARE; g_Config.iPSPModel = PSP_MODEL_SLIM; @@ -457,7 +487,7 @@ namespace Libretro static void EmuThreadFunc() { - setCurrentThreadName("Emu"); + SetCurrentThreadName("Emu"); for (;;) { @@ -529,8 +559,8 @@ namespace Libretro bool retro_load_game(const struct retro_game_info *game) { - static std::string retro_base_dir; - static std::string retro_save_dir; + static Path retro_base_dir; + static Path retro_save_dir; std::string error_string; enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888; const char *nickname = NULL; @@ -570,43 +600,30 @@ bool retro_load_game(const struct retro_game_info *game) if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir_ptr) && dir_ptr) { - size_t last; - retro_base_dir = dir_ptr; - // Make sure that we don't have any lingering slashes, etc, as they break Windows. - last = retro_base_dir.find_last_not_of(DIR_SEP_CHRS); - if (last != std::string::npos) - last++; - - retro_base_dir = retro_base_dir.substr(0, last) + DIR_SEP; + retro_base_dir = Path(dir_ptr); } if (environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &dir_ptr) && dir_ptr) { - retro_save_dir = dir_ptr; - // Make sure that we don't have any lingering slashes, etc, as they break Windows. - size_t last = retro_save_dir.find_last_not_of(DIR_SEP_CHRS); - if (last != std::string::npos) - last++; - - retro_save_dir = retro_save_dir.substr(0, last) + DIR_SEP; + retro_save_dir = Path(dir_ptr); } if (retro_base_dir.empty()) - retro_base_dir = File::GetDir(game->path); + retro_base_dir = Path(game->path).NavigateUp(); - retro_base_dir += "PPSSPP" DIR_SEP; + retro_base_dir /= "PPSSPP"; if (retro_save_dir.empty()) - retro_save_dir = File::GetDir(game->path); + retro_save_dir = Path(game->path).NavigateUp(); - g_Config.currentDirectory = retro_base_dir; + g_Config.currentDirectory = retro_base_dir.ToString(); g_Config.externalDirectory = retro_base_dir; g_Config.memStickDirectory = retro_save_dir; - g_Config.flash0Directory = retro_base_dir + "flash0" DIR_SEP; + g_Config.flash0Directory = retro_base_dir / "flash0"; g_Config.internalDataDirectory = retro_base_dir; - VFSRegister("", new DirectoryAssetReader(retro_base_dir.c_str())); + VFSRegister("", new DirectoryAssetReader(retro_base_dir)); coreState = CORE_POWERUP; ctx = LibretroGraphicsContext::CreateGraphicsContext(); @@ -619,8 +636,8 @@ bool retro_load_game(const struct retro_game_info *game) CoreParameter coreParam = {}; coreParam.enableSound = true; - coreParam.fileToStart = std::string(game->path); - coreParam.mountIso = ""; + coreParam.fileToStart = Path(std::string(game->path)); + coreParam.mountIso.clear(); coreParam.startBreak = false; coreParam.printfEmuLog = true; coreParam.headLess = true; diff --git a/unittest/JitHarness.cpp b/unittest/JitHarness.cpp index 7ff7f589ad..6743a5a62e 100644 --- a/unittest/JitHarness.cpp +++ b/unittest/JitHarness.cpp @@ -23,12 +23,14 @@ #include "Common/System/System.h" #include "Common/TimeUtil.h" #include "Core/ConfigValues.h" +#include "Core/Debugger/SymbolMap.h" #include "Core/MIPS/JitCommon/JitCommon.h" #include "Core/MIPS/JitCommon/JitBlockCache.h" #include "Core/MIPS/MIPSCodeUtils.h" #include "Core/MIPS/MIPSDebugInterface.h" #include "Core/MIPS/MIPSAsm.h" #include "Core/MIPS/MIPSTables.h" +#include "Core/MIPS/MIPSVFPUUtils.h" #include "Core/MemMap.h" #include "Core/Core.h" #include "Core/CoreTiming.h" @@ -82,6 +84,7 @@ static void SetupJitHarness() { // This is pretty much the bare minimum required to setup jit. coreState = CORE_POWERUP; currentMIPS = &mipsr4k; + g_symbolMap = new SymbolMap(); Memory::g_MemorySize = Memory::RAM_NORMAL_SIZE; PSP_CoreParameter().cpuCore = CPUCore::INTERPRETER; PSP_CoreParameter().unthrottle = true; @@ -89,6 +92,7 @@ static void SetupJitHarness() { Memory::Init(); mipsr4k.Reset(); CoreTiming::Init(); + InitVFPUSinCos(); } static void DestroyJitHarness() { @@ -99,6 +103,7 @@ static void DestroyJitHarness() { Memory::Shutdown(); coreState = CORE_POWERDOWN; currentMIPS = nullptr; + delete g_symbolMap; } bool TestJit() { diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index 2733287e55..47c48c1eda 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -28,12 +28,14 @@ // Search for "availableTests". #include "ppsspp_config.h" + #include #include #include #include #include #include + #if PPSSPP_PLATFORM(ANDROID) #include #endif @@ -41,9 +43,10 @@ #include "Common/System/NativeApp.h" #include "Common/System/System.h" #include "Common/Input/InputState.h" -#include "ext/disarm.h" +#include "Common/File/Path.h" #include "Common/Math/math_util.h" #include "Common/Data/Text/Parsers.h" +#include "Common/Data/Encoding/Utf8.h" #include "Common/ArmEmitter.h" #include "Common/BitScan.h" @@ -302,7 +305,7 @@ bool TestParsers() { bool TestVFPUSinCos() { float sine, cosine; - InitVFPUSinCos(false); + InitVFPUSinCos(); EXPECT_FALSE(vfpu_sincos == nullptr); vfpu_sincos(0.0f, sine, cosine); EXPECT_EQ_FLOAT(sine, 0.0f); @@ -566,9 +569,40 @@ static bool TestMemMap() { EXPECT_EQ_HEX(Memory::ValidSize(base, 0x40000001), range.size); EXPECT_EQ_HEX(Memory::ValidSize(base, 0x20000001), range.size); EXPECT_EQ_HEX(Memory::ValidSize(base, 0x10000001), range.size); + + EXPECT_EQ_HEX(Memory::ValidSize(base + range.size - 0x10, 0x20000001), 0x10); } } + EXPECT_FALSE(Memory::IsValidAddress(0x00015000)); + EXPECT_FALSE(Memory::IsValidAddress(0x04900000)); + EXPECT_EQ_HEX(Memory::ValidSize(0x00015000, 4), 0); + EXPECT_EQ_HEX(Memory::ValidSize(0x04900000, 4), 0); + + return true; +} + +static bool TestPath() { + // Also test the Path class while we're at it. + Path path("/asdf/jkl/"); + EXPECT_EQ_STR(path.ToString(), std::string("/asdf/jkl")); + + Path path2("/asdf/jkl"); + EXPECT_EQ_STR(path2.NavigateUp().ToString(), std::string("/asdf")); + + Path path3 = path2 / "foo/bar"; + EXPECT_EQ_STR(path3.WithExtraExtension(".txt").ToString(), std::string("/asdf/jkl/foo/bar.txt")); + + EXPECT_EQ_STR(Path("foo.bar/hello").GetFileExtension(), std::string("")); + EXPECT_EQ_STR(Path("foo.bar/hello.txt").WithReplacedExtension(".txt", ".html").ToString(), std::string("foo.bar/hello.html")); + + EXPECT_EQ_STR(Path("C:\\Yo").NavigateUp().ToString(), std::string("C:")); + EXPECT_EQ_STR(Path("C:").NavigateUp().ToString(), std::string("/")); + + EXPECT_EQ_STR(Path("C:\\Yo").GetDirectory(), std::string("C:")); + EXPECT_EQ_STR(Path("C:\\Yo").GetFilename(), std::string("Yo")); + EXPECT_EQ_STR(Path("C:\\Yo\\Lo").GetDirectory(), std::string("C:/Yo")); + EXPECT_EQ_STR(Path("C:\\Yo\\Lo").GetFilename(), std::string("Lo")); return true; } @@ -606,7 +640,9 @@ TestItem availableTests[] = { TEST_ITEM(ParseLBN), TEST_ITEM(QuickTexHash), TEST_ITEM(CLZ), + TEST_ITEM(MemMap), TEST_ITEM(ShaderGenerators), + TEST_ITEM(Path), }; int main(int argc, const char *argv[]) { diff --git a/unittest/UnitTests.vcxproj b/unittest/UnitTests.vcxproj index 5a63519960..0b73c9224a 100644 --- a/unittest/UnitTests.vcxproj +++ b/unittest/UnitTests.vcxproj @@ -409,6 +409,9 @@ {3baae095-e0ab-4b0e-b5df-ce39c8ae31de} + + {8bfd8150-94d5-4bf9-8a50-7bd9929a0850} + {f761046e-6c38-4428-a5f1-38391a37bb34}