1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-15 11:03:00 +01:00

Rename G1Element members, add unions for palette entries

This commit is contained in:
Gymnasiast
2025-12-27 14:30:36 +01:00
parent 8ce8eec426
commit e6fa262b9e
18 changed files with 92 additions and 84 deletions

View File

@@ -888,7 +888,7 @@ void OpenGLDrawingContext::DrawSprite(RenderTarget& rt, const ImageId imageId, c
zoomedRT.width = rt.width;
zoomedRT.pitch = rt.pitch;
zoomedRT.zoom_level = rt.zoom_level - 1;
DrawSprite(zoomedRT, imageId.WithIndex(imageId.GetIndex() - g1Element->zoomed_offset), x >> 1, y >> 1);
DrawSprite(zoomedRT, imageId.WithIndex(imageId.GetIndex() - g1Element->zoomedOffset), x >> 1, y >> 1);
return;
}
if (g1Element->flags & G1_FLAG_NO_ZOOM_DRAW)
@@ -899,8 +899,8 @@ void OpenGLDrawingContext::DrawSprite(RenderTarget& rt, const ImageId imageId, c
auto texture = _textureCache->GetOrLoadImageTexture(imageId);
int32_t left = x + g1Element->x_offset;
int32_t top = y + g1Element->y_offset;
int32_t left = x + g1Element->xOffset;
int32_t top = y + g1Element->yOffset;
int32_t xModifier = 0;
int32_t yModifier = 0;
@@ -1015,8 +1015,8 @@ void OpenGLDrawingContext::DrawSpriteRawMasked(
const auto textureMask = _textureCache->GetOrLoadImageTexture(maskImage);
const auto textureColour = _textureCache->GetOrLoadImageTexture(colourImage);
int32_t drawOffsetX = g1ElementMask->x_offset;
int32_t drawOffsetY = g1ElementMask->y_offset;
int32_t drawOffsetX = g1ElementMask->xOffset;
int32_t drawOffsetY = g1ElementMask->yOffset;
int32_t drawWidth = std::min(g1ElementMask->width, g1ElementColour->width);
int32_t drawHeight = std::min(g1ElementMask->height, g1ElementColour->height);
@@ -1075,8 +1075,8 @@ void OpenGLDrawingContext::DrawSpriteSolid(RenderTarget& rt, const ImageId image
const auto texture = _textureCache->GetOrLoadImageTexture(image);
int32_t drawOffsetX = g1Element->x_offset;
int32_t drawOffsetY = g1Element->y_offset;
int32_t drawOffsetX = g1Element->xOffset;
int32_t drawOffsetY = g1Element->yOffset;
int32_t drawWidth = static_cast<uint16_t>(g1Element->width);
int32_t drawHeight = static_cast<uint16_t>(g1Element->height);
@@ -1127,8 +1127,8 @@ void OpenGLDrawingContext::DrawGlyph(RenderTarget& rt, const ImageId image, int3
const auto texture = _textureCache->GetOrLoadGlyphTexture(image, palette);
int32_t left = x + g1Element->x_offset;
int32_t top = y + g1Element->y_offset;
int32_t left = x + g1Element->xOffset;
int32_t top = y + g1Element->yOffset;
int32_t right = left + static_cast<uint16_t>(g1Element->width);
int32_t bottom = top + static_cast<uint16_t>(g1Element->height);

View File

@@ -226,7 +226,7 @@ void TextureCache::GeneratePaletteTexture()
const auto* element = GfxGetG1Element(g1Index.value());
if (element != nullptr)
{
GfxDrawSpriteSoftware(rt, ImageId(g1Index.value()), { -element->x_offset, y - element->y_offset });
GfxDrawSpriteSoftware(rt, ImageId(g1Index.value()), { -element->xOffset, y - element->yOffset });
}
}
}
@@ -361,7 +361,7 @@ RenderTarget TextureCache::GetImageAsDPI(const ImageId imageId)
int32_t height = g1Element->height;
RenderTarget rt = CreateDPI(width, height);
GfxDrawSpriteSoftware(rt, imageId, { -g1Element->x_offset, -g1Element->y_offset });
GfxDrawSpriteSoftware(rt, imageId, { -g1Element->xOffset, -g1Element->yOffset });
return rt;
}
@@ -373,7 +373,7 @@ RenderTarget TextureCache::GetGlyphAsDPI(const ImageId imageId, const PaletteMap
RenderTarget rt = CreateDPI(width, height);
const auto glyphCoords = ScreenCoordsXY{ -g1Element->x_offset, -g1Element->y_offset };
const auto glyphCoords = ScreenCoordsXY{ -g1Element->xOffset, -g1Element->yOffset };
GfxDrawSpritePaletteSetSoftware(rt, imageId, glyphCoords, palette);
return rt;
}

View File

@@ -151,7 +151,7 @@ namespace OpenRCT2::Scripting
DukObject obj(ctx);
obj.Set("id", id);
obj.Set("offset", ToDuk<ScreenCoordsXY>(ctx, { g1->x_offset, g1->y_offset }));
obj.Set("offset", ToDuk<ScreenCoordsXY>(ctx, { g1->xOffset, g1->yOffset }));
obj.Set("width", g1->width);
obj.Set("height", g1->height);
@@ -162,7 +162,7 @@ namespace OpenRCT2::Scripting
if (g1->flags & G1_FLAG_HAS_ZOOM_SPRITE)
{
obj.Set("nextZoomId", id - g1->zoomed_offset);
obj.Set("nextZoomId", id - g1->zoomedOffset);
}
else
{

View File

@@ -58,8 +58,8 @@ namespace OpenRCT2::CommandLine::Sprite
G1Element* g1 = &spriteFile->Entries[spriteIndex];
printf("width: %d\n", g1->width);
printf("height: %d\n", g1->height);
printf("x offset: %d\n", g1->x_offset);
printf("y offset: %d\n", g1->y_offset);
printf("x offset: %d\n", g1->xOffset);
printf("y offset: %d\n", g1->yOffset);
printf("data offset: %p\n", g1->offset);
return 0;
}

View File

@@ -47,7 +47,7 @@ namespace OpenRCT2::CommandLine::Sprite
fprintf(stderr, "Could not export\n");
return -1;
}
fprintf(stdout, "{ \"x\": %d, \"y\": %d }\n", spriteHeader.x_offset, spriteHeader.y_offset);
fprintf(stdout, "{ \"x\": %d, \"y\": %d }\n", spriteHeader.xOffset, spriteHeader.yOffset);
return 0;
}
} // namespace OpenRCT2::CommandLine::Sprite

View File

@@ -71,7 +71,7 @@ namespace OpenRCT2::CommandLine::Sprite
}
path = fs::u8path(path).generic_u8string();
fprintf(stdout, "{ \"path\": \"%s\", \"x\": %d, \"y\": %d }", path.c_str(), g1.x_offset, g1.y_offset);
fprintf(stdout, "{ \"path\": \"%s\", \"x\": %d, \"y\": %d }", path.c_str(), g1.xOffset, g1.yOffset);
}
fprintf(stdout, (spriteIndex + 1 != maxIndex) ? ",\n" : "\n");

View File

@@ -35,10 +35,10 @@ namespace OpenRCT2::CommandLine::Sprite
entry.offset = reinterpret_cast<uint8_t*>(static_cast<uintptr_t>(entry32bit.offset));
entry.width = entry32bit.width;
entry.height = entry32bit.height;
entry.x_offset = entry32bit.x_offset;
entry.y_offset = entry32bit.y_offset;
entry.xOffset = entry32bit.x_offset;
entry.yOffset = entry32bit.y_offset;
entry.flags = entry32bit.flags;
entry.zoomed_offset = entry32bit.zoomed_offset;
entry.zoomedOffset = entry32bit.zoomed_offset;
spriteFile.Entries.push_back(std::move(entry));
}
spriteFile.Data.resize(spriteFile.Header.total_size);
@@ -109,10 +109,10 @@ namespace OpenRCT2::CommandLine::Sprite
entry32bit.offset = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(const_cast<uint8_t*>(entry.offset)));
entry32bit.width = entry.width;
entry32bit.height = entry.height;
entry32bit.x_offset = entry.x_offset;
entry32bit.y_offset = entry.y_offset;
entry32bit.x_offset = entry.xOffset;
entry32bit.y_offset = entry.yOffset;
entry32bit.flags = entry.flags;
entry32bit.zoomed_offset = entry.zoomed_offset;
entry32bit.zoomed_offset = entry.zoomedOffset;
stream.Write(&entry32bit, sizeof(entry32bit));
}

View File

@@ -122,7 +122,7 @@ static void OverrideElementOffsets(size_t index, G1Element& element)
switch (index)
{
case 25285:
element.x_offset -= 1;
element.xOffset -= 1;
break;
case 25286:
case 25317:
@@ -288,16 +288,16 @@ static void OverrideElementOffsets(size_t index, G1Element& element)
case 25850:
case 25851:
case 25852:
element.y_offset += 1;
element.yOffset += 1;
break;
case 25307:
case 25315:
case 25319:
element.x_offset -= 1;
element.y_offset += 1;
element.xOffset -= 1;
element.yOffset += 1;
break;
case 25802:
element.y_offset += 2;
element.yOffset += 2;
break;
}
}
@@ -341,17 +341,17 @@ static void ReadAndConvertGxDat(IStream* stream, size_t count, bool is_rctc, G1E
elements[i].offset = reinterpret_cast<uint8_t*>(static_cast<uintptr_t>(src.offset));
elements[i].width = src.width;
elements[i].height = src.height;
elements[i].x_offset = src.x_offset;
elements[i].y_offset = src.y_offset;
elements[i].xOffset = src.x_offset;
elements[i].yOffset = src.y_offset;
elements[i].flags = src.flags;
if (src.flags & G1_FLAG_HAS_ZOOM_SPRITE)
{
elements[i].zoomed_offset = static_cast<int32_t>(i - rctc_to_rct2_index(rctc - src.zoomed_offset));
elements[i].zoomedOffset = static_cast<int32_t>(i - rctc_to_rct2_index(rctc - src.zoomed_offset));
}
else
{
elements[i].zoomed_offset = src.zoomed_offset;
elements[i].zoomedOffset = src.zoomed_offset;
}
++rctc;
@@ -364,8 +364,8 @@ static void ReadAndConvertGxDat(IStream* stream, size_t count, bool is_rctc, G1E
{
for (auto i = 0u; i < SPR_PEEP_PICKUP_COUNT; ++i)
{
elements[animation.start + i].x_offset -= animation.x_offset;
elements[animation.start + i].y_offset -= animation.y_offset;
elements[animation.start + i].xOffset -= animation.x_offset;
elements[animation.start + i].yOffset -= animation.y_offset;
}
}
}
@@ -380,10 +380,10 @@ static void ReadAndConvertGxDat(IStream* stream, size_t count, bool is_rctc, G1E
elements[i].offset = reinterpret_cast<uint8_t*>(static_cast<uintptr_t>(src.offset));
elements[i].width = src.width;
elements[i].height = src.height;
elements[i].x_offset = src.x_offset;
elements[i].y_offset = src.y_offset;
elements[i].xOffset = src.x_offset;
elements[i].yOffset = src.y_offset;
elements[i].flags = src.flags;
elements[i].zoomed_offset = src.zoomed_offset;
elements[i].zoomedOffset = src.zoomed_offset;
}
}
}
@@ -652,7 +652,7 @@ bool GfxLoadCsg()
// RCT1 used zoomed offsets that counted from the beginning of the file, rather than from the current sprite.
if (_csg.elements[i].flags & G1_FLAG_HAS_ZOOM_SPRITE)
{
_csg.elements[i].zoomed_offset = i - _csg.elements[i].zoomed_offset;
_csg.elements[i].zoomedOffset = i - _csg.elements[i].zoomedOffset;
}
}
_csgLoaded = true;
@@ -781,7 +781,7 @@ void FASTCALL GfxDrawSpritePaletteSetSoftware(
const auto spriteCoords = ScreenCoordsXY{ coords.x / 2, coords.y / 2 };
GfxDrawSpritePaletteSetSoftware(
zoomedRT, imageId.WithIndex(imageId.GetIndex() - g1->zoomed_offset), spriteCoords, paletteMap);
zoomedRT, imageId.WithIndex(imageId.GetIndex() - g1->zoomedOffset), spriteCoords, paletteMap);
return;
}
@@ -795,11 +795,11 @@ void FASTCALL GfxDrawSpritePaletteSetSoftware(
// For the moment, I've added this block here just for magnification with the old code continuing below.
if (zoomLevel < ZoomLevel{ 0 })
{
ScreenCoordsXY spriteTopLeft = { zoomLevel.ApplyInversedTo(coords.x + g1->x_offset),
zoomLevel.ApplyInversedTo(coords.y + g1->y_offset) };
ScreenCoordsXY spriteTopLeft = { zoomLevel.ApplyInversedTo(coords.x + g1->xOffset),
zoomLevel.ApplyInversedTo(coords.y + g1->yOffset) };
ScreenCoordsXY spriteBottomLeft{ zoomLevel.ApplyInversedTo(coords.x + g1->x_offset + g1->width),
zoomLevel.ApplyInversedTo(coords.y + g1->y_offset + g1->height) };
ScreenCoordsXY spriteBottomLeft{ zoomLevel.ApplyInversedTo(coords.x + g1->xOffset + g1->width),
zoomLevel.ApplyInversedTo(coords.y + g1->yOffset + g1->height) };
const int32_t width = std::min(spriteBottomLeft.x, rt.x + rt.width) - std::max(spriteTopLeft.x, rt.x);
const int32_t height = std::min(spriteBottomLeft.y, rt.y + rt.height) - std::max(spriteTopLeft.y, rt.y);
@@ -830,7 +830,7 @@ void FASTCALL GfxDrawSpritePaletteSetSoftware(
int32_t height = g1->height;
// This is the start y coordinate on the destination
int16_t dest_start_y = y + g1->y_offset;
int16_t dest_start_y = y + g1->yOffset;
// For whatever reason the RLE version does not use
// the zoom mask on the y coordinate but does on x.
@@ -889,7 +889,7 @@ void FASTCALL GfxDrawSpritePaletteSetSoftware(
// This is the source start x coordinate
int32_t source_start_x = 0;
// This is the destination start x coordinate
int16_t dest_start_x = ((x + g1->x_offset + ~zoom_mask) & zoom_mask) - rt.WorldX();
int16_t dest_start_x = ((x + g1->xOffset + ~zoom_mask) & zoom_mask) - rt.WorldX();
if (dest_start_x < 0)
{
@@ -981,7 +981,7 @@ void FASTCALL GfxDrawSpriteRawMaskedSoftware(
width = zoom.ApplyInversedTo(std::min(imgMask->width, imgColour->width));
height = zoom.ApplyInversedTo(std::min(imgMask->height, imgColour->height));
ScreenCoordsXY offsetCoords = scrCoords + ScreenCoordsXY{ imgMask->x_offset, imgMask->y_offset };
ScreenCoordsXY offsetCoords = scrCoords + ScreenCoordsXY{ imgMask->xOffset, imgMask->yOffset };
offsetCoords.x = zoom.ApplyInversedTo(offsetCoords.x);
offsetCoords.y = zoom.ApplyInversedTo(offsetCoords.y);
@@ -1163,7 +1163,7 @@ size_t G1CalculateDataSize(const G1Element* g1)
{
if (g1->flags & G1_FLAG_PALETTE)
{
return g1->width * 3;
return g1->numColours * 3;
}
if (g1->flags & G1_FLAG_RLE_COMPRESSION)

View File

@@ -700,7 +700,7 @@ void GfxTransposePalette(int32_t pal, uint8_t product)
if (g1 != nullptr)
{
int32_t width = g1->width;
int32_t x = g1->x_offset;
int32_t x = g1->xOffset;
uint8_t* source_pointer = g1->offset;
for (; width > 0; width--)
@@ -743,7 +743,7 @@ void LoadPalette()
if (g1 != nullptr)
{
int32_t width = g1->width;
int32_t x = g1->x_offset;
int32_t x = g1->xOffset;
uint8_t* src = g1->offset;
for (; width > 0; width--)
{
@@ -835,8 +835,8 @@ void GfxInvalidatePickedUpPeep()
auto* g1 = GfxGetG1Element(imageId);
if (g1 != nullptr)
{
int32_t left = gPickupPeepX + g1->x_offset;
int32_t top = gPickupPeepY + g1->y_offset;
int32_t left = gPickupPeepX + g1->xOffset;
int32_t top = gPickupPeepY + g1->yOffset;
int32_t right = left + g1->width;
int32_t bottom = top + g1->height;
GfxSetDirtyBlocks({ { left, top }, { right, bottom } });
@@ -956,7 +956,7 @@ void UpdatePaletteEffects()
const G1Element* g1 = GfxGetG1Element(palette);
if (g1 != nullptr)
{
int32_t xoffset = g1->x_offset;
int32_t xoffset = g1->xOffset;
for (int32_t i = 0; i < g1->width; i++)
{
@@ -985,7 +985,7 @@ void UpdatePaletteEffects()
const G1Element* g1 = GfxGetG1Element(palette);
if (g1 != nullptr)
{
int32_t xoffset = g1->x_offset;
int32_t xoffset = g1->xOffset;
for (int32_t i = 0; i < g1->width; i++)
{

View File

@@ -48,12 +48,20 @@ namespace OpenRCT2::Drawing
struct G1Element
{
uint8_t* offset = nullptr; // 0x00
int16_t width = 0; // 0x04
int16_t height = 0; // 0x06
int16_t x_offset = 0; // 0x08
int16_t y_offset = 0; // 0x0A
union
{
int16_t width = 0; // 0x04
int16_t numColours; // If G1_FLAG_PALETTE is set
};
int16_t height = 0; // 0x06
union
{
int16_t xOffset = 0; // 0x08
int16_t startIndex; // If G1_FLAG_PALETTE is set
};
int16_t yOffset = 0; // 0x0A
uint16_t flags = 0; // 0x0C
int32_t zoomed_offset = 0; // 0x0E
int32_t zoomedOffset = 0; // 0x0E
};
#pragma pack(push, 1)

View File

@@ -274,7 +274,7 @@ void FontSpriteInitialiseCharacters()
int32_t width = 0;
if (g1 != nullptr)
{
width = g1->width + (2 * g1->x_offset) - 1;
width = g1->width + (2 * g1->xOffset) - 1;
}
_spriteFontCharacterWidths[EnumValue(fontStyle)][glyphIndex] = static_cast<uint8_t>(width);
}

View File

@@ -46,9 +46,9 @@ namespace OpenRCT2::Drawing
outElement.width = meta.srcSize.width;
outElement.height = meta.srcSize.height;
outElement.flags = isRLE ? G1_FLAG_RLE_COMPRESSION : G1_FLAG_HAS_TRANSPARENCY;
outElement.x_offset = meta.offset.x;
outElement.y_offset = meta.offset.y;
outElement.zoomed_offset = meta.zoomedOffset;
outElement.xOffset = meta.offset.x;
outElement.yOffset = meta.offset.y;
outElement.zoomedOffset = meta.zoomedOffset;
if (HasFlag(meta.importFlags, ImportFlags::NoDrawOnZoom))
outElement.flags |= G1_FLAG_NO_ZOOM_DRAW;

View File

@@ -89,8 +89,8 @@ namespace OpenRCT2::Drawing::ScrollingText
// Initialize the scrolling text sprite.
G1Element g1{};
g1.offset = _drawScrollTextList[i].bitmap;
g1.x_offset = -32;
g1.y_offset = 0;
g1.xOffset = -32;
g1.yOffset = 0;
g1.flags = G1_FLAG_HAS_TRANSPARENCY;
g1.width = 64;
g1.height = 40;

View File

@@ -1567,7 +1567,7 @@ namespace OpenRCT2
while (g1->flags & G1_FLAG_HAS_ZOOM_SPRITE && zoomLevel > ZoomLevel{ 0 })
{
imageId = imageId.WithIndex(imageId.GetIndex() - g1->zoomed_offset);
imageId = imageId.WithIndex(imageId.GetIndex() - g1->zoomedOffset);
g1 = GfxGetG1Element(imageId);
if (g1 == nullptr || g1->flags & G1_FLAG_NO_ZOOM_DRAW)
{
@@ -1581,8 +1581,8 @@ namespace OpenRCT2
}
}
origin.x += g1->x_offset;
origin.y += g1->y_offset;
origin.x += g1->xOffset;
origin.y += g1->yOffset;
interactionPoint -= origin;
if (interactionPoint.x < 0 || interactionPoint.y < 0 || interactionPoint.x >= g1->width

View File

@@ -63,10 +63,10 @@ namespace OpenRCT2
g1 = *orig;
g1.offset = new uint8_t[length];
std::memcpy(g1.offset, orig->offset, length);
if ((g1.flags & G1_FLAG_HAS_ZOOM_SPRITE) && g1.zoomed_offset != 0)
if ((g1.flags & G1_FLAG_HAS_ZOOM_SPRITE) && g1.zoomedOffset != 0)
{
// Fetch image for next zoom level
next_zoom = std::make_unique<RequiredImage>(static_cast<uint32_t>(idx - g1.zoomed_offset), getter);
next_zoom = std::make_unique<RequiredImage>(static_cast<uint32_t>(idx - g1.zoomedOffset), getter);
if (!next_zoom->HasData())
{
next_zoom = nullptr;
@@ -469,10 +469,10 @@ namespace OpenRCT2
g1Element.width = stream->ReadValue<int16_t>();
g1Element.height = stream->ReadValue<int16_t>();
g1Element.x_offset = stream->ReadValue<int16_t>();
g1Element.y_offset = stream->ReadValue<int16_t>();
g1Element.xOffset = stream->ReadValue<int16_t>();
g1Element.yOffset = stream->ReadValue<int16_t>();
g1Element.flags = stream->ReadValue<uint16_t>();
g1Element.zoomed_offset = stream->ReadValue<uint16_t>();
g1Element.zoomedOffset = stream->ReadValue<uint16_t>();
newEntries.push_back(std::move(g1Element));
}
@@ -568,9 +568,9 @@ namespace OpenRCT2
for (auto& image : images)
{
if (hasXOverride)
image->g1.x_offset = xOverride;
image->g1.xOffset = xOverride;
if (hasYOverride)
image->g1.y_offset = yOverride;
image->g1.yOffset = yOverride;
}
}
@@ -606,14 +606,14 @@ namespace OpenRCT2
// Set old image zoom offset to zoom image which we are about to add
auto g1a = const_cast<G1Element*>(&GetImages()[tableIndex]);
g1a->zoomed_offset = static_cast<int32_t>(tableIndex) - static_cast<int32_t>(GetCount());
g1a->zoomedOffset = static_cast<int32_t>(tableIndex) - static_cast<int32_t>(GetCount());
while (img != nullptr)
{
auto g1b = img->g1;
if (img->next_zoom != nullptr)
{
g1b.zoomed_offset = -1;
g1b.zoomedOffset = -1;
}
AddImage(&g1b);
img = img->next_zoom.get();

View File

@@ -123,8 +123,8 @@ namespace OpenRCT2
G1Element g1 = {};
g1.offset = data.get();
g1.width = static_cast<int16_t>(numColours);
g1.x_offset = Json::GetNumber<int16_t>(jPalette["index"]);
g1.numColours = static_cast<int16_t>(numColours);
g1.startIndex = Json::GetNumber<int16_t>(jPalette["index"]);
g1.flags = G1_FLAG_PALETTE;
auto& imageTable = GetImageTable();

View File

@@ -107,8 +107,8 @@ static void PaintSessionAddPSToQuadrant(PaintSession& session, PaintStruct* ps)
static constexpr bool imageWithinDPI(const ScreenCoordsXY& imagePos, const G1Element& g1, const RenderTarget& rt)
{
const int32_t left = imagePos.x + g1.x_offset;
const int32_t bottom = imagePos.y + g1.y_offset;
const int32_t left = imagePos.x + g1.xOffset;
const int32_t bottom = imagePos.y + g1.yOffset;
const int32_t right = left + g1.width;
const int32_t top = bottom + g1.height;

View File

@@ -48,9 +48,9 @@ TEST_F(ImageImporterTests, Import_Logo)
ASSERT_EQ(result.Buffer.data(), result.Element.offset);
ASSERT_EQ(128, result.Element.width);
ASSERT_EQ(128, result.Element.height);
ASSERT_EQ(3, result.Element.x_offset);
ASSERT_EQ(5, result.Element.y_offset);
ASSERT_EQ(0, result.Element.zoomed_offset);
ASSERT_EQ(3, result.Element.xOffset);
ASSERT_EQ(5, result.Element.yOffset);
ASSERT_EQ(0, result.Element.zoomedOffset);
// Check to ensure RLE data doesn't change unexpectedly.
// Update expected hash if change is expected.