Decompress Steam's cloud-compression ZIP. Returns false (and leaves out untouched) on non-ZIP or failure.
| 106 | |
| 107 | // Decompress Steam's cloud-compression ZIP. Returns false (and leaves out untouched) on non-ZIP or failure. |
| 108 | static bool TryDecompressZip(const std::vector<uint8_t>& data, std::vector<uint8_t>& out) { |
| 109 | if (data.size() < 4) return false; |
| 110 | if (data[0] != 0x50 || data[1] != 0x4B || data[2] != 0x03 || data[3] != 0x04) |
| 111 | return false; |
| 112 | |
| 113 | mz_zip_archive zip{}; |
| 114 | if (!mz_zip_reader_init_mem(&zip, data.data(), data.size(), 0)) { |
| 115 | LOG("[HttpServer] ZIP init failed: %s", mz_zip_get_error_string(mz_zip_get_last_error(&zip))); |
| 116 | return false; |
| 117 | } |
| 118 | |
| 119 | // Steam cloud ZIP is single-entry, inner name "z"; anything else is a game save that's natively ZIP. |
| 120 | if (mz_zip_reader_get_num_files(&zip) != 1) { |
| 121 | mz_zip_reader_end(&zip); |
| 122 | return false; |
| 123 | } |
| 124 | mz_zip_archive_file_stat fstat; |
| 125 | if (!mz_zip_reader_file_stat(&zip, 0, &fstat) || |
| 126 | strcmp(fstat.m_filename, "z") != 0) { |
| 127 | mz_zip_reader_end(&zip); |
| 128 | return false; |
| 129 | } |
| 130 | |
| 131 | // Reject if uncompressed size exceeds 512 MB or would overflow size_t |
| 132 | if (fstat.m_uncomp_size > 512ULL * 1024 * 1024 || fstat.m_uncomp_size > SIZE_MAX) { |
| 133 | LOG("[HttpServer] ZIP rejected: declared size %llu", (unsigned long long)fstat.m_uncomp_size); |
| 134 | mz_zip_reader_end(&zip); |
| 135 | return false; |
| 136 | } |
| 137 | |
| 138 | size_t uncompSize = 0; |
| 139 | void* p = mz_zip_reader_extract_to_heap(&zip, 0, &uncompSize, 0); |
| 140 | mz_zip_reader_end(&zip); |
| 141 | |
| 142 | if (!p) { |
| 143 | LOG("[HttpServer] ZIP extract failed"); |
| 144 | return false; |
| 145 | } |
| 146 | |
| 147 | out.assign(static_cast<uint8_t*>(p), static_cast<uint8_t*>(p) + uncompSize); |
| 148 | mz_free(p); |
| 149 | return true; |
| 150 | } |
| 151 | |
| 152 | // Verify the connecting client is our own process via /proc/net/tcp inode lookup. |
| 153 | static bool IsConnectionFromSteam(int clientFd) { |
no test coverage detected