current commit
260929.11 · deps: update lief to 1.0.0
/* Copyright 2017 - 2026 R. Thomas
 * Copyright 2017 - 2026 Quarkslab
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <fstream>

#include "LIEF/iostream.hpp"
#include "LIEF/hash.hpp"
#include "LIEF/BinaryStream/SpanStream.hpp"

#include "LIEF/PE/resources/ResourceIcon.hpp"
#include "PE/Structures.hpp"

#include "logging.hpp"
#include <spdlog/fmt/fmt.h>


namespace LIEF::PE {

ResourceIcon::ResourceIcon(const details::pe_resource_icon_group& header) :
  width_{header.width},
  height_{header.height},
  color_count_{header.color_count},
  reserved_{header.reserved},
  planes_{header.planes},
  bit_count_{header.bit_count},
  id_{header.ID} {}


ResourceIcon::ResourceIcon(const details::pe_icon_header& header) :
  width_{header.width},
  height_{header.height},
  color_count_{header.color_count},
  reserved_{header.reserved},
  planes_{header.planes},
  bit_count_{header.bit_count},
  id_{static_cast<uint32_t>(-1)} {}

result<ResourceIcon> ResourceIcon::from_serialization(const uint8_t* buffer,
                                                      size_t size) {
  const uint32_t minsz =
      sizeof(details::pe_resource_icon_dir) + sizeof(details::pe_icon_header);
  if (size < minsz) {
    return make_error_code(lief_errors::read_error);
  }

  SpanStream stream(buffer, size);
  /* reserved */ stream.read<uint16_t>();
  auto type = stream.read<uint16_t>().value_or(0);
  if (type != 1) {
    return make_error_code(lief_errors::corrupted);
  }

  auto count = stream.read<uint16_t>().value_or(0);
  if (count != 1) {
    return make_error_code(lief_errors::corrupted);
  }

  ResourceIcon icon;

  icon.width(stream.read<uint8_t>().value_or(0));
  icon.height(stream.read<uint8_t>().value_or(0));
  icon.color_count(stream.read<uint8_t>().value_or(0));
  icon.reserved(stream.read<uint8_t>().value_or(0));
  icon.planes(stream.read<uint16_t>().value_or(0));
  icon.bit_count(stream.read<uint16_t>().value_or(0));

  auto pixel_size = stream.read<uint32_t>().value_or(0);
  if (pixel_size == 0) {
    return make_error_code(lief_errors::corrupted);
  }

  auto offset = stream.read<uint32_t>().value_or(0);
  if (offset == 0 || (offset + pixel_size) > size) {
    return make_error_code(lief_errors::corrupted);
  }
  std::vector<uint8_t> pixels;
  stream.read_data(pixels, pixel_size);
  icon.pixels(std::move(pixels));
  return icon;
}

std::vector<uint8_t> ResourceIcon::serialize() const {
  if (pixels_.empty()) {
    return {};
  }

  vector_iostream ios;
  ios.reserve(sizeof(details::pe_resource_icon_dir) +
              sizeof(details::pe_icon_header) + pixels_.size());

  ios
      // pe_resource_icon_dir header
      .write<uint16_t>(/*reserved*/ 0)
      .write<uint16_t>(/*type=icon*/ 1)
      .write<uint16_t>(/*count*/ 1)
      // pe_icon_header header
      .write<uint8_t>(width())
      .write<uint8_t>(height())
      .write<uint8_t>(color_count())
      .write<uint8_t>(reserved())
      .write<uint16_t>(planes())
      .write<uint16_t>(bit_count())
      .write<uint32_t>(size())
      .write<uint32_t>((uint32_t)ios.tellp() + sizeof(uint32_t))
      // Raw pixels
      .write(pixels());
  return ios.raw();
}

void ResourceIcon::save(const std::string& filename) const {
  std::ofstream output_file(filename,
                            std::ios::out | std::ios::binary | std::ios::trunc);
  if (!output_file) {
    LIEF_ERR("Failed to open {} for writing", filename);
    return;
  }
  std::vector<uint8_t> raw = serialize();
  output_file.write(reinterpret_cast<const char*>(raw.data()), raw.size());
}

void ResourceIcon::accept(Visitor& visitor) const {
  visitor.visit(*this);
}

std::ostream& operator<<(std::ostream& os, const ResourceIcon& icon) {
  static constexpr auto DEFAULT_ALIGN = 13;
  os << fmt::format("Icon id={:#06x}\n", icon.id());
  os << fmt::format("  {:{}}: {}x{}\n", "size", DEFAULT_ALIGN, icon.width(),
                    icon.height());
  os << fmt::format("  {:{}}: {}\n", "color count", DEFAULT_ALIGN,
                    icon.color_count());
  os << fmt::format("  {:{}}: {}\n", "reserved", DEFAULT_ALIGN, icon.reserved());
  os << fmt::format("  {:{}}: {}\n", "planes", DEFAULT_ALIGN, icon.planes());
  os << fmt::format("  {:{}}: {}\n", "bit count", DEFAULT_ALIGN, icon.bit_count());
  os << fmt::format("  {:{}}: {:#010x}\n", "pixel (hash)", DEFAULT_ALIGN,
                    Hash::hash(icon.pixels()));
  return os;
}


}