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

Commit ed2af4df authored by Ryan Mitchell's avatar Ryan Mitchell Committed by Android (Google) Code Review
Browse files

Merge "Add actor and name parsing for overlayable"

parents 7b2b4c83 54237ffe
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