123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- /**
- * Copyright (c) 2006-2023 LOVE Development Team
- *
- * This software is provided 'as-is', without any express or implied
- * warranty. In no event will the authors be held liable for any damages
- * arising from the use of this software.
- *
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- *
- * 1. The origin of this software must not be misrepresented; you must not
- * claim that you wrote the original software. If you use this software
- * in a product, an acknowledgment in the product documentation would be
- * appreciated but is not required.
- * 2. Altered source versions must be plainly marked as such, and must not be
- * misrepresented as being the original software.
- * 3. This notice may not be removed or altered from any source distribution.
- **/
- #include <cassert>
- #include <algorithm>
- #include "PhysfsIo.h"
- namespace love
- {
- namespace filesystem
- {
- namespace physfs
- {
- bool StripSuffixIo::determineStrippedLength()
- {
- if (!file) {
- return false;
- }
- const int64_t fullSize = fullLength();
- int64_t chunkSize = std::min(fullSize, (int64_t)8192);
- std::string buffer;
- buffer.reserve(chunkSize);
- int64_t i = fullSize - chunkSize;
- // I don't think we really need to go through the whole file. The main known use
- // case for this functionality is to skip windows codesign signatures, which are
- // from what I have seen ~12KB or so, but trying is better than just failing.
- while (i >= 0)
- {
- buffer.resize(chunkSize);
- if (seek(i) == 0)
- return false;
- const auto n = read(&buffer[0], chunkSize);
- if (n <= 0)
- return false;
- buffer.resize(n);
- // We are looking for the magic bytes that indicate the start
- // of the "End of cental directory record (EOCD)".
- // As this is most likely not a multi-disk zip, we could include 4 bytes of 0x00,
- // but I'd rather make fewer assumptions.
- const auto endOfCentralDirectory = buffer.rfind("\x50\x4B\x05\x06");
- if (endOfCentralDirectory != std::string::npos)
- {
- i = i + endOfCentralDirectory;
- break;
- }
- if (i == 0)
- break;
- i = std::max((int64_t)0, i - chunkSize);
- }
- if (i > 0)
- {
- // The EOCD record is at least 22 bytes but may include a comment
- if (i + 22 > fullSize)
- return false; // Incomplete central directory
- // The comment length (u16) is located 20 bytes from the start of the EOCD record
- if (seek(i + 20) == 0)
- return false;
- uint8_t buffer[2];
- const auto n = read(buffer, 2);
- if (n <= 0)
- return false;
- const auto commentSize = (buffer[1] << 8) | buffer[0];
- if (i + 22 + commentSize > fullSize) // Comment incomplete
- return false;
- // We pretend the file ends just after the comment
- // (which should be the end of the embedded zip file)
- strippedLength_ = i + 22 + commentSize;
- }
- else
- {
- strippedLength_ = fullSize;
- }
- if (seek(0) == 0)
- return false;
- return true;
- }
- int64_t StripSuffixIo::read(void* buf, uint64_t len)
- {
- if (!file)
- {
- PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
- return -1;
- }
- const auto ret = std::fread(buf, 1, len, file);
- if (ret == 0)
- {
- if (std::feof(file))
- {
- PHYSFS_setErrorCode(PHYSFS_ERR_OK);
- return 0;
- }
- else
- {
- PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
- return -1;
- }
- }
- else if (ret < len && std::ferror(file))
- {
- PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
- return -1;
- }
- PHYSFS_setErrorCode(PHYSFS_ERR_OK);
- return ret;
- }
- int64_t StripSuffixIo::write(const void* /*buf*/, uint64_t /*len*/)
- {
- PHYSFS_setErrorCode(PHYSFS_ERR_READ_ONLY);
- return -1;
- }
- int64_t StripSuffixIo::seek(uint64_t offset)
- {
- if (!file)
- {
- PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
- return 0;
- }
- const auto ret = std::fseek(file, offset, SEEK_SET);
- PHYSFS_setErrorCode(ret != 0 ? PHYSFS_ERR_OS_ERROR : PHYSFS_ERR_OK);
- return ret == 0 ? 1 : 0;
- }
- int64_t StripSuffixIo::tell()
- {
- if (!file)
- {
- PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
- return -1;
- }
- return std::ftell(file);
- }
- int64_t StripSuffixIo::length()
- {
- return strippedLength_;
- }
- int64_t StripSuffixIo::flush()
- {
- if (!file)
- {
- PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
- return 0;
- }
- return std::fflush(file) == 0 ? 1 : 0;
- }
- int64_t StripSuffixIo::fullLength()
- {
- assert(file);
- const auto cur = std::ftell(file);
- if (cur == -1)
- {
- PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
- return -1;
- }
- if (std::fseek(file, 0, SEEK_END) != 0)
- {
- PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
- return -1;
- }
- const auto len = std::ftell(file);
- if (len == -1)
- {
- PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
- return -1;
- }
- if (std::fseek(file, cur, SEEK_SET) != 0)
- {
- // We do have the length now, but something is wrong, so we return an error anyways
- PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
- return -1;
- }
- return len;
- }
- } // physfs
- } // filesystem
- } // love
|