Rewrite MakeIco
There is some cursed memory problem that only ever manifests in ghactions workflows and for cursed people. I'm neither so I just rewrote the whole thing from scratch, slightly better than last time.
This commit is contained in:
parent
452b553350
commit
1ef0c1a3e0
@ -1,67 +1,137 @@
|
|||||||
#include <fstream>
|
|
||||||
#include <vector>
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
static void writeU32LE(uint8_t *dest, uint32_t value)
|
||||||
|
{
|
||||||
|
dest[0] = uint8_t( value & 0xFF);
|
||||||
|
dest[1] = uint8_t((value >> 8) & 0xFF);
|
||||||
|
dest[2] = uint8_t((value >> 16) & 0xFF);
|
||||||
|
dest[3] = uint8_t((value >> 24) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t readU32BE(const uint8_t *src)
|
||||||
|
{
|
||||||
|
return uint32_t(src[3]) |
|
||||||
|
(uint32_t(src[2]) << 8) |
|
||||||
|
(uint32_t(src[1]) << 16) |
|
||||||
|
(uint32_t(src[0]) << 24);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
if (argc < 3)
|
if (argc < 3)
|
||||||
{
|
{
|
||||||
return 1;
|
std::cerr << "usage: " << argv[0] << " OUTPUT INPUT..." << std::endl;
|
||||||
}
|
exit(1);
|
||||||
auto *outputIcoPath = argv[1];
|
|
||||||
std::ofstream outputIco(outputIcoPath, std::ios::binary);
|
|
||||||
if (!outputIco)
|
|
||||||
{
|
|
||||||
return 2;
|
|
||||||
}
|
}
|
||||||
auto images = argc - 2;
|
auto images = argc - 2;
|
||||||
std::vector<char> header(22 + images * 16);
|
if (images > 255)
|
||||||
auto *incondir = &header[0];
|
|
||||||
*reinterpret_cast<uint16_t *>(&incondir[0]) = 0; // reserved
|
|
||||||
*reinterpret_cast<uint16_t *>(&incondir[2]) = 1; // icon
|
|
||||||
*reinterpret_cast<uint16_t *>(&incondir[4]) = uint16_t(images);
|
|
||||||
std::vector<char> allData;
|
|
||||||
for (auto i = 0; i < images; ++i)
|
|
||||||
{
|
{
|
||||||
auto *inputAnyPath = argv[i + 2];
|
std::cerr << "too many images specified" << std::endl;
|
||||||
std::ifstream inputAny(inputAnyPath, std::ios::binary);
|
exit(1);
|
||||||
std::vector<char> data;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
char ch;
|
|
||||||
inputAny.read(&ch, 1);
|
|
||||||
if (inputAny.eof())
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!inputAny)
|
|
||||||
{
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
data.push_back(ch);
|
|
||||||
}
|
|
||||||
if (*reinterpret_cast<uint64_t *>(&data[0]) != UINT64_C(0x0A1A0A0D474E5089)) // png magic
|
|
||||||
{
|
|
||||||
return 5;
|
|
||||||
}
|
|
||||||
auto width = uint8_t(data[19]);
|
|
||||||
auto height = uint8_t(data[23]);
|
|
||||||
auto *incondirentry = &header[6 + i * 16];
|
|
||||||
*reinterpret_cast<uint8_t *>(&incondirentry[0]) = width;
|
|
||||||
*reinterpret_cast<uint8_t *>(&incondirentry[1]) = height;
|
|
||||||
*reinterpret_cast<uint8_t *>(&incondirentry[2]) = 0; // no color palette
|
|
||||||
*reinterpret_cast<uint8_t *>(&incondirentry[3]) = 0; // reserved
|
|
||||||
*reinterpret_cast<uint16_t *>(&incondirentry[4]) = 1; // 1 color plane
|
|
||||||
*reinterpret_cast<uint16_t *>(&incondirentry[6]) = 32; // 32 bits per pixel
|
|
||||||
*reinterpret_cast<uint32_t *>(&incondirentry[8]) = uint32_t(data.size()); // data size
|
|
||||||
*reinterpret_cast<uint32_t *>(&incondirentry[12]) = uint32_t(header.size() + allData.size()); // data offset
|
|
||||||
allData.insert(allData.end(), data.begin(), data.end());
|
|
||||||
}
|
}
|
||||||
outputIco.write(&header[0], header.size());
|
std::string outputPath = argv[1];
|
||||||
outputIco.write(&allData[0], allData.size());
|
std::ofstream output(outputPath, std::ios::binary);
|
||||||
if (!outputIco)
|
auto outputFailure = [&outputPath](std::string action) {
|
||||||
|
std::cerr << "failed to " << action << " " << outputPath << ": " << strerror(errno) << std::endl;
|
||||||
|
exit(1);
|
||||||
|
};
|
||||||
|
if (!output)
|
||||||
{
|
{
|
||||||
return 4;
|
outputFailure("open");
|
||||||
}
|
}
|
||||||
|
std::vector<char> header(6 + images * 16, 0);
|
||||||
|
auto writeHeader = [&header, &output, &outputFailure]() {
|
||||||
|
output.seekp(0, std::ios_base::beg);
|
||||||
|
output.write(&header[0], header.size());
|
||||||
|
if (!output)
|
||||||
|
{
|
||||||
|
outputFailure("write");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
writeHeader(); // make space for header
|
||||||
|
auto *headerU8 = reinterpret_cast<uint8_t *>(&header[0]);
|
||||||
|
headerU8[2] = 1;
|
||||||
|
headerU8[4] = images;
|
||||||
|
for (auto image = 0; image < images; ++image)
|
||||||
|
{
|
||||||
|
std::string inputPath = argv[2 + image];
|
||||||
|
std::ifstream input(inputPath, std::ios::binary);
|
||||||
|
auto inputFailure = [&inputPath](std::string action) {
|
||||||
|
std::cerr << "failed to " << action << " " << inputPath << ": " << strerror(errno) << std::endl;
|
||||||
|
exit(1);
|
||||||
|
};
|
||||||
|
auto imageFailure = [&inputPath](std::string failure) {
|
||||||
|
std::cerr << "failed to process " << inputPath << ": " << failure << std::endl;
|
||||||
|
exit(1);
|
||||||
|
};
|
||||||
|
if (!input)
|
||||||
|
{
|
||||||
|
inputFailure("open");
|
||||||
|
}
|
||||||
|
std::vector<char> buf;
|
||||||
|
input.seekg(0, std::ios_base::end);
|
||||||
|
buf.resize(input.tellg());
|
||||||
|
input.seekg(0, std::ios_base::beg);
|
||||||
|
input.read(&buf[0], buf.size());
|
||||||
|
if (!input)
|
||||||
|
{
|
||||||
|
inputFailure("read");
|
||||||
|
}
|
||||||
|
auto *bufU8 = reinterpret_cast<uint8_t *>(&buf[0]);
|
||||||
|
if (buf.size() < 0x21 ||
|
||||||
|
readU32BE(&bufU8[0]) != UINT32_C(0x89504E47) ||
|
||||||
|
readU32BE(&bufU8[4]) != UINT32_C(0x0D0A1A0A) ||
|
||||||
|
bufU8[0x18] != 8 ||
|
||||||
|
bufU8[0x19] != 6)
|
||||||
|
{
|
||||||
|
imageFailure("not a 32bpp RGBA PNG");
|
||||||
|
}
|
||||||
|
auto writeOffset = output.tellp();
|
||||||
|
output.write(&buf[0], buf.size());
|
||||||
|
if (!output)
|
||||||
|
{
|
||||||
|
outputFailure("write");
|
||||||
|
}
|
||||||
|
auto width = readU32BE(&bufU8[0x10]);
|
||||||
|
auto height = readU32BE(&bufU8[0x14]);
|
||||||
|
if (width == 256)
|
||||||
|
{
|
||||||
|
width = 0;
|
||||||
|
}
|
||||||
|
if (width > 255)
|
||||||
|
{
|
||||||
|
imageFailure("width exceeds U8 limit");
|
||||||
|
}
|
||||||
|
if (height == 256)
|
||||||
|
{
|
||||||
|
height = 0;
|
||||||
|
}
|
||||||
|
if (height > 255)
|
||||||
|
{
|
||||||
|
imageFailure("height exceeds U8 limit");
|
||||||
|
}
|
||||||
|
auto *entryU8 = headerU8 + 6 + image * 16;
|
||||||
|
entryU8[0] = width;
|
||||||
|
entryU8[1] = height;
|
||||||
|
entryU8[4] = 1;
|
||||||
|
entryU8[6] = 32;
|
||||||
|
if (buf.size() > UINT32_MAX)
|
||||||
|
{
|
||||||
|
imageFailure("data size exceeds U32 limit");
|
||||||
|
}
|
||||||
|
writeU32LE(&entryU8[8], uint32_t(buf.size()));
|
||||||
|
if (writeOffset > UINT32_MAX)
|
||||||
|
{
|
||||||
|
std::cerr << "output data size exceeds U32 limit" << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
writeU32LE(&entryU8[12], uint32_t(writeOffset));
|
||||||
|
}
|
||||||
|
writeHeader(); // actually write it out
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user