mirror of
https://github.com/mirkobrombin/raven.git
synced 2026-10-30 21:35:06 +00:00
107 lines
4.8 KiB
C++
107 lines
4.8 KiB
C++
#include "icon_group.h"
|
|
#include "pe/loaded_image.h"
|
|
#include "pe/resources.h"
|
|
|
|
#include <array>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
|
|
static void check(bool condition, const char *message)
|
|
{
|
|
if (!condition)
|
|
throw std::runtime_error(message);
|
|
}
|
|
|
|
static std::vector<uint8_t> read_file(const char *path)
|
|
{
|
|
std::ifstream file(path, std::ios::binary | std::ios::ate);
|
|
check(bool(file) && file.tellg() > 0 && file.tellg() < 1024 * 1024 * 1024, "invalid group fixture");
|
|
std::vector<uint8_t> bytes(static_cast<size_t>(file.tellg()));
|
|
file.seekg(0);
|
|
check(bool(file.read(reinterpret_cast<char *>(bytes.data()), static_cast<std::streamsize>(bytes.size()))), "cannot read group fixture");
|
|
return bytes;
|
|
}
|
|
|
|
static void rejected(std::span<const uint8_t> bytes, std::string_view expected)
|
|
{
|
|
std::string failure;
|
|
try { raven::decode_icon_group(bytes); } catch (const std::runtime_error &error) { failure = error.what(); }
|
|
check(failure.find(expected) != failure.npos, "group rejection failed for another reason");
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
try {
|
|
check(argc == 2 || argc == 3, "application and optional selection output required");
|
|
std::vector<uint8_t> bytes{0,0,1,0,1,0,0,0,0,0,1,0,32,0,1,0,0,0,5,0};
|
|
const auto decoded = raven::decode_icon_group(bytes);
|
|
check(decoded.size() == 1 && decoded[0].width == 256 && decoded[0].height == 256 && decoded[0].id == 5,
|
|
"zero directory dimensions did not decode as 256");
|
|
for (size_t size = 0; size < bytes.size(); ++size)
|
|
rejected(std::span(bytes).first(size), size < 6 ? "header" : "truncated");
|
|
auto invalid = bytes;
|
|
invalid[0] = 1; rejected(invalid, "header");
|
|
invalid = bytes; invalid[2] = 2; rejected(invalid, "header");
|
|
invalid = bytes; invalid[4] = 0; rejected(invalid, "empty");
|
|
invalid = bytes; invalid[9] = 1; rejected(invalid, "entry");
|
|
invalid = bytes; invalid[14] = 0; rejected(invalid, "entry");
|
|
invalid = bytes; invalid[18] = 0; rejected(invalid, "entry");
|
|
const std::array<raven::IconEntry, 4> entries{{{16,16,32,1,1}, {32,32,8,1,2}, {32,32,32,1,3}, {48,48,32,1,4}}};
|
|
check(raven::lookup_icon(entries, 24, 24, {}, false) == 1 &&
|
|
raven::lookup_icon(entries, 25, 25, {}, false) == 3, "nearest reference size or tie order differs");
|
|
check(raven::lookup_icon(entries, 24, 24, {}, true) == 2, "color depth did not resolve a size-distance tie");
|
|
check(raven::lookup_icon(entries, 0, 0, {48,48,16}, false) == 4 &&
|
|
raven::lookup_icon(entries, 0, 0, {32,32,16}, false) == 2 &&
|
|
raven::lookup_icon(entries, 32, 32, {}, true) == 2, "display or minimum depth selection differs");
|
|
check(raven::lookup_icon(entries, 0, 16, {48,48,32}, false) == 1 &&
|
|
raven::lookup_icon({}, 1, 1, {}, false) == 0, "one-axis metric default or empty lookup differs");
|
|
const auto file_bytes = read_file(argv[1]);
|
|
raven::PEImage image(file_bytes);
|
|
raven::LoadedImage loaded(image, image.image_base);
|
|
const auto resources = image.resources();
|
|
std::ofstream output;
|
|
if (argc == 3) {
|
|
output.open(argv[2], std::ios::trunc);
|
|
check(bool(output), "cannot save icon selection output");
|
|
}
|
|
unsigned groups = 0;
|
|
for (const auto &resource : resources) {
|
|
if (resource.type != raven::ResourceKey(uint32_t(14)))
|
|
continue;
|
|
const auto group = raven::decode_icon_group(loaded.data().subspan(resource.data_address, resource.size));
|
|
for (const auto &entry : group) {
|
|
bool found = false;
|
|
for (const auto &target : resources)
|
|
if (target.type == raven::ResourceKey(uint32_t(3)) && target.name == raven::ResourceKey(uint32_t(entry.id)) &&
|
|
target.language == resource.language && target.size == entry.size)
|
|
found = true;
|
|
check(found, "real group references missing image bytes");
|
|
}
|
|
const std::array<uint32_t, 25> sizes{0,1,12,13,15,16,17,20,24,25,31,32,33,40,47,48,49,64,96,128,192,200,255,256,300};
|
|
const auto record = [&](uint32_t width, uint32_t height, bool monochrome) {
|
|
const auto id = raven::lookup_icon(group, width, height, {}, monochrome);
|
|
check(id != 0, "real icon group produced no selection");
|
|
if (output.is_open())
|
|
output << std::get<uint32_t>(resource.name) << ',' << width << ',' << height << ',' << monochrome << ',' << id << '\n';
|
|
};
|
|
for (bool monochrome : {false, true}) {
|
|
for (const auto size : sizes)
|
|
record(size, size, monochrome);
|
|
for (uint32_t width = 16; width <= 48; width += 8)
|
|
for (uint32_t height = 16; height <= 48; height += 8)
|
|
if (width != height)
|
|
record(width, height, monochrome);
|
|
}
|
|
++groups;
|
|
}
|
|
check(groups > 0 && (!output.is_open() || bool(output)), "icon group evidence failed");
|
|
std::cout << groups << " real icon groups, checked references, size/depth selection and malformed controls passed\n";
|
|
return 0;
|
|
} catch (const std::exception &error) {
|
|
std::cerr << error.what() << '\n';
|
|
return 1;
|
|
}
|
|
}
|