@ 261107.7
current
/* 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 "LIEF/PE/ResourceData.hpp"
#include "LIEF/PE/resources/ResourceDialog.hpp"
#include "LIEF/PE/resources/ResourceDialogRegular.hpp"
#include "LIEF/PE/resources/ResourceDialogExtended.hpp"

#include "LIEF/BinaryStream/SpanStream.hpp"
#include "LIEF/utils.hpp"

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

#include "PE/resources/styles_array.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);

namespace LIEF::PE {

static bool is_extended(BinaryStream& strm) {
  ScopedStream scope(strm);
  auto version = scope->read<uint16_t>().value_or(0);
  auto signature = scope->read<uint16_t>().value_or(0);
  return version == 1 && signature == 0xFFFF;
}

ResourceDialog::dialogs_t ResourceDialog::parse(const uint8_t* buffer,
                                                size_t size) {
  if (size == 0) {
    return {};
  }
  dialogs_t dialogs;

  SpanStream strm(buffer, size);
  LIEF_DEBUG("Parsing dialogs from stream size: {:#010x}", size);
  while (strm) {
    std::unique_ptr<ResourceDialog> dia;
    if (is_extended(strm)) {
      dia = ResourceDialogExtended::create(strm);
    } else {
      dia = ResourceDialogRegular::create(strm);
    }

    if (dia != nullptr) {
      dialogs.push_back(std::move(dia));
    }

    break;
  }

  LIEF_DEBUG("{} bytes left", (int64_t)size - (int64_t)strm.pos());
  return dialogs;
}

ResourceDialog::dialogs_t ResourceDialog::parse(const ResourceData& node) {
  span<const uint8_t> content = node.content();
  if (content.empty()) {
    return {};
  }

  return parse(content.data(), content.size());
}

ok_error_t ResourceDialog::parse_menu(ResourceDialog& dialog,
                                      BinaryStream& stream) {
  auto menu = stream.read<uint16_t>();
  if (!menu) {
    return make_error_code(menu.error());
  }

  switch (*menu) {
    case 0x0000: break;
    case 0xFFFF:
    {
      auto ordinal = stream.read<uint16_t>();
      if (!ordinal) {
        return make_error_code(ordinal.error());
      }
      dialog.menu(*ordinal);
      break;
    }

    default:
    {
      stream.decrement_pos(2); // because of the first read<uint16_t>
      auto name = stream.read_u16string();
      if (!name) {
        return make_error_code(name.error());
      }
      dialog.menu(std::move(*name));
      break;
    }
  }
  stream.align(sizeof(uint16_t));
  return ok();
}

ok_error_t ResourceDialog::parse_class(ResourceDialog& dialog,
                                       BinaryStream& stream) {
  auto clazz = stream.read<uint16_t>();
  if (!clazz) {
    return make_error_code(clazz.error());
  }

  switch (*clazz) {
    case 0x0000: break;
    case 0xFFFF:
    {
      auto ordinal = stream.read<uint16_t>();
      if (!ordinal) {
        return make_error_code(ordinal.error());
      }
      dialog.window_class(*ordinal);
      break;
    }
    default:
    {
      stream.decrement_pos(2);
      auto name = stream.read_u16string();
      if (!name) {
        return make_error_code(name.error());
      }
      dialog.window_class(std::move(*name));
      break;
    }
  }
  stream.align(sizeof(uint16_t));
  return ok();
}

ok_error_t ResourceDialog::parse_class(ResourceDialog::Item& item,
                                       BinaryStream& stream) {
  auto clazz = stream.read<uint16_t>();
  if (!clazz) {
    return make_error_code(clazz.error());
  }

  switch (*clazz) {
    case 0xFFFF:
    {
      auto ordinal = stream.read<uint16_t>();
      if (!ordinal) {
        return make_error_code(ordinal.error());
      }
      item.clazz(*ordinal);
      break;
    }
    default:
    {
      stream.decrement_pos(2);
      auto name = stream.read_u16string();
      if (!name) {
        return make_error_code(name.error());
      }
      item.clazz(std::move(*name));
      break;
    }
  }
  stream.align(sizeof(uint16_t));
  return ok();
}


ok_error_t ResourceDialog::parse_title(ResourceDialog& dialog,
                                       BinaryStream& stream) {
  auto title = stream.read_u16string();

  if (!title) {
    return make_error_code(title.error());
  }

  dialog.title(std::move(*title));
  stream.align(sizeof(uint16_t));
  return ok();
}

ok_error_t ResourceDialog::parse_title(ResourceDialog::Item& item,
                                       BinaryStream& stream) {
  auto info = stream.read<uint16_t>();
  if (!info) {
    return make_error_code(info.error());
  }

  switch (*info) {
    case 0xFFFF:
    {
      auto ordinal = stream.read<uint16_t>();
      if (!ordinal) {
        return make_error_code(ordinal.error());
      }
      item.title(*ordinal);
      break;
    }
    default:
    {
      stream.decrement_pos(2);
      auto name = stream.read_u16string();
      if (!name) {
        return make_error_code(name.error());
      }
      item.title(std::move(*name));
      break;
    }
  }
  stream.align(sizeof(uint16_t));
  return ok();
}

ok_error_t ResourceDialog::parse_creation_data(ResourceDialog::Item& item,
                                               BinaryStream& stream) {
  auto size = stream.read<uint16_t>();
  if (!size) {
    return make_error_code(size.error());
  }
  std::vector<uint8_t> raw;
  auto is_ok = stream.read_data(raw, *size);

  item.data(std::move(raw));
  return is_ok;
}

std::vector<ResourceDialog::DIALOG_STYLES> ResourceDialog::styles_list() const {
  std::vector<DIALOG_STYLES> flags;
  std::copy_if(DIALOG_STYLES_ARRAY.begin(), DIALOG_STYLES_ARRAY.end(),
               std::back_inserter(flags),
               [this](DIALOG_STYLES f) { return has(f); });
  return flags;
}

std::vector<ResourceDialog::WINDOW_STYLES>
    ResourceDialog::windows_styles_list() const {
  std::vector<WINDOW_STYLES> flags;
  std::copy_if(WINDOW_STYLES_ARRAY.begin(), WINDOW_STYLES_ARRAY.end(),
               std::back_inserter(flags),
               [this](WINDOW_STYLES f) { return has(f); });
  return flags;
}

std::vector<ResourceDialog::WINDOW_EXTENDED_STYLES>
    ResourceDialog::windows_ext_styles_list() const {
  std::vector<WINDOW_EXTENDED_STYLES> flags;
  std::copy_if(WINDOW_EXTENDED_STYLES_ARRAY.begin(),
               WINDOW_EXTENDED_STYLES_ARRAY.end(), std::back_inserter(flags),
               [this](WINDOW_EXTENDED_STYLES f) { return has(f); });
  return flags;
}


std::string ResourceDialog::title_utf8() const {
  return u16tou8(title_);
}

std::string ResourceDialog::ordinal_or_str_t::to_string() const {
  if (ordinal) {
    return fmt::format("ord={}", *ordinal);
  }
  return u16tou8(string);
}

std::vector<ResourceDialog::WINDOW_STYLES>
    ResourceDialog::Item::window_styles() const {
  std::vector<WINDOW_STYLES> flags;
  std::copy_if(WINDOW_STYLES_ARRAY.begin(), WINDOW_STYLES_ARRAY.end(),
               std::back_inserter(flags),
               [this](WINDOW_STYLES f) { return has(f); });
  return flags;
}

std::vector<ResourceDialog::CONTROL_STYLES>
    ResourceDialog::Item::control_styles() const {
  std::vector<CONTROL_STYLES> flags;
  std::copy_if(CONTROL_STYLES_ARRAY.begin(), CONTROL_STYLES_ARRAY.end(),
               std::back_inserter(flags),
               [this](CONTROL_STYLES f) { return has(f); });
  return flags;
}

ResourceDialog& ResourceDialog::title(const std::string& title) {
  if (auto u16 = u8tou16(title)) {
    return this->title(std::move(*u16));
  }
  return *this;
}


const char* to_string(ResourceDialog::DIALOG_STYLES e) {
#define ENTRY(X) std::pair(ResourceDialog::DIALOG_STYLES::X, #X)
  STRING_MAP enums2str{
      ENTRY(ABSALIGN),      ENTRY(SYSMODAL),    ENTRY(LOCALEDIT),
      ENTRY(SETFONT),       ENTRY(MODALFRAME),  ENTRY(NOIDLEMSG),
      ENTRY(SETFOREGROUND), ENTRY(S3DLOOK),     ENTRY(FIXEDSYS),
      ENTRY(NOFAILCREATE),  ENTRY(CONTROL),     ENTRY(CENTER),
      ENTRY(CENTERMOUSE),   ENTRY(CONTEXTHELP), ENTRY(SHELLFONT),
  };
#undef ENTRY

  if (auto it = enums2str.find(e); it != enums2str.end()) {
    return it->second;
  }
  return "UNKNOWN";
}

const char* to_string(ResourceDialog::WINDOW_STYLES e) {
#define ENTRY(X) std::pair(ResourceDialog::WINDOW_STYLES::X, #X)
  STRING_MAP enums2str{
      ENTRY(OVERLAPPED), ENTRY(POPUP),    ENTRY(CHILD),        ENTRY(MINIMIZE),
      ENTRY(VISIBLE),    ENTRY(DISABLED), ENTRY(CLIPSIBLINGS), ENTRY(CLIPCHILDREN),
      ENTRY(MAXIMIZE),   ENTRY(CAPTION),  ENTRY(BORDER),       ENTRY(DLGFRAME),
      ENTRY(VSCROLL),    ENTRY(HSCROLL),  ENTRY(SYSMENU),      ENTRY(THICKFRAME),
      ENTRY(GROUP),      ENTRY(TABSTOP),
  };
#undef ENTRY

  if (auto it = enums2str.find(e); it != enums2str.end()) {
    return it->second;
  }
  return "UNKNOWN";
}


const char* to_string(ResourceDialog::WINDOW_EXTENDED_STYLES e) {
#define ENTRY(X) std::pair(ResourceDialog::WINDOW_EXTENDED_STYLES::X, #X)
  STRING_MAP enums2str{
      ENTRY(DLGMODALFRAME), ENTRY(NOPARENTNOTIFY),  ENTRY(TOPMOST),
      ENTRY(ACCEPTFILES),   ENTRY(TRANSPARENT_STY), ENTRY(MDICHILD),
      ENTRY(TOOLWINDOW),    ENTRY(WINDOWEDGE),      ENTRY(CLIENTEDGE),
      ENTRY(CONTEXTHELP),   ENTRY(RIGHT),           ENTRY(LEFT),
      ENTRY(RTLREADING),    ENTRY(LEFTSCROLLBAR),   ENTRY(CONTROLPARENT),
      ENTRY(STATICEDGE),    ENTRY(APPWINDOW),
  };
#undef ENTRY

  if (auto it = enums2str.find(e); it != enums2str.end()) {
    return it->second;
  }
  return "UNKNOWN";
}

const char* to_string(ResourceDialog::CONTROL_STYLES e) {
#define ENTRY(X) std::pair(ResourceDialog::CONTROL_STYLES::X, #X)
  STRING_MAP enums2str{
      ENTRY(TOP),           ENTRY(NOMOVEY),    ENTRY(BOTTOM),    ENTRY(NORESIZE),
      ENTRY(NOPARENTALIGN), ENTRY(ADJUSTABLE), ENTRY(NODIVIDER), ENTRY(VERT),
      ENTRY(LEFT),          ENTRY(RIGHT),      ENTRY(NOMOVEX),
  };
#undef ENTRY

  if (auto it = enums2str.find(e); it != enums2str.end()) {
    return it->second;
  }
  return "UNKNOWN";
}

}