连接器客户端
This commit is contained in:
1 parent
d10807d877
commit
41151b7f8b
28 files changed
+2195
-12
No files matched your search
@@ -0,0 +1,136 @@
|
||||
#include "zip.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace connector {
|
||||
namespace {
|
||||
|
||||
void append_u16(std::vector<std::uint8_t>& output, std::uint16_t value) {
|
||||
output.push_back(static_cast<std::uint8_t>(value));
|
||||
output.push_back(static_cast<std::uint8_t>(value >> 8));
|
||||
}
|
||||
|
||||
void append_u32(std::vector<std::uint8_t>& output, std::uint32_t value) {
|
||||
append_u16(output, static_cast<std::uint16_t>(value));
|
||||
append_u16(output, static_cast<std::uint16_t>(value >> 16));
|
||||
}
|
||||
|
||||
std::uint32_t crc32(const std::vector<std::uint8_t>& data) {
|
||||
static const std::array<std::uint32_t, 256> table = [] {
|
||||
std::array<std::uint32_t, 256> values{};
|
||||
for (std::uint32_t value = 0; value < values.size(); ++value) {
|
||||
std::uint32_t crc = value;
|
||||
for (int bit = 0; bit < 8; ++bit) crc = (crc & 1) ? 0xedb88320U ^ (crc >> 1) : crc >> 1;
|
||||
values[value] = crc;
|
||||
}
|
||||
return values;
|
||||
}();
|
||||
std::uint32_t crc = 0xffffffffU;
|
||||
for (const auto byte : data) crc = table[(crc ^ byte) & 0xff] ^ (crc >> 8);
|
||||
return crc ^ 0xffffffffU;
|
||||
}
|
||||
|
||||
std::string archive_path(const std::string& raw) {
|
||||
std::string value = raw;
|
||||
for (char& c : value) if (c == '\\') c = '/';
|
||||
while (value.rfind("./", 0) == 0) value.erase(0, 2);
|
||||
if (value.empty() || value.front() == '/' || value == ".." || value.rfind("../", 0) == 0 ||
|
||||
value.find("/../") != std::string::npos) {
|
||||
throw std::runtime_error("ZIP 文件路径无效");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
struct Timestamp {
|
||||
std::uint16_t time;
|
||||
std::uint16_t date;
|
||||
};
|
||||
|
||||
Timestamp dos_timestamp() {
|
||||
const std::time_t now = std::time(nullptr);
|
||||
std::tm local{};
|
||||
#ifdef _WIN32
|
||||
localtime_s(&local, &now);
|
||||
#else
|
||||
localtime_r(&now, &local);
|
||||
#endif
|
||||
const int year = std::max(1980, local.tm_year + 1900);
|
||||
return {
|
||||
static_cast<std::uint16_t>((local.tm_hour << 11) | (local.tm_min << 5) | (local.tm_sec / 2)),
|
||||
static_cast<std::uint16_t>(((year - 1980) << 9) | ((local.tm_mon + 1) << 5) | local.tm_mday),
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::vector<std::uint8_t> create_zip(const std::vector<ArchiveFile>& files) {
|
||||
if (files.size() > 65535) throw std::runtime_error("ZIP 文件数量过多");
|
||||
struct CentralRecord {
|
||||
std::string name;
|
||||
std::uint32_t crc;
|
||||
std::uint32_t size;
|
||||
std::uint32_t offset;
|
||||
};
|
||||
std::vector<std::uint8_t> output;
|
||||
std::vector<CentralRecord> records;
|
||||
const Timestamp timestamp = dos_timestamp();
|
||||
for (const auto& file : files) {
|
||||
const std::string name = archive_path(file.name);
|
||||
if (name.size() > 65535 || file.data.size() > 0xffffffffULL || output.size() > 0xffffffffULL) {
|
||||
throw std::runtime_error("ZIP 文件超过 Store 格式限制");
|
||||
}
|
||||
const auto checksum = crc32(file.data);
|
||||
records.push_back({name, checksum, static_cast<std::uint32_t>(file.data.size()),
|
||||
static_cast<std::uint32_t>(output.size())});
|
||||
append_u32(output, 0x04034b50);
|
||||
append_u16(output, 20);
|
||||
append_u16(output, 0x0800);
|
||||
append_u16(output, 0);
|
||||
append_u16(output, timestamp.time);
|
||||
append_u16(output, timestamp.date);
|
||||
append_u32(output, checksum);
|
||||
append_u32(output, static_cast<std::uint32_t>(file.data.size()));
|
||||
append_u32(output, static_cast<std::uint32_t>(file.data.size()));
|
||||
append_u16(output, static_cast<std::uint16_t>(name.size()));
|
||||
append_u16(output, 0);
|
||||
output.insert(output.end(), name.begin(), name.end());
|
||||
output.insert(output.end(), file.data.begin(), file.data.end());
|
||||
}
|
||||
const auto central_offset = static_cast<std::uint32_t>(output.size());
|
||||
for (const auto& record : records) {
|
||||
append_u32(output, 0x02014b50);
|
||||
append_u16(output, 20);
|
||||
append_u16(output, 20);
|
||||
append_u16(output, 0x0800);
|
||||
append_u16(output, 0);
|
||||
append_u16(output, timestamp.time);
|
||||
append_u16(output, timestamp.date);
|
||||
append_u32(output, record.crc);
|
||||
append_u32(output, record.size);
|
||||
append_u32(output, record.size);
|
||||
append_u16(output, static_cast<std::uint16_t>(record.name.size()));
|
||||
append_u16(output, 0);
|
||||
append_u16(output, 0);
|
||||
append_u16(output, 0);
|
||||
append_u16(output, 0);
|
||||
append_u32(output, 0);
|
||||
append_u32(output, record.offset);
|
||||
output.insert(output.end(), record.name.begin(), record.name.end());
|
||||
}
|
||||
const auto central_size = static_cast<std::uint32_t>(output.size()) - central_offset;
|
||||
append_u32(output, 0x06054b50);
|
||||
append_u16(output, 0);
|
||||
append_u16(output, 0);
|
||||
append_u16(output, static_cast<std::uint16_t>(records.size()));
|
||||
append_u16(output, static_cast<std::uint16_t>(records.size()));
|
||||
append_u32(output, central_size);
|
||||
append_u32(output, central_offset);
|
||||
append_u16(output, 0);
|
||||
return output;
|
||||
}
|
||||
|
||||
} // namespace connector
|
||||
Reference in new issue
Block a user