/* * This file is part of OpenTTD. * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . */ /** @file newgrf_act7_9.cpp NewGRF Action 0x07 and Action 0x09 handler. */ #include "../stdafx.h" #include "../debug.h" #include "../genworld.h" #include "../network/network.h" #include "../newgrf_engine.h" #include "../newgrf_cargo.h" #include "../rail.h" #include "../road.h" #include "../settings_type.h" #include "newgrf_bytereader.h" #include "newgrf_internal.h" #include "../safeguards.h" /** 32 * 8 = 256 flags. Apparently TTDPatch uses this many.. */ static std::array _ttdpatch_flags; /** Initialize the TTDPatch flags */ void InitializePatchFlags() { _ttdpatch_flags[0] = ((_settings_game.station.never_expire_airports ? 1U : 0U) << 0x0C) // keepsmallairport | (1U << 0x0D) // newairports | (1U << 0x0E) // largestations | ((_settings_game.construction.max_bridge_length > 16 ? 1U : 0U) << 0x0F) // longbridges | (0U << 0x10) // loadtime | (1U << 0x12) // presignals | (1U << 0x13) // extpresignals | ((_settings_game.vehicle.never_expire_vehicles ? 1U : 0U) << 0x16) // enginespersist | (1U << 0x1B) // multihead | (1U << 0x1D) // lowmemory | (1U << 0x1E); // generalfixes _ttdpatch_flags[1] = ((_settings_game.economy.station_noise_level ? 1U : 0U) << 0x07) // moreairports - based on units of noise | (1U << 0x08) // mammothtrains | (1U << 0x09) // trainrefit | (0U << 0x0B) // subsidiaries | ((_settings_game.order.gradual_loading ? 1U : 0U) << 0x0C) // gradualloading | (1U << 0x12) // unifiedmaglevmode - set bit 0 mode. Not revelant to OTTD | (1U << 0x13) // unifiedmaglevmode - set bit 1 mode | (1U << 0x14) // bridgespeedlimits | (1U << 0x16) // eternalgame | (1U << 0x17) // newtrains | (1U << 0x18) // newrvs | (1U << 0x19) // newships | (1U << 0x1A) // newplanes | ((_settings_game.construction.train_signal_side == 1 ? 1U : 0U) << 0x1B) // signalsontrafficside | ((_settings_game.vehicle.disable_elrails ? 0U : 1U) << 0x1C); // electrifiedrailway _ttdpatch_flags[2] = (1U << 0x01) // loadallgraphics - obsolote | (1U << 0x03) // semaphores | (1U << 0x0A) // newobjects | (0U << 0x0B) // enhancedgui | (0U << 0x0C) // newagerating | ((_settings_game.construction.build_on_slopes ? 1U : 0U) << 0x0D) // buildonslopes | (1U << 0x0E) // fullloadany | (1U << 0x0F) // planespeed | (0U << 0x10) // moreindustriesperclimate - obsolete | (0U << 0x11) // moretoylandfeatures | (1U << 0x12) // newstations | (1U << 0x13) // tracktypecostdiff | (1U << 0x14) // manualconvert | ((_settings_game.construction.build_on_slopes ? 1U : 0U) << 0x15) // buildoncoasts | (1U << 0x16) // canals | (1U << 0x17) // newstartyear | ((_settings_game.vehicle.freight_trains > 1 ? 1U : 0U) << 0x18) // freighttrains | (1U << 0x19) // newhouses | (1U << 0x1A) // newbridges | (1U << 0x1B) // newtownnames | (1U << 0x1C) // moreanimation | ((_settings_game.vehicle.wagon_speed_limits ? 1U : 0U) << 0x1D) // wagonspeedlimits | (1U << 0x1E) // newshistory | (0U << 0x1F); // custombridgeheads _ttdpatch_flags[3] = (0U << 0x00) // newcargodistribution | (1U << 0x01) // windowsnap | ((_settings_game.economy.allow_town_roads || _generating_world ? 0U : 1U) << 0x02) // townbuildnoroad | (1U << 0x03) // pathbasedsignalling | (0U << 0x04) // aichoosechance | (1U << 0x05) // resolutionwidth | (1U << 0x06) // resolutionheight | (1U << 0x07) // newindustries | ((_settings_game.order.improved_load ? 1U : 0U) << 0x08) // fifoloading | (0U << 0x09) // townroadbranchprob | (0U << 0x0A) // tempsnowline | (1U << 0x0B) // newcargo | (1U << 0x0C) // enhancemultiplayer | (1U << 0x0D) // onewayroads | (1U << 0x0E) // irregularstations | (1U << 0x0F) // statistics | (1U << 0x10) // newsounds | (1U << 0x11) // autoreplace | (1U << 0x12) // autoslope | (0U << 0x13) // followvehicle | (1U << 0x14) // trams | (0U << 0x15) // enhancetunnels | (1U << 0x16) // shortrvs | (1U << 0x17) // articulatedrvs | ((_settings_game.vehicle.dynamic_engines ? 1U : 0U) << 0x18) // dynamic engines | (1U << 0x1E) // variablerunningcosts | (1U << 0x1F); // any switch is on _ttdpatch_flags[4] = (1U << 0x00) // larger persistent storage | ((_settings_game.economy.inflation ? 1U : 0U) << 0x01) // inflation is on | (1U << 0x02); // extended string range } uint32_t GetParamVal(uint8_t param, uint32_t *cond_val) { /* First handle variable common with VarAction2 */ uint32_t value; if (GetGlobalVariable(param - 0x80, &value, _cur.grffile)) return value; /* Non-common variable */ switch (param) { case 0x84: { // GRF loading stage uint32_t res = 0; if (_cur.stage > GLS_INIT) SetBit(res, 0); if (_cur.stage == GLS_RESERVE) SetBit(res, 8); if (_cur.stage == GLS_ACTIVATION) SetBit(res, 9); return res; } case 0x85: // TTDPatch flags, only for bit tests if (cond_val == nullptr) { /* Supported in Action 0x07 and 0x09, not 0x0D */ return 0; } else { uint32_t index = *cond_val / 0x20; uint32_t param_val = index < std::size(_ttdpatch_flags) ? _ttdpatch_flags[index] : 0; *cond_val %= 0x20; return param_val; } case 0x88: // GRF ID check return 0; /* case 0x99: Global ID offset not implemented */ default: /* GRF Parameter */ if (param < 0x80) return _cur.grffile->GetParam(param); /* In-game variable. */ GrfMsg(1, "Unsupported in-game variable 0x{:02X}", param); return UINT_MAX; } } /* Action 0x07 * Action 0x09 */ static void SkipIf(ByteReader &buf) { /* <07/09> * * B param-num * B param-size * B condition-type * V value * B num-sprites */ uint32_t cond_val = 0; uint32_t mask = 0; bool result; uint8_t param = buf.ReadByte(); uint8_t paramsize = buf.ReadByte(); uint8_t condtype = buf.ReadByte(); if (condtype < 2) { /* Always 1 for bit tests, the given value should be ignored. */ paramsize = 1; } switch (paramsize) { case 8: cond_val = buf.ReadDWord(); mask = buf.ReadDWord(); break; case 4: cond_val = buf.ReadDWord(); mask = 0xFFFFFFFF; break; case 2: cond_val = buf.ReadWord(); mask = 0x0000FFFF; break; case 1: cond_val = buf.ReadByte(); mask = 0x000000FF; break; default: break; } if (param < 0x80 && std::size(_cur.grffile->param) <= param) { GrfMsg(7, "SkipIf: Param {} undefined, skipping test", param); return; } GrfMsg(7, "SkipIf: Test condtype {}, param 0x{:02X}, condval 0x{:08X}", condtype, param, cond_val); /* condtypes that do not use 'param' are always valid. * condtypes that use 'param' are either not valid for param 0x88, or they are only valid for param 0x88. */ if (condtype >= 0x0B) { /* Tests that ignore 'param' */ switch (condtype) { case 0x0B: result = !IsValidCargoType(GetCargoTypeByLabel(CargoLabel(std::byteswap(cond_val)))); break; case 0x0C: result = IsValidCargoType(GetCargoTypeByLabel(CargoLabel(std::byteswap(cond_val)))); break; case 0x0D: result = GetRailTypeByLabel(std::byteswap(cond_val)) == INVALID_RAILTYPE; break; case 0x0E: result = GetRailTypeByLabel(std::byteswap(cond_val)) != INVALID_RAILTYPE; break; case 0x0F: { RoadType rt = GetRoadTypeByLabel(std::byteswap(cond_val)); result = rt == INVALID_ROADTYPE || !RoadTypeIsRoad(rt); break; } case 0x10: { RoadType rt = GetRoadTypeByLabel(std::byteswap(cond_val)); result = rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt); break; } case 0x11: { RoadType rt = GetRoadTypeByLabel(std::byteswap(cond_val)); result = rt == INVALID_ROADTYPE || !RoadTypeIsTram(rt); break; } case 0x12: { RoadType rt = GetRoadTypeByLabel(std::byteswap(cond_val)); result = rt != INVALID_ROADTYPE && RoadTypeIsTram(rt); break; } default: GrfMsg(1, "SkipIf: Unsupported condition type {:02X}. Ignoring", condtype); return; } } else if (param == 0x88) { /* GRF ID checks */ GRFConfig *c = GetGRFConfig(cond_val, mask); if (c != nullptr && c->flags.Test(GRFConfigFlag::Static) && !_cur.grfconfig->flags.Test(GRFConfigFlag::Static) && _networking) { DisableStaticNewGRFInfluencingNonStaticNewGRFs(*c); c = nullptr; } if (condtype != 10 && c == nullptr) { GrfMsg(7, "SkipIf: GRFID 0x{:08X} unknown, skipping test", std::byteswap(cond_val)); return; } switch (condtype) { /* Tests 0x06 to 0x0A are only for param 0x88, GRFID checks */ case 0x06: // Is GRFID active? result = c->status == GCS_ACTIVATED; break; case 0x07: // Is GRFID non-active? result = c->status != GCS_ACTIVATED; break; case 0x08: // GRFID is not but will be active? result = c->status == GCS_INITIALISED; break; case 0x09: // GRFID is or will be active? result = c->status == GCS_ACTIVATED || c->status == GCS_INITIALISED; break; case 0x0A: // GRFID is not nor will be active /* This is the only condtype that doesn't get ignored if the GRFID is not found */ result = c == nullptr || c->status == GCS_DISABLED || c->status == GCS_NOT_FOUND; break; default: GrfMsg(1, "SkipIf: Unsupported GRF condition type {:02X}. Ignoring", condtype); return; } } else { /* Tests that use 'param' and are not GRF ID checks. */ uint32_t param_val = GetParamVal(param, &cond_val); // cond_val is modified for param == 0x85 switch (condtype) { case 0x00: result = !!(param_val & (1 << cond_val)); break; case 0x01: result = !(param_val & (1 << cond_val)); break; case 0x02: result = (param_val & mask) == cond_val; break; case 0x03: result = (param_val & mask) != cond_val; break; case 0x04: result = (param_val & mask) < cond_val; break; case 0x05: result = (param_val & mask) > cond_val; break; default: GrfMsg(1, "SkipIf: Unsupported condition type {:02X}. Ignoring", condtype); return; } } if (!result) { GrfMsg(2, "SkipIf: Not skipping sprites, test was false"); return; } uint8_t numsprites = buf.ReadByte(); /* numsprites can be a GOTO label if it has been defined in the GRF * file. The jump will always be the first matching label that follows * the current nfo_line. If no matching label is found, the first matching * label in the file is used. */ const GRFLabel *choice = nullptr; for (const auto &label : _cur.grffile->labels) { if (label.label != numsprites) continue; /* Remember a goto before the current line */ if (choice == nullptr) choice = &label; /* If we find a label here, this is definitely good */ if (label.nfo_line > _cur.nfo_line) { choice = &label; break; } } if (choice != nullptr) { GrfMsg(2, "SkipIf: Jumping to label 0x{:X} at line {}, test was true", choice->label, choice->nfo_line); _cur.file->SeekTo(choice->pos, SEEK_SET); _cur.nfo_line = choice->nfo_line; return; } GrfMsg(2, "SkipIf: Skipping {} sprites, test was true", numsprites); _cur.skip_sprites = numsprites; if (_cur.skip_sprites == 0) { /* Zero means there are no sprites to skip, so * we use -1 to indicate that all further * sprites should be skipped. */ _cur.skip_sprites = -1; /* If an action 8 hasn't been encountered yet, disable the grf. */ if (_cur.grfconfig->status != (_cur.stage < GLS_RESERVE ? GCS_INITIALISED : GCS_ACTIVATED)) { DisableGrf(); } } } template <> void GrfActionHandler<0x07>::FileScan(ByteReader &) { } template <> void GrfActionHandler<0x07>::SafetyScan(ByteReader &) { } template <> void GrfActionHandler<0x07>::LabelScan(ByteReader &) { } template <> void GrfActionHandler<0x07>::Init(ByteReader &) { } template <> void GrfActionHandler<0x07>::Reserve(ByteReader &buf) { SkipIf(buf); } template <> void GrfActionHandler<0x07>::Activation(ByteReader &buf) { SkipIf(buf); } template <> void GrfActionHandler<0x09>::FileScan(ByteReader &) { } template <> void GrfActionHandler<0x09>::SafetyScan(ByteReader &) { } template <> void GrfActionHandler<0x09>::LabelScan(ByteReader &) { } template <> void GrfActionHandler<0x09>::Init(ByteReader &buf) { SkipIf(buf); } template <> void GrfActionHandler<0x09>::Reserve(ByteReader &buf) { SkipIf(buf); } template <> void GrfActionHandler<0x09>::Activation(ByteReader &buf) { SkipIf(buf); }