From a241a4ce97ffe3f519ecf656ad10c518d646d423 Mon Sep 17 00:00:00 2001 From: frosch Date: Sun, 29 May 2011 16:56:22 +0000 Subject: (svn r22518) -Feature: [NewGRF] Advanced sprite layouts with register modifiers. --- src/newgrf.cpp | 190 +++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 166 insertions(+), 24 deletions(-) (limited to 'src/newgrf.cpp') diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 605b17023..a2ff7a5a1 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -482,16 +482,19 @@ static void MapSpriteMappingRecolour(PalSpriteID *grf_sprite) * Read a sprite and a palette from the GRF and convert them into a format * suitable to OpenTTD. * @param buf Input stream. + * @param read_flags Whether to read TileLayoutFlags. * @param invert_action1_flag Set to true, if palette bit 15 means 'not from action 1'. * @param action1_offset Offset to add to action 1 sprites. * @param action1_pitch Factor to multiply action 1 sprite indices with. * @param action1_max Maximal valid action 1 index. * @param [out] grf_sprite Read sprite and palette. + * @return read TileLayoutFlags. */ -static void ReadSpriteLayoutSprite(ByteReader *buf, bool invert_action1_flag, uint action1_offset, uint action1_pitch, uint action1_max, PalSpriteID *grf_sprite) +static TileLayoutFlags ReadSpriteLayoutSprite(ByteReader *buf, bool read_flags, bool invert_action1_flag, uint action1_offset, uint action1_pitch, uint action1_max, PalSpriteID *grf_sprite) { grf_sprite->sprite = buf->ReadWord(); grf_sprite->pal = buf->ReadWord(); + TileLayoutFlags flags = read_flags ? (TileLayoutFlags)buf->ReadWord() : TLF_NOTHING; MapSpriteMappingRecolour(grf_sprite); @@ -509,7 +512,143 @@ static void ReadSpriteLayoutSprite(ByteReader *buf, bool invert_action1_flag, ui SB(grf_sprite->sprite, 0, SPRITE_WIDTH, sprite); SetBit(grf_sprite->sprite, SPRITE_MODIFIER_CUSTOM_SPRITE); } + } else if ((flags & TLF_SPRITE_VAR10) && !(flags & TLF_SPRITE_REG_FLAGS)) { + grfmsg(1, "ReadSpriteLayoutSprite: Spritelayout specifies var10 value for non-action-1 sprite"); + DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT); + return flags; } + + if (flags & TLF_CUSTOM_PALETTE) { + /* Use palette from Action 1 */ + uint index = GB(grf_sprite->pal, 0, 14); + if (action1_pitch == 0 || index >= action1_max) { + grfmsg(1, "ReadSpriteLayoutSprite: Spritelayout uses undefined custom spriteset %d for 'palette'", index); + grf_sprite->pal = PAL_NONE; + } else { + SpriteID sprite = action1_offset + index * action1_pitch; + SB(grf_sprite->pal, 0, SPRITE_WIDTH, sprite); + SetBit(grf_sprite->pal, SPRITE_MODIFIER_CUSTOM_SPRITE); + } + } else if ((flags & TLF_PALETTE_VAR10) && !(flags & TLF_PALETTE_REG_FLAGS)) { + grfmsg(1, "ReadSpriteLayoutRegisters: Spritelayout specifies var10 value for non-action-1 palette"); + DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT); + return flags; + } + + return flags; +} + +/** + * Preprocess the TileLayoutFlags and read register modifiers from the GRF. + * @param buf Input stream. + * @param flags TileLayoutFlags to process. + * @param is_parent Whether the sprite is a parentsprite with a bounding box. + * @param dts Sprite layout to insert data into. + * @param index Sprite index to process; 0 for ground sprite. + */ +static void ReadSpriteLayoutRegisters(ByteReader *buf, TileLayoutFlags flags, bool is_parent, NewGRFSpriteLayout *dts, uint index) +{ + if (!(flags & TLF_DRAWING_FLAGS)) return; + + if (dts->registers == NULL) dts->AllocateRegisters(); + TileLayoutRegisters ®s = const_cast(dts->registers[index]); + regs.flags = flags & TLF_DRAWING_FLAGS; + + if (flags & TLF_DODRAW) regs.dodraw = buf->ReadByte(); + if (flags & TLF_SPRITE) regs.sprite = buf->ReadByte(); + if (flags & TLF_PALETTE) regs.palette = buf->ReadByte(); + + if (is_parent) { + if (flags & TLF_BB_XY_OFFSET) { + regs.delta.parent[0] = buf->ReadByte(); + regs.delta.parent[1] = buf->ReadByte(); + } + if (flags & TLF_BB_Z_OFFSET) regs.delta.parent[2] = buf->ReadByte(); + } else { + if (flags & TLF_CHILD_X_OFFSET) regs.delta.child[0] = buf->ReadByte(); + if (flags & TLF_CHILD_Y_OFFSET) regs.delta.child[1] = buf->ReadByte(); + } + + if (flags & TLF_SPRITE_VAR10) { + regs.sprite_var10 = buf->ReadByte(); + if (regs.sprite_var10 > TLR_MAX_VAR10) { + grfmsg(1, "ReadSpriteLayoutRegisters: Spritelayout specifies var10 (%d) exceeding the maximal allowed value %d", regs.sprite_var10, TLR_MAX_VAR10); + DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT); + return; + } + } + + if (flags & TLF_PALETTE_VAR10) { + regs.palette_var10 = buf->ReadByte(); + if (regs.palette_var10 > TLR_MAX_VAR10) { + grfmsg(1, "ReadSpriteLayoutRegisters: Spritelayout specifies var10 (%d) exceeding the maximal allowed value %d", regs.palette_var10, TLR_MAX_VAR10); + DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT); + return; + } + } +} + +/** + * Read a spritelayout from the GRF. + * @param buf Input + * @param num_building_sprites Number of building sprites to read + * @param action1_offset Offset to add to action 1 sprites + * @param action1_pitch Factor to multiply action 1 sprite indices with + * @param action1_max Maximal valid action 1 index + * @param allow_var10 Whether the spritelayout may specifiy var10 values for resolving multiple action-1-2-3 chains + * @param no_z_position Whether bounding boxes have no Z offset + * @param dts Layout container to output into + * @return true on error (GRF was disabled) + */ +static bool ReadSpriteLayout(ByteReader *buf, uint num_building_sprites, uint action1_offset, uint action1_pitch, uint action1_max, bool allow_var10, bool no_z_position, NewGRFSpriteLayout *dts) +{ + bool has_flags = HasBit(num_building_sprites, 6); + ClrBit(num_building_sprites, 6); + TileLayoutFlags valid_flags = TLF_KNOWN_FLAGS; + if (!allow_var10) valid_flags &= ~TLF_VAR10_FLAGS; + dts->Allocate(num_building_sprites); // allocate before reading groundsprite flags + + /* Groundsprite */ + TileLayoutFlags flags = ReadSpriteLayoutSprite(buf, has_flags, false, action1_offset, action1_pitch, action1_max, &dts->ground); + if (_skip_sprites < 0) return true; + + if (flags & ~(valid_flags & ~TLF_NON_GROUND_FLAGS)) { + grfmsg(1, "ReadSpriteLayout: Spritelayout uses invalid flag 0x%x for ground sprite", flags & ~(valid_flags & ~TLF_NON_GROUND_FLAGS)); + DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT); + return true; + } + + ReadSpriteLayoutRegisters(buf, flags, false, dts, 0); + if (_skip_sprites < 0) return true; + + for (uint i = 0; i < num_building_sprites; i++) { + DrawTileSeqStruct *seq = const_cast(&dts->seq[i]); + + flags = ReadSpriteLayoutSprite(buf, has_flags, false, action1_offset, action1_pitch, action1_max, &seq->image); + if (_skip_sprites < 0) return true; + + if (flags & ~valid_flags) { + grfmsg(1, "ReadSpriteLayout: Spritelayout uses unknown flag 0x%x", flags & ~valid_flags); + DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT); + return true; + } + + seq->delta_x = buf->ReadByte(); + seq->delta_y = buf->ReadByte(); + + if (!no_z_position) seq->delta_z = buf->ReadByte(); + + if (seq->IsParentSprite()) { + seq->size_x = buf->ReadByte(); + seq->size_y = buf->ReadByte(); + seq->size_z = buf->ReadByte(); + } + + ReadSpriteLayoutRegisters(buf, flags, seq->IsParentSprite(), dts, i + 1); + if (_skip_sprites < 0) return true; + } + + return false; } /** @@ -540,6 +679,7 @@ static void ConvertTTDBasePrice(uint32 base_pointer, const char *error_location, enum ChangeInfoResult { CIR_SUCCESS, ///< Variable was parsed and read + CIR_DISABLED, ///< GRF was disabled due to error CIR_UNHANDLED, ///< Variable was parsed but unread CIR_UNKNOWN, ///< Variable is unknown CIR_INVALID_ID, ///< Attempt to modify an invalid ID @@ -1269,7 +1409,9 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte continue; } - ReadSpriteLayoutSprite(buf, false, 0, 1, UINT_MAX, &dts->ground); + ReadSpriteLayoutSprite(buf, false, false, 0, 1, UINT_MAX, &dts->ground); + /* On error, bail out immediately. Temporary GRF data was already freed */ + if (_skip_sprites < 0) return CIR_DISABLED; static SmallVector tmp_layout; tmp_layout.Clear(); @@ -1286,7 +1428,9 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte dtss->size_y = buf->ReadByte(); dtss->size_z = buf->ReadByte(); - ReadSpriteLayoutSprite(buf, true, 0, 1, UINT_MAX, &dtss->image); + ReadSpriteLayoutSprite(buf, false, true, 0, 1, UINT_MAX, &dtss->image); + /* On error, bail out immediately. Temporary GRF data was already freed */ + if (_skip_sprites < 0) return CIR_DISABLED; } dts->Clone(tmp_layout.Begin()); } @@ -1428,6 +1572,19 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte statspec->animation.triggers = buf->ReadWord(); break; + case 0x20: // Advanced sprite layout + statspec->tiles = buf->ReadExtendedByte(); + delete[] statspec->renderdata; // delete earlier loaded stuff + statspec->renderdata = new NewGRFSpriteLayout[statspec->tiles]; + + for (uint t = 0; t < statspec->tiles; t++) { + NewGRFSpriteLayout *dts = &statspec->renderdata[t]; + uint num_building_sprites = buf->ReadByte(); + /* On error, bail out immediately. Temporary GRF data was already freed */ + if (ReadSpriteLayout(buf, num_building_sprites, 0, 1, UINT_MAX, true, false, dts)) return CIR_DISABLED; + } + break; + default: ret = CIR_UNKNOWN; break; @@ -3524,6 +3681,10 @@ static bool HandleChangeInfoResult(const char *caller, ChangeInfoResult cir, uin switch (cir) { default: NOT_REACHED(); + case CIR_DISABLED: + /* Error has already been printed; just stop parsing */ + return true; + case CIR_SUCCESS: return false; @@ -3961,7 +4122,6 @@ static void NewSpriteGroup(ByteReader *buf) byte num_spriteset_ents = _cur_grffile->spriteset_numents; byte num_spritesets = _cur_grffile->spriteset_numsets; byte num_building_sprites = max((uint8)1, type); - uint i; assert(TileLayoutSpriteGroup::CanAllocateItem()); TileLayoutSpriteGroup *group = new TileLayoutSpriteGroup(); @@ -3969,26 +4129,8 @@ static void NewSpriteGroup(ByteReader *buf) /* num_building_stages should be 1, if we are only using non-custom sprites */ group->num_building_stages = max((uint8)1, num_spriteset_ents); - /* Groundsprite */ - ReadSpriteLayoutSprite(buf, false, _cur_grffile->spriteset_start, num_spriteset_ents, num_spritesets, &group->dts.ground); - - group->dts.Allocate(num_building_sprites); - for (i = 0; i < num_building_sprites; i++) { - DrawTileSeqStruct *seq = const_cast(&group->dts.seq[i]); - - ReadSpriteLayoutSprite(buf, false, _cur_grffile->spriteset_start, num_spriteset_ents, num_spritesets, &seq->image); - seq->delta_x = buf->ReadByte(); - seq->delta_y = buf->ReadByte(); - - if (type > 0) { - seq->delta_z = buf->ReadByte(); - if (!seq->IsParentSprite()) continue; - } - - seq->size_x = buf->ReadByte(); - seq->size_y = buf->ReadByte(); - seq->size_z = buf->ReadByte(); - } + /* On error, bail out immediately. Temporary GRF data was already freed */ + if (ReadSpriteLayout(buf, num_building_sprites, _cur_grffile->spriteset_start, num_spriteset_ents, num_spritesets, false, type == 0, &group->dts)) return; break; } -- cgit v1.2.3-70-g09d2