Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 54237ffe authored by Ryan Mitchell's avatar Ryan Mitchell
Browse files

Add actor and name parsing for overlayable

Add parsing of two overlayable attributes:
 name : The unnique identifying name of the overlayable set of resources
 actor: The component responsible for enabling and disabling overlays
        targeting the specified set of resources

Bug: 110869880
Bug: 119390855
Test: m -j aapt2_tests
Change-Id: Id42463e2b92b69034fb39cd29bc8606affb61ba7
parent 066f0314
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -623,7 +623,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
              }

              // Add the pairing of overlayable properties to resource ids to the package
              OverlayableInfo overlayable_info;
              OverlayableInfo overlayable_info{};
              overlayable_info.policy_flags = policy_header->policy_flags;
              loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids));
              break;
+65 −36
Original line number Diff line number Diff line
@@ -99,7 +99,7 @@ struct ParsedResource {
  ResourceId id;
  Visibility::Level visibility_level = Visibility::Level::kUndefined;
  bool allow_new = false;
  Maybe<Overlayable> overlayable;
  Maybe<OverlayableItem> overlayable_item;

  std::string comment;
  std::unique_ptr<Value> value;
@@ -133,8 +133,8 @@ static bool AddResourcesToTable(ResourceTable* table, IDiagnostics* diag, Parsed
    }
  }

  if (res->overlayable) {
    if (!table->SetOverlayable(res->name, res->overlayable.value(), diag)) {
  if (res->overlayable_item) {
    if (!table->SetOverlayable(res->name, res->overlayable_item.value(), diag)) {
      return false;
    }
  }
@@ -1063,88 +1063,115 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource
                << "' for <overlayable> tag");
  }

  Maybe<StringPiece> overlayable_name = xml::FindNonEmptyAttribute(parser, "name");
  if (!overlayable_name) {
    diag_->Error(DiagMessage(out_resource->source)
                  << "<overlayable> tag must have a 'name' attribute");
    return false;
  }

  const std::string kActorUriScheme =
      android::base::StringPrintf("%s://", Overlayable::kActorScheme);
  Maybe<StringPiece> overlayable_actor = xml::FindNonEmptyAttribute(parser, "actor");
  if (overlayable_actor && !util::StartsWith(overlayable_actor.value(), kActorUriScheme)) {
    diag_->Error(DiagMessage(out_resource->source)
                 << "specified <overlayable> tag 'actor' attribute must use the scheme '"
                 << Overlayable::kActorScheme << "'");
    return false;
  }

  // Create a overlayable entry grouping that represents this <overlayable>
  auto overlayable = std::make_shared<Overlayable>(
      overlayable_name.value(), (overlayable_actor) ? overlayable_actor.value() : "",
      out_resource->source);

  bool error = false;
  std::string comment;
  Overlayable::PolicyFlags current_policies = Overlayable::Policy::kNone;
  OverlayableItem::PolicyFlags current_policies = OverlayableItem::Policy::kNone;
  const size_t start_depth = parser->depth();
  while (xml::XmlPullParser::IsGoodEvent(parser->Next())) {
    xml::XmlPullParser::Event event = parser->event();
    if (event == xml::XmlPullParser::Event::kEndElement && parser->depth() == start_depth) {
      // Break the loop when exiting the overlayable element
      // Break the loop when exiting the <overlayable>
      break;
    } else if (event == xml::XmlPullParser::Event::kEndElement
               && parser->depth() == start_depth + 1) {
      // Clear the current policies when exiting the policy element
      current_policies = Overlayable::Policy::kNone;
      // Clear the current policies when exiting the <policy> tags
      current_policies = OverlayableItem::Policy::kNone;
      continue;
    } else if (event == xml::XmlPullParser::Event::kComment) {
      // Get the comment of individual item elements
      // Retrieve the comment of individual <item> tags
      comment = parser->comment();
      continue;
    } else if (event != xml::XmlPullParser::Event::kStartElement) {
      // Skip to the next element
      // Skip to the start of the next element
      continue;
    }

    const Source item_source = source_.WithLine(parser->line_number());
    const Source element_source = source_.WithLine(parser->line_number());
    const std::string& element_name = parser->element_name();
    const std::string& element_namespace = parser->element_namespace();
    if (element_namespace.empty() && element_name == "item") {
      // Items specify the name and type of resource that should be overlayable
      Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
      if (!maybe_name) {
        diag_->Error(DiagMessage(item_source)
                     << "<item> within an <overlayable> tag must have a 'name' attribute");
      Maybe<StringPiece> item_name = xml::FindNonEmptyAttribute(parser, "name");
      if (!item_name) {
        diag_->Error(DiagMessage(element_source)
                     << "<item> within an <overlayable> must have a 'name' attribute");
        error = true;
        continue;
      }

      Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
      if (!maybe_type) {
        diag_->Error(DiagMessage(item_source)
                     << "<item> within an <overlayable> tag must have a 'type' attribute");
      Maybe<StringPiece> item_type = xml::FindNonEmptyAttribute(parser, "type");
      if (!item_type) {
        diag_->Error(DiagMessage(element_source)
                     << "<item> within an <overlayable> must have a 'type' attribute");
        error = true;
        continue;
      }

      const ResourceType* type = ParseResourceType(maybe_type.value());
      const ResourceType* type = ParseResourceType(item_type.value());
      if (type == nullptr) {
        diag_->Error(DiagMessage(item_source)
                     << "invalid resource type '" << maybe_type.value()
        diag_->Error(DiagMessage(element_source)
                     << "invalid resource type '" << item_type.value()
                     << "' in <item> within an <overlayable>");
        error = true;
        continue;
      }

      ParsedResource child_resource;
      OverlayableItem overlayable_item(overlayable);
      overlayable_item.policies = current_policies;
      overlayable_item.comment = comment;
      overlayable_item.source = element_source;

      ParsedResource child_resource{};
      child_resource.name.type = *type;
      child_resource.name.entry = maybe_name.value().to_string();
      child_resource.overlayable = Overlayable{current_policies, item_source, comment};
      child_resource.name.entry = item_name.value().to_string();
      child_resource.overlayable_item = overlayable_item;
      out_resource->child_resources.push_back(std::move(child_resource));

    } else if (element_namespace.empty() && element_name == "policy") {
      if (current_policies != Overlayable::Policy::kNone) {
      if (current_policies != OverlayableItem::Policy::kNone) {
        // If the policy list is not empty, then we are currently inside a policy element
        diag_->Error(DiagMessage(item_source) << "<policy> blocks cannot be recursively nested");
        diag_->Error(DiagMessage(element_source) << "<policy> blocks cannot be recursively nested");
        error = true;
        break;
      } else if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
        // Parse the polices separated by vertical bar characters to allow for specifying multiple
        // policies
        // policies. Items within the policy tag will have the specified policy.
        for (StringPiece part : util::Tokenize(maybe_type.value(), '|')) {
          StringPiece trimmed_part = util::TrimWhitespace(part);
          if (trimmed_part == "public") {
            current_policies |= Overlayable::Policy::kPublic;
            current_policies |= OverlayableItem::Policy::kPublic;
          } else if (trimmed_part == "product") {
            current_policies |= Overlayable::Policy::kProduct;
            current_policies |= OverlayableItem::Policy::kProduct;
          } else if (trimmed_part == "product_services") {
            current_policies |= Overlayable::Policy::kProductServices;
            current_policies |= OverlayableItem::Policy::kProductServices;
          } else if (trimmed_part == "system") {
            current_policies |= Overlayable::Policy::kSystem;
            current_policies |= OverlayableItem::Policy::kSystem;
          } else if (trimmed_part == "vendor") {
            current_policies |= Overlayable::Policy::kVendor;
            current_policies |= OverlayableItem::Policy::kVendor;
          } else {
            diag_->Error(DiagMessage(item_source)
            diag_->Error(DiagMessage(element_source)
                         << "<policy> has unsupported type '" << trimmed_part << "'");
            error = true;
            continue;
@@ -1152,11 +1179,13 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource
        }
      }
    } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
      diag_->Error(DiagMessage(item_source) << "invalid element <" << element_name << "> "
      diag_->Error(DiagMessage(element_source) << "invalid element <" << element_name << "> "
                                            << " in <overlayable>");
      error = true;
      break;
    }

    comment.clear();
  }

  return !error;
+89 −55
Original line number Diff line number Diff line
@@ -892,11 +892,8 @@ TEST_F(ResourceParserTest, ParsePlatformIndependentNewline) {
}

TEST_F(ResourceParserTest, ParseOverlayable) {
  std::string input = R"(<overlayable />)";
  EXPECT_TRUE(TestParse(input));

  input = R"(
      <overlayable>
  std::string input = R"(
      <overlayable name="Name" actor="overlay://theme">
        <item type="string" name="foo" />
        <item type="drawable" name="bar" />
      </overlayable>)";
@@ -905,24 +902,35 @@ TEST_F(ResourceParserTest, ParseOverlayable) {
  auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
  ASSERT_TRUE(search_result);
  ASSERT_THAT(search_result.value().entry, NotNull());
  ASSERT_TRUE(search_result.value().entry->overlayable);
  EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
              Eq(Overlayable::Policy::kNone));
  ASSERT_TRUE(search_result.value().entry->overlayable_item);
  OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
  EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));

  search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar"));
  ASSERT_TRUE(search_result);
  ASSERT_THAT(search_result.value().entry, NotNull());
  ASSERT_TRUE(search_result.value().entry->overlayable);
  EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
              Eq(Overlayable::Policy::kNone));
  ASSERT_TRUE(search_result.value().entry->overlayable_item);
  result_overlayable_item = search_result.value().entry->overlayable_item.value();
  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
  EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
}

TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
  std::string input = R"(<overlayable />)";
  EXPECT_TRUE(TestParse(input));
TEST_F(ResourceParserTest, ParseOverlayableRequiresName) {
  EXPECT_FALSE(TestParse(R"(<overlayable actor="overlay://theme" />)"));
  EXPECT_TRUE(TestParse(R"(<overlayable name="Name" />)"));
  EXPECT_TRUE(TestParse(R"(<overlayable name="Name" actor="overlay://theme" />)"));
}

  input = R"(
      <overlayable>
TEST_F(ResourceParserTest, ParseOverlayableBadActorFail) {
  EXPECT_FALSE(TestParse(R"(<overlayable name="Name" actor="overley://theme" />)"));
}

TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
  std::string input = R"(
      <overlayable name="Name">
        <item type="string" name="foo" />
        <policy type="product">
          <item type="string" name="bar" />
@@ -945,49 +953,55 @@ TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
  auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
  ASSERT_TRUE(search_result);
  ASSERT_THAT(search_result.value().entry, NotNull());
  ASSERT_TRUE(search_result.value().entry->overlayable);
  Overlayable& overlayable = search_result.value().entry->overlayable.value();
  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kNone));
  ASSERT_TRUE(search_result.value().entry->overlayable_item);
  OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));

  search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
  ASSERT_TRUE(search_result);
  ASSERT_THAT(search_result.value().entry, NotNull());
  ASSERT_TRUE(search_result.value().entry->overlayable);
  overlayable = search_result.value().entry->overlayable.value();
  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProduct));
  ASSERT_TRUE(search_result.value().entry->overlayable_item);
  result_overlayable_item = search_result.value().entry->overlayable_item.value();
  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct));

  search_result = table_.FindResource(test::ParseNameOrDie("string/baz"));
  ASSERT_TRUE(search_result);
  ASSERT_THAT(search_result.value().entry, NotNull());
  ASSERT_TRUE(search_result.value().entry->overlayable);
  overlayable = search_result.value().entry->overlayable.value();
  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProductServices));
  ASSERT_TRUE(search_result.value().entry->overlayable_item);
  result_overlayable_item = search_result.value().entry->overlayable_item.value();
  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProductServices));

  search_result = table_.FindResource(test::ParseNameOrDie("string/fiz"));
  ASSERT_TRUE(search_result);
  ASSERT_THAT(search_result.value().entry, NotNull());
  ASSERT_TRUE(search_result.value().entry->overlayable);
  overlayable = search_result.value().entry->overlayable.value();
  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kSystem));
  ASSERT_TRUE(search_result.value().entry->overlayable_item);
  result_overlayable_item = search_result.value().entry->overlayable_item.value();
  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSystem));

  search_result = table_.FindResource(test::ParseNameOrDie("string/fuz"));
  ASSERT_TRUE(search_result);
  ASSERT_THAT(search_result.value().entry, NotNull());
  ASSERT_TRUE(search_result.value().entry->overlayable);
  overlayable = search_result.value().entry->overlayable.value();
  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kVendor));
  ASSERT_TRUE(search_result.value().entry->overlayable_item);
  result_overlayable_item = search_result.value().entry->overlayable_item.value();
  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor));

  search_result = table_.FindResource(test::ParseNameOrDie("string/faz"));
  ASSERT_TRUE(search_result);
  ASSERT_THAT(search_result.value().entry, NotNull());
  ASSERT_TRUE(search_result.value().entry->overlayable);
  overlayable = search_result.value().entry->overlayable.value();
  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kPublic));
  ASSERT_TRUE(search_result.value().entry->overlayable_item);
  result_overlayable_item = search_result.value().entry->overlayable_item.value();
  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
}

TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) {
  std::string input = R"(
      <overlayable>
      <overlayable name="Name">
        <policy type="illegal_policy">
          <item type="string" name="foo" />
        </policy>
@@ -995,7 +1009,7 @@ TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) {
  EXPECT_FALSE(TestParse(input));

  input = R"(
      <overlayable>
      <overlayable name="Name">
        <policy type="product">
          <item name="foo" />
        </policy>
@@ -1003,7 +1017,7 @@ TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) {
  EXPECT_FALSE(TestParse(input));

  input = R"(
      <overlayable>
      <overlayable name="Name">
        <policy type="vendor">
          <item type="string" />
        </policy>
@@ -1013,7 +1027,7 @@ TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) {

TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) {
  std::string input = R"(
      <overlayable>
      <overlayable name="Name">
        <policy type="vendor|product_services">
          <item type="string" name="foo" />
        </policy>
@@ -1026,39 +1040,59 @@ TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) {
  auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
  ASSERT_TRUE(search_result);
  ASSERT_THAT(search_result.value().entry, NotNull());
  ASSERT_TRUE(search_result.value().entry->overlayable);
  Overlayable& overlayable = search_result.value().entry->overlayable.value();
  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kVendor
                                       | Overlayable::Policy::kProductServices));
  ASSERT_TRUE(search_result.value().entry->overlayable_item);
  OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor
                                                   | OverlayableItem::Policy::kProductServices));

  search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
  ASSERT_TRUE(search_result);
  ASSERT_THAT(search_result.value().entry, NotNull());
  ASSERT_TRUE(search_result.value().entry->overlayable);
  overlayable = search_result.value().entry->overlayable.value();
  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProduct
                                       | Overlayable::Policy::kSystem));
  ASSERT_TRUE(search_result.value().entry->overlayable_item);
  result_overlayable_item = search_result.value().entry->overlayable_item.value();
  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
                                                   | OverlayableItem::Policy::kSystem));
}

TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
  std::string input = R"(
      <overlayable>
      <overlayable name="Name">
        <item type="string" name="foo" />
        <item type="string" name="foo" />
      </overlayable>)";
  EXPECT_FALSE(TestParse(input));

  input = R"(
      <overlayable name="Name">
        <item type="string" name="foo" />
      </overlayable>
      <overlayable name="Name">
        <item type="string" name="foo" />
      </overlayable>)";
  EXPECT_FALSE(TestParse(input));

  input = R"(
      <overlayable name="Name">
        <item type="string" name="foo" />
      </overlayable>
      <overlayable name="Other">
        <item type="string" name="foo" />
      </overlayable>)";
  EXPECT_FALSE(TestParse(input));

  input = R"(
      <overlayable>
      <overlayable name="Name" actor="overlay://my.actor.one">
        <item type="string" name="foo" />
      </overlayable>
      <overlayable>
      <overlayable name="Other" actor="overlay://my.actor.two">
        <item type="string" name="foo" />
      </overlayable>)";
  EXPECT_FALSE(TestParse(input));

  input = R"(
      <overlayable>
      <overlayable name="Name">
        <policy type="product">
          <item type="string" name="foo" />
          <item type="string" name="foo" />
@@ -1067,7 +1101,7 @@ TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
  EXPECT_FALSE(TestParse(input));

  input = R"(
      <overlayable>
      <overlayable name="Name">
        <policy type="product">
          <item type="string" name="foo" />
        </policy>
@@ -1076,7 +1110,7 @@ TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
  EXPECT_FALSE(TestParse(input));

  input = R"(
      <overlayable>
      <overlayable name="Name">
        <policy type="product">
          <item type="string" name="foo" />
        </policy>
@@ -1087,13 +1121,13 @@ TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
  EXPECT_FALSE(TestParse(input));

  input = R"(
      <overlayable>
      <overlayable name="Name">
        <policy type="product">
          <item type="string" name="foo" />
        </policy>
      </overlayable>

      <overlayable>
      <overlayable name="Name">
        <policy type="product">
          <item type="string" name="foo" />
        </policy>
@@ -1103,7 +1137,7 @@ TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {

TEST_F(ResourceParserTest, NestPolicyInOverlayableError) {
  std::string input = R"(
      <overlayable>
      <overlayable name="Name">
        <policy type="vendor|product">
          <policy type="product_services">
            <item type="string" name="foo" />
+12 −8
Original line number Diff line number Diff line
@@ -40,6 +40,8 @@ using ::android::base::StringPrintf;

namespace aapt {

const char* Overlayable::kActorScheme = "overlay";

static bool less_than_type_and_id(const std::unique_ptr<ResourceTableType>& lhs,
                                  const std::pair<ResourceType, Maybe<uint8_t>>& rhs) {
  return lhs->type < rhs.first || (lhs->type == rhs.first && rhs.second && lhs->id < rhs.second);
@@ -625,17 +627,18 @@ bool ResourceTable::SetAllowNewImpl(const ResourceNameRef& name, const AllowNew&
  return true;
}

bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const OverlayableItem& overlayable,
                                   IDiagnostics* diag) {
  return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
}

bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name,
                                          const Overlayable& overlayable, IDiagnostics* diag) {
                                          const OverlayableItem& overlayable, IDiagnostics* diag) {
  return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag);
}

bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name,
                                       const OverlayableItem& overlayable,
                                       NameValidator name_validator, IDiagnostics *diag) {
  CHECK(diag != nullptr);

@@ -647,14 +650,15 @@ bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overla
  ResourceTableType* type = package->FindOrCreateType(name.type);
  ResourceEntry* entry = type->FindOrCreateEntry(name.entry);

  if (entry->overlayable) {
  if (entry->overlayable_item) {
    diag->Error(DiagMessage(overlayable.source)
                << "duplicate overlayable declaration for resource '" << name << "'");
    diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here");
    diag->Error(DiagMessage(entry->overlayable_item.value().source)
                << "previous declaration here");
    return false;
  }

  entry->overlayable = overlayable;
  entry->overlayable_item = overlayable;
  return true;
}

@@ -690,7 +694,7 @@ std::unique_ptr<ResourceTable> ResourceTable::Clone() const {
        new_entry->id = entry->id;
        new_entry->visibility = entry->visibility;
        new_entry->allow_new = entry->allow_new;
        new_entry->overlayable = entry->overlayable;
        new_entry->overlayable_item = entry->overlayable_item;

        for (const auto& config_value : entry->values) {
          ResourceConfigValue* new_value =
+24 −8

File changed.

Preview size limit exceeded, changes collapsed.

Loading