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 <sstream>

#include "LIEF/Visitor.hpp"
#include "LIEF/utils.hpp"
#include "LIEF/PE/resources/ResourceDialogExtended.hpp"

#include "LIEF/BinaryStream/BinaryStream.hpp"

#include "logging.hpp"
#include "fmt_formatter.hpp"

FMT_FORMATTER(LIEF::PE::ResourceDialog::DIALOG_STYLES, LIEF::PE::to_string);
FMT_FORMATTER(LIEF::PE::ResourceDialog::WINDOW_EXTENDED_STYLES,
              LIEF::PE::to_string);
FMT_FORMATTER(LIEF::PE::ResourceDialog::WINDOW_STYLES, LIEF::PE::to_string);
FMT_FORMATTER(LIEF::PE::ResourceDialog::CONTROL_STYLES, LIEF::PE::to_string);

namespace LIEF::PE {

result<ResourceDialogExtended::Item>
    ResourceDialogExtended::Item::parse(BinaryStream& stream) {
  // typedef struct {
  //   DWORD     helpID;
  //   DWORD     exStyle;
  //   DWORD     style;
  //   short     x;
  //   short     y;
  //   short     cx;
  //   short     cy;
  //   DWORD     id;
  //   sz_Or_Ord windowClass;
  //   sz_Or_Ord title;
  //   WORD      extraCount;
  // } DLGITEMTEMPLATEEX;
  stream.align(sizeof(uint32_t));

  auto helpID = stream.read<uint32_t>();
  if (!helpID) {
    return make_error_code(helpID.error());
  }

  auto exStyle = stream.read<uint32_t>();
  if (!exStyle) {
    return make_error_code(exStyle.error());
  }

  auto style = stream.read<uint32_t>();
  if (!style) {
    return make_error_code(style.error());
  }

  auto x = stream.read<uint16_t>();
  if (!x) {
    return make_error_code(x.error());
  }

  auto y = stream.read<uint16_t>();
  if (!y) {
    return make_error_code(y.error());
  }

  auto cx = stream.read<uint16_t>();
  if (!cx) {
    return make_error_code(cx.error());
  }

  auto cy = stream.read<uint16_t>();
  if (!cy) {
    return make_error_code(cy.error());
  }

  auto id = stream.read<int32_t>();
  if (!id) {
    return make_error_code(id.error());
  }

  ResourceDialogExtended::Item item;
  item.help_id(*helpID)
      .style(*style)
      .extended_style(*exStyle)
      .x(*x)
      .y(*y)
      .cx(*cx)
      .cy(*cy)
      .id(*id);

  stream.align(sizeof(uint16_t));

  if (auto is_ok = parse_class(item, stream); !is_ok) {
    return item;
  }

  if (auto is_ok = parse_title(item, stream); !is_ok) {
    return item;
  }

  if (auto is_ok = parse_creation_data(item, stream); !is_ok) {
    return item;
  }

  return item;
}


ok_error_t parse_typeface(ResourceDialogExtended& dialog, BinaryStream& stream) {
  if (!dialog.has(ResourceDialog::DIALOG_STYLES::SETFONT) &&
      !dialog.has(ResourceDialog::DIALOG_STYLES::SHELLFONT))
  {
    return ok();
  }
  stream.align(sizeof(uint16_t));

  auto point_size = stream.read<uint16_t>();
  if (!point_size) {
    return make_error_code(point_size.error());
  }

  auto weight = stream.read<uint16_t>();
  if (!weight) {
    return make_error_code(weight.error());
  }

  auto italic = stream.read<uint8_t>();
  if (!italic) {
    return make_error_code(italic.error());
  }

  auto charset = stream.read<uint8_t>();
  if (!charset) {
    return make_error_code(charset.error());
  }

  auto typeface = stream.read_u16string();
  if (!typeface) {
    return make_error_code(typeface.error());
  }

  dialog.font(*point_size, *weight, (bool)*italic, *charset, std::move(*typeface));

  stream.align(sizeof(uint16_t));
  return ok();
}


std::unique_ptr<ResourceDialogExtended>
    ResourceDialogExtended::create(BinaryStream& stream) {
  auto version = stream.read<uint16_t>();
  if (!version) {
    return nullptr;
  }

  auto signature = stream.read<uint16_t>();

  if (!signature || *signature != 0xFFFF) {
    return nullptr;
  }

  auto help_id = stream.read<uint32_t>();
  if (!help_id) {
    return nullptr;
  }

  auto ext_style = stream.read<uint32_t>();
  if (!ext_style) {
    return nullptr;
  }

  auto style = stream.read<uint32_t>();
  if (!style) {
    return nullptr;
  }

  auto nb_items = stream.read<uint16_t>();
  if (!nb_items) {
    return nullptr;
  }

  auto x = stream.read<int16_t>();
  if (!x) {
    return nullptr;
  }

  auto y = stream.read<int16_t>();
  if (!y) {
    return nullptr;
  }

  auto cx = stream.read<int16_t>();
  if (!cx) {
    return nullptr;
  }

  auto cy = stream.read<int16_t>();
  if (!cy) {
    return nullptr;
  }

  auto dialog = std::make_unique<ResourceDialogExtended>();
  (*dialog)
      .version(*version)
      .signature(*signature)
      .help_id(*help_id)
      .extended_style(*ext_style)
      .style(*style)
      .x(*x)
      .y(*y)
      .cx(*cx)
      .cy(*cy);

  if (auto is_ok = parse_menu(*dialog, stream); !is_ok) {
    return dialog;
  }

  if (auto is_ok = parse_class(*dialog, stream); !is_ok) {
    return dialog;
  }

  if (auto is_ok = parse_title(*dialog, stream); !is_ok) {
    return dialog;
  }

  if (auto is_ok = parse_typeface(*dialog, stream); !is_ok) {
    return dialog;
  }

  LIEF_DEBUG("Dialog item count: {}", *nb_items);
  for (size_t i = 0; i < *nb_items; ++i) {
    auto item = ResourceDialogExtended::Item::parse(stream);
    if (!item) {
      LIEF_INFO("Failed to parse dialog item #{:02d}", i);
      return dialog;
    }
    dialog->add_item(*item);
  }

  LIEF_DEBUG("#{} items parsed", dialog->items_.size());

  return dialog;
}

std::string ResourceDialogExtended::font_t::to_string() const {
  return fmt::format("{}, {}", point_size, u16tou8(typeface));
}

std::string ResourceDialogExtended::Item::to_string() const {
  std::string win_class_str;
  {
    if (auto ord = clazz().ordinal) {
      switch ((WINDOW_CLASS)*ord) {
        case WINDOW_CLASS::BUTTON: win_class_str = "Button"; break;
        case WINDOW_CLASS::EDIT: win_class_str = "Edit"; break;
        case WINDOW_CLASS::STATIC: win_class_str = "Static"; break;
        case WINDOW_CLASS::LIST_BOX: win_class_str = "List box"; break;
        case WINDOW_CLASS::SCROLL_BAR: win_class_str = "Scroll bar"; break;
        case WINDOW_CLASS::COMBO_BOX: win_class_str = "Combo box"; break;
        default: win_class_str = fmt::format("unknown ({:#06x})", *ord); break;
      }
    } else {
      win_class_str = u16tou8(clazz().string);
    }
  }

  return fmt::format("CONTROL '{}', {}, {}, {} {}, {}, {}, {}, {}",
                     title().to_string(), id(), win_class_str,
                     fmt::join(control_styles(), " | "),
                     fmt::join(window_styles(), " | "), x(), y(), cx(), cy());
}

std::string ResourceDialogExtended::to_string() const {
  std::ostringstream oss;
  oss << fmt::format("DIALOGEX {}, {}, {}, {}\n", x(), y(), cx(), cy());

  oss << fmt::format("STYLE: {} {}\n", fmt::join(styles_list(), " | "),
                     fmt::join(windows_styles_list(), " | "));
  if (auto m = menu()) {
    oss << fmt::format("MENU: {}\n", m.to_string());
  }

  if (auto c = window_class()) {
    oss << fmt::format("WINDOWS CLASS: {}\n", c.to_string());
  }

  oss << "CAPTION: \"" << title_utf8() << "\"\n";

  if (const font_t& f = font()) {
    oss << "FONT: " << f.to_string() << '\n';
  }

  if (items_.empty()) {
    return oss.str();
  }

  oss << "{\n";
  for (const Item& item : items()) {
    oss << "  " << item << '\n';
  }
  oss << "}\n";

  return oss.str();
}

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