From 96ed0f037e1a1980aac8a988b588770e5f14fda8 Mon Sep 17 00:00:00 2001 From: simu_poppo Date: Wed, 8 Apr 2026 17:30:48 +0900 Subject: [PATCH 1/4] read extended pakset --- Makefile | 1 + descriptor/image_array_3d.h | 36 +++ descriptor/objversion.h | 9 +- descriptor/reader/bridge_reader.cc | 64 ++++- descriptor/reader/building_reader.cc | 19 +- descriptor/reader/citycar_reader.cc | 10 +- descriptor/reader/crossing_reader.cc | 10 +- descriptor/reader/factory_reader.cc | 28 +- descriptor/reader/good_reader.cc | 47 +++- descriptor/reader/groundobj_reader.cc | 10 +- descriptor/reader/imagelist3d_reader.cc | 28 ++ descriptor/reader/imagelist3d_reader.h | 23 ++ descriptor/reader/pedestrian_reader.cc | 10 +- descriptor/reader/roadsign_reader.cc | 44 +++- descriptor/reader/sim_reader.cc | 2 + descriptor/reader/sound_reader.cc | 10 +- descriptor/reader/tree_reader.cc | 10 +- descriptor/reader/tunnel_reader.cc | 75 +++++- descriptor/reader/vehicle_reader.cc | 332 ++++++++++++++++++------ descriptor/reader/way_obj_reader.cc | 10 +- descriptor/reader/way_reader.cc | 57 +++- 21 files changed, 731 insertions(+), 104 deletions(-) create mode 100644 descriptor/image_array_3d.h create mode 100644 descriptor/reader/imagelist3d_reader.cc create mode 100644 descriptor/reader/imagelist3d_reader.h diff --git a/Makefile b/Makefile index 6bc83c1109c..a9c24f78196 100644 --- a/Makefile +++ b/Makefile @@ -339,6 +339,7 @@ SOURCES += descriptor/reader/ground_reader.cc SOURCES += descriptor/reader/groundobj_reader.cc SOURCES += descriptor/reader/image_reader.cc SOURCES += descriptor/reader/imagelist2d_reader.cc +SOURCES += descriptor/reader/imagelist3d_reader.cc SOURCES += descriptor/reader/imagelist_reader.cc SOURCES += descriptor/reader/obj_reader.cc SOURCES += descriptor/reader/pedestrian_reader.cc diff --git a/descriptor/image_array_3d.h b/descriptor/image_array_3d.h new file mode 100644 index 00000000000..17bc69f84a8 --- /dev/null +++ b/descriptor/image_array_3d.h @@ -0,0 +1,36 @@ +/* + * This file is part of the Simutrans project under the Artistic License. + * (see LICENSE.txt) + */ + +#ifndef DESCRIPTOR_IMAGE_ARRAY_3D_H +#define DESCRIPTOR_IMAGE_ARRAY_3D_H + + +#include "image_list.h" +#include "image_array.h" + +/** + * Three-dimensional array of images (ported from Simutrans-Extended) + * + * Child nodes: + * 0 1st image_array_t (2D) + * 1 2nd image_array_t (2D) + * ... ... + */ +class image_array_3d_t : public obj_desc_t { + friend class imagelist3d_reader_t; + + uint16 count; + +public: + image_array_3d_t() : count(0) {} + + uint16 get_count() const { return count; } + + image_array_t const* get_list_2d(uint16 i) const { return i < count ? get_child(i) : 0; } + image_list_t const* get_list(uint16 i, uint16 j) const { return i < count ? get_child(i)->get_list(j) : 0; } + image_t const* get_image(uint16 i, uint16 j, uint16 k) const { return i < count ? get_child(i)->get_image(j, k) : 0; } +}; + +#endif diff --git a/descriptor/objversion.h b/descriptor/objversion.h index ad8639fbe16..949a074a2d2 100644 --- a/descriptor/objversion.h +++ b/descriptor/objversion.h @@ -13,6 +13,9 @@ #define COMPILER_VERSION_CODE_11 (0 * 1000000 + 1 * 1000 + 1) #define COMPILER_VERSION_CODE (0 * 1000000 + 1 * 1000 + 3) +// Extended pak flag: bit 14 of the version field marks Extended-compiled pak objects +#define EX_VER 0x4000 + /* * obj_type value are stored inside the pak-files. Values are choosen to make * them somewhat readable (up to 4 uppercase letters describing the type). @@ -59,7 +62,11 @@ enum obj_type obj_vehicle = C4ID('V','H','C','L'), obj_way = C4ID('W','A','Y', 0 ), obj_way_obj = C4ID('W','Y','O','B'), - obj_xref = C4ID('X','R','E','F') + obj_xref = C4ID('X','R','E','F'), + // Extended-only object types (for pak compatibility) + obj_fupgrade = C4ID('F','U','P','G'), + obj_imagelist3d = C4ID('I','M','G','3'), + obj_pier = C4ID('P','I','E','R') }; #endif diff --git a/descriptor/reader/bridge_reader.cc b/descriptor/reader/bridge_reader.cc index e723ee81b94..d74f0645816 100644 --- a/descriptor/reader/bridge_reader.cc +++ b/descriptor/reader/bridge_reader.cc @@ -3,6 +3,7 @@ * (see LICENSE.txt) */ +#include "../objversion.h" #include #include "../../simdebug.h" @@ -39,7 +40,17 @@ obj_desc_t *bridge_reader_t::read_node(FILE *fp, obj_node_info_t &node) // old versions of PAK files have no version stamp. // But we know, the higher most bit was always cleared. const uint16 v = decode_uint16(p); - const int version = v & 0x8000 ? v & 0x7FFF : 0; + int version = v & 0x8000 ? v & 0x7FFF : 0; + const bool extended = version > 0 ? (v & EX_VER) != 0 : false; + uint16 extended_version = 0; + if (extended) { + version = version & EX_VER ? version & 0x3FFF : 0; + while (version > 0x100) { + version -= 0x100; + extended_version++; + } + extended_version--; + } // some defaults bridge_desc_t *desc = new bridge_desc_t(); @@ -138,10 +149,31 @@ obj_desc_t *bridge_reader_t::read_node(FILE *fp, obj_node_info_t &node) desc->pillars_asymmetric = (decode_uint8(p)!=0); desc->max_height = decode_uint8(p); desc->number_of_seasons = decode_uint8(p); + if (extended) { + // skip Extended-specific: axle_load(4), way_constraints(2), optional gradient fields + desc->axle_load = decode_uint32(p); + decode_uint8(p); // permissive + decode_uint8(p); // prohibitive + if (extended_version >= 1) { + decode_uint16(p); // topspeed_gradient_1 + decode_uint16(p); // topspeed_gradient_2 + decode_sint8(p); // max_altitude + decode_uint8(p); // max_vehicles_on_tile + decode_uint8(p); // has_own_way_graphics + decode_uint8(p); // has_way + } + if (extended_version > 1) { + dbg->fatal("bridge_reader_t::read_node()", "Incompatible Extended pak version %i", extended_version); + } + } } else if (version==9) { + // Extended v9 has different field order than OTRP v9: + // Extended: ..., pillars_asymmetric, max_height, axle_load(u16), [extended fields], number_of_seasons + // OTRP: ..., pillars_asymmetric, axle_load(u16), max_height, number_of_seasons + desc->topspeed = decode_uint16(p); desc->price = decode_uint32(p); desc->maintenance = decode_uint32(p); @@ -151,9 +183,33 @@ obj_desc_t *bridge_reader_t::read_node(FILE *fp, obj_node_info_t &node) desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->pillars_asymmetric = (decode_uint8(p)!=0); - desc->axle_load = decode_uint16(p); // new - desc->max_height = decode_uint8(p); - desc->number_of_seasons = decode_uint8(p); + if (extended) { + // Extended reads max_height before axle_load + desc->max_height = decode_uint8(p); + desc->axle_load = decode_uint16(p); + // skip Extended-specific fields: max_weight(4), way_constraints(2), optionals + decode_uint32(p); // max_weight (Extended distinguishes from axle_load) + decode_uint8(p); // permissive + decode_uint8(p); // prohibitive + if (extended_version >= 1) { + decode_uint16(p); // topspeed_gradient_1 + decode_uint16(p); // topspeed_gradient_2 + decode_sint8(p); // max_altitude + decode_uint8(p); // max_vehicles_on_tile + decode_uint8(p); // has_own_way_graphics + decode_uint8(p); // has_way + } + if (extended_version > 1) { + dbg->fatal("bridge_reader_t::read_node()", "Incompatible Extended pak version %i", extended_version); + } + desc->number_of_seasons = decode_uint8(p); + } + else { + // OTRP reads axle_load before max_height + desc->axle_load = decode_uint16(p); + desc->max_height = decode_uint8(p); + desc->number_of_seasons = decode_uint8(p); + } } else if (version==10) { diff --git a/descriptor/reader/building_reader.cc b/descriptor/reader/building_reader.cc index 22c8eec341d..b7d72b4ff43 100644 --- a/descriptor/reader/building_reader.cc +++ b/descriptor/reader/building_reader.cc @@ -3,6 +3,7 @@ * (see LICENSE.txt) */ +#include "../objversion.h" #include #include #include "../../bauer/hausbauer.h" @@ -42,7 +43,14 @@ obj_desc_t * tile_reader_t::read_node(FILE *fp, obj_node_info_t &node) // old versions of PAK files have no version stamp. // But we know, the highest bit was always cleared. const uint16 v = decode_uint16(p); - const int version = (v & 0x8000)!=0 ? v&0x7FFF : 0; + int version = (v & 0x8000) != 0 ? v & 0x7FFF : 0; + const bool extended = version > 0 ? (v & EX_VER) != 0 : false; + uint16 extended_version = 0; + if (extended) { + version = version & EX_VER ? version & 0x3FFF : 0; + while (version > 0x100) { version -= 0x100; extended_version++; } + extended_version--; + } building_tile_desc_t *desc = new building_tile_desc_t(); @@ -231,7 +239,14 @@ obj_desc_t * building_reader_t::read_node(FILE *fp, obj_node_info_t &node) // old versions of PAK files have no version stamp. // But we know, the highest bit was always cleared. const uint16 v = decode_uint16(p); - const int version = (v & 0x8000)!=0 ? v&0x7FFF : 0; + int version = (v & 0x8000) != 0 ? v & 0x7FFF : 0; + const bool extended = version > 0 ? (v & EX_VER) != 0 : false; + uint16 extended_version = 0; + if (extended) { + version = version & EX_VER ? version & 0x3FFF : 0; + while (version > 0x100) { version -= 0x100; extended_version++; } + extended_version--; + } old_btyp::typ btyp; building_desc_t *desc = new building_desc_t(); diff --git a/descriptor/reader/citycar_reader.cc b/descriptor/reader/citycar_reader.cc index fbc84e04450..5b3413951af 100644 --- a/descriptor/reader/citycar_reader.cc +++ b/descriptor/reader/citycar_reader.cc @@ -3,6 +3,7 @@ * (see LICENSE.txt) */ +#include "../objversion.h" #include #include "../../simunits.h" @@ -48,7 +49,14 @@ obj_desc_t * citycar_reader_t::read_node(FILE *fp, obj_node_info_t &node) // old versions of PAK files have no version stamp. // But we know, the higher most bit was always cleared. const uint16 v = decode_uint16(p); - const int version = v & 0x8000 ? v & 0x7FFF : 0; + int version = v & 0x8000 ? v & 0x7FFF : 0; + const bool extended = version > 0 ? (v & EX_VER) != 0 : false; + uint16 extended_version = 0; + if (extended) { + version = version & EX_VER ? version & 0x3FFF : 0; + while (version > 0x100) { version -= 0x100; extended_version++; } + extended_version--; + } citycar_desc_t *desc = new citycar_desc_t(); diff --git a/descriptor/reader/crossing_reader.cc b/descriptor/reader/crossing_reader.cc index 2a125f35aa7..c621e8cae67 100644 --- a/descriptor/reader/crossing_reader.cc +++ b/descriptor/reader/crossing_reader.cc @@ -3,6 +3,7 @@ * (see LICENSE.txt) */ +#include "../objversion.h" #include #include "../../dataobj/crossing_logic.h" @@ -42,7 +43,14 @@ obj_desc_t * crossing_reader_t::read_node(FILE *fp, obj_node_info_t &node) // old versions of PAK files have no version stamp. // But we know, the higher most bit was always cleared. const uint16 v = decode_uint16(p); - const int version = v & 0x8000 ? v & 0x7FFF : 0; + int version = v & 0x8000 ? v & 0x7FFF : 0; + const bool extended = version > 0 ? (v & EX_VER) != 0 : false; + uint16 extended_version = 0; + if (extended) { + version = version & EX_VER ? version & 0x3FFF : 0; + while (version > 0x100) { version -= 0x100; extended_version++; } + extended_version--; + } crossing_desc_t *desc = new crossing_desc_t(); diff --git a/descriptor/reader/factory_reader.cc b/descriptor/reader/factory_reader.cc index f6683c0db3d..f13f9161955 100644 --- a/descriptor/reader/factory_reader.cc +++ b/descriptor/reader/factory_reader.cc @@ -3,6 +3,7 @@ * (see LICENSE.txt) */ +#include "../objversion.h" #include #include "../../simfab.h" #include "../../bauer/fabrikbauer.h" @@ -205,7 +206,14 @@ obj_desc_t *factory_supplier_reader_t::read_node(FILE *fp, obj_node_info_t &node // But we know, the higher most bit was always cleared. const uint16 v = decode_uint16(p); - const int version = v & 0x8000 ? v & 0x7FFF : 0; + int version = v & 0x8000 ? v & 0x7FFF : 0; + const bool extended = version > 0 ? (v & EX_VER) != 0 : false; + uint16 extended_version = 0; + if (extended) { + version = version & EX_VER ? version & 0x3FFF : 0; + while (version > 0x100) { version -= 0x100; extended_version++; } + extended_version--; + } factory_supplier_desc_t *desc = new factory_supplier_desc_t(); @@ -241,7 +249,14 @@ obj_desc_t *factory_product_reader_t::read_node(FILE *fp, obj_node_info_t &node) // old versions of PAK files have no version stamp. // But we know, the higher most bit was always cleared. const uint16 v = decode_uint16(p); - const int version = v & 0x8000 ? v & 0x7FFF : 0; + int version = v & 0x8000 ? v & 0x7FFF : 0; + const bool extended = version > 0 ? (v & EX_VER) != 0 : false; + uint16 extended_version = 0; + if (extended) { + version = version & EX_VER ? version & 0x3FFF : 0; + while (version > 0x100) { version -= 0x100; extended_version++; } + extended_version--; + } factory_product_desc_t *desc = new factory_product_desc_t(); if(version == 1) { @@ -279,7 +294,14 @@ obj_desc_t *factory_reader_t::read_node(FILE *fp, obj_node_info_t &node) // old versions of PAK files have no version stamp. // But we know, the higher most bit was always cleared. const uint16 v = decode_uint16(p); - const int version = v & 0x8000 ? v & 0x7FFF : 0; + int version = v & 0x8000 ? v & 0x7FFF : 0; + const bool extended = version > 0 ? (v & EX_VER) != 0 : false; + uint16 extended_version = 0; + if (extended) { + version = version & EX_VER ? version & 0x3FFF : 0; + while (version > 0x100) { version -= 0x100; extended_version++; } + extended_version--; + } factory_desc_t *desc = new factory_desc_t(); desc->sound_id = NO_SOUND; diff --git a/descriptor/reader/good_reader.cc b/descriptor/reader/good_reader.cc index 3e1a5a0de44..484b3fd9da1 100644 --- a/descriptor/reader/good_reader.cc +++ b/descriptor/reader/good_reader.cc @@ -3,6 +3,7 @@ * (see LICENSE.txt) */ +#include "../objversion.h" #include #include "../../simdebug.h" #include "../../bauer/goods_manager.h" @@ -46,7 +47,14 @@ obj_desc_t * goods_reader_t::read_node(FILE *fp, obj_node_info_t &node) // old versions of PAK files have no version stamp. // But we know, the higher most bit was always cleared. const uint16 v = decode_uint16(p); - const int version = v & 0x8000 ? v & 0x7FFF : 0; + int version = v & 0x8000 ? v & 0x7FFF : 0; + const bool extended = version > 0 ? (v & EX_VER) != 0 : false; + uint16 extended_version = 0; + if (extended) { + version = version & EX_VER ? version & 0x3FFF : 0; + while (version > 0x100) { version -= 0x100; extended_version++; } + extended_version--; + } // some defaults goods_desc_t *desc = new goods_desc_t(); @@ -64,7 +72,6 @@ obj_desc_t * goods_reader_t::read_node(FILE *fp, obj_node_info_t &node) } else if(version == 2) { // Versioned node, version 2 - desc->base_value = decode_uint16(p); desc->catg = (uint8)decode_uint16(p); desc->speed_bonus = decode_uint16(p); @@ -73,14 +80,46 @@ obj_desc_t * goods_reader_t::read_node(FILE *fp, obj_node_info_t &node) } else if(version == 3) { // Versioned node, version 3 - desc->base_value = decode_uint16(p); + const uint16 base_value = decode_uint16(p); desc->catg = decode_uint8(p); desc->speed_bonus = decode_uint16(p); desc->weight_per_unit = decode_uint16(p); desc->color = decode_uint8(p); - + if (extended) { + // Extended has fare_stages and class data; OTRP uses simple base_value + uint8 number_of_classes = 1; + if (extended_version >= 1) { + number_of_classes = decode_uint8(p); + } + const uint8 fare_stages = decode_uint8(p); + if (fare_stages > 0) { + // read first fare stage as base_value, skip rest + desc->base_value = decode_uint16(p); // to_distance + desc->base_value = decode_uint16(p); // price -> use as base_value + for (int i = 1; i < fare_stages; i++) { + decode_uint16(p); // to_distance + decode_uint16(p); // price + } + } + else { + desc->base_value = base_value; + } + if (extended_version >= 1) { + // skip class revenue percentages + for (uint8 i = 0; i < number_of_classes; i++) { + decode_uint16(p); + } + } + if (extended_version > 1) { + dbg->fatal("goods_reader_t::read_node()", "Incompatible Extended pak version %i", extended_version); + } + } + else { + desc->base_value = base_value; + } } else if (version == 4) { + // OTRP-only: base_value as sint64 desc->base_value = decode_sint64(p); desc->catg = decode_uint8(p); desc->speed_bonus = decode_uint16(p); diff --git a/descriptor/reader/groundobj_reader.cc b/descriptor/reader/groundobj_reader.cc index de7f339c218..1b12f23069f 100644 --- a/descriptor/reader/groundobj_reader.cc +++ b/descriptor/reader/groundobj_reader.cc @@ -3,6 +3,7 @@ * (see LICENSE.txt) */ +#include "../objversion.h" #include #include "../../simunits.h" @@ -52,7 +53,14 @@ obj_desc_t * groundobj_reader_t::read_node(FILE *fp, obj_node_info_t &node) // old versions of PAK files have no version stamp. // But we know, the highest bit was always cleared. const uint16 v = decode_uint16(p); - const int version = v & 0x8000 ? v & 0x7FFF : 0; + int version = v & 0x8000 ? v & 0x7FFF : 0; + const bool extended = version > 0 ? (v & EX_VER) != 0 : false; + uint16 extended_version = 0; + if (extended) { + version = version & EX_VER ? version & 0x3FFF : 0; + while (version > 0x100) { version -= 0x100; extended_version++; } + extended_version--; + } groundobj_desc_t *desc = new groundobj_desc_t(); if (version == 2) { // cost as sint64 diff --git a/descriptor/reader/imagelist3d_reader.cc b/descriptor/reader/imagelist3d_reader.cc new file mode 100644 index 00000000000..a2382cad812 --- /dev/null +++ b/descriptor/reader/imagelist3d_reader.cc @@ -0,0 +1,28 @@ +/* + * This file is part of the Simutrans project under the Artistic License. + * (see LICENSE.txt) + */ + +#include +#include "../../simdebug.h" + +#include "../image_array_3d.h" + +#include "imagelist3d_reader.h" +#include "../obj_node_info.h" +#include "../../tpl/array_tpl.h" + + +obj_desc_t *imagelist3d_reader_t::read_node(FILE *fp, obj_node_info_t &node) +{ + array_tpl desc_buf(node.size); + if (fread(desc_buf.begin(), node.size, 1, fp) != 1) { + return NULL; + } + char *p = desc_buf.begin(); + + image_array_3d_t *desc = new image_array_3d_t(); + desc->count = decode_uint16(p); + + return desc; +} diff --git a/descriptor/reader/imagelist3d_reader.h b/descriptor/reader/imagelist3d_reader.h new file mode 100644 index 00000000000..a5959813056 --- /dev/null +++ b/descriptor/reader/imagelist3d_reader.h @@ -0,0 +1,23 @@ +/* + * This file is part of the Simutrans project under the Artistic License. + * (see LICENSE.txt) + */ + +#ifndef DESCRIPTOR_READER_IMAGELIST3D_READER_H +#define DESCRIPTOR_READER_IMAGELIST3D_READER_H + + +#include "obj_reader.h" + + +class imagelist3d_reader_t : public obj_reader_t +{ + OBJ_READER_DEF(imagelist3d_reader_t, obj_imagelist3d, "imagelist3d"); + +public: + /// @copydoc obj_reader::read_node + obj_desc_t *read_node(FILE *fp, obj_node_info_t &node) OVERRIDE; +}; + + +#endif diff --git a/descriptor/reader/pedestrian_reader.cc b/descriptor/reader/pedestrian_reader.cc index 0cdebcbd8a5..03e4ba23a33 100644 --- a/descriptor/reader/pedestrian_reader.cc +++ b/descriptor/reader/pedestrian_reader.cc @@ -3,6 +3,7 @@ * (see LICENSE.txt) */ +#include "../objversion.h" #include #include "../../vehicle/pedestrian.h" @@ -48,7 +49,14 @@ obj_desc_t * pedestrian_reader_t::read_node(FILE *fp, obj_node_info_t &node) // old versions of PAK files have no version stamp. // But we know, the higher most bit was always cleared. const uint16 v = decode_uint16(p); - const int version = v & 0x8000 ? v & 0x7FFF : 0; + int version = v & 0x8000 ? v & 0x7FFF : 0; + const bool extended = version > 0 ? (v & EX_VER) != 0 : false; + uint16 extended_version = 0; + if (extended) { + version = version & EX_VER ? version & 0x3FFF : 0; + while (version > 0x100) { version -= 0x100; extended_version++; } + extended_version--; + } pedestrian_desc_t *desc = new pedestrian_desc_t(); desc->steps_per_frame = 0; diff --git a/descriptor/reader/roadsign_reader.cc b/descriptor/reader/roadsign_reader.cc index 29baddaf917..0f6b1e0a36e 100644 --- a/descriptor/reader/roadsign_reader.cc +++ b/descriptor/reader/roadsign_reader.cc @@ -3,6 +3,7 @@ * (see LICENSE.txt) */ +#include "../objversion.h" #include #include "../../obj/roadsign.h" @@ -46,11 +47,18 @@ obj_desc_t * roadsign_reader_t::read_node(FILE *fp, obj_node_info_t &node) char *p = desc_buf.begin(); const uint16 v = decode_uint16(p); - const int version = v & 0x8000 ? v & 0x7FFF : 0; + int version = v & 0x8000 ? v & 0x7FFF : 0; + const bool extended = version > 0 ? (v & EX_VER) != 0 : false; + uint16 extended_version = 0; + if (extended) { + version = version & EX_VER ? version & 0x3FFF : 0; + while (version > 0x100) { version -= 0x100; extended_version++; } + extended_version--; + } roadsign_desc_t *desc = new roadsign_desc_t(); if (version == 6) { - // cost as sint64, maintenance added + // OTRP-only: cost as sint64, maintenance added desc->min_speed = kmh_to_speed(decode_uint16(p)); desc->price = decode_sint64(p); desc->maintenance = decode_sint64(p); @@ -61,7 +69,7 @@ obj_desc_t * roadsign_reader_t::read_node(FILE *fp, obj_node_info_t &node) desc->retire_date = decode_uint16(p); } else if(version==5) { - // Versioned node, version 5 + // Versioned node, version 5 (OTRP-only: flags as uint16) desc->min_speed = kmh_to_speed(decode_uint16(p)); desc->price = decode_uint32(p); desc->flags = decode_uint16(p); @@ -79,6 +87,30 @@ obj_desc_t * roadsign_reader_t::read_node(FILE *fp, obj_node_info_t &node) desc->wtyp = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); + if (extended) { + if (extended_version > 3) { + dbg->fatal("roadsign_reader_t::read_node()", "Incompatible Extended pak version %i", extended_version); + } + decode_uint8(p); // allow_underground + if (extended_version >= 1) { + decode_uint32(p); // signal_group + decode_uint32(p); // base_maintenance + decode_uint32(p); // max_distance_to_signalbox + decode_uint8(p); // aspects + decode_sint8(p); // has_call_on + decode_sint8(p); // has_selective_choose + decode_uint8(p); // working_method + decode_sint8(p); // permissive + decode_uint32(p); // max_speed (kmh) + decode_uint32(p); // base_way_only_cost + decode_uint8(p); // upgrade_group + decode_uint8(p); // intermediate_block + decode_uint8(p); // normal_danger + } + if (extended_version >= 2) { + decode_uint8(p); // double_block + } + } } else if(version==3) { // Versioned node, version 3 @@ -89,6 +121,12 @@ obj_desc_t * roadsign_reader_t::read_node(FILE *fp, obj_node_info_t &node) desc->wtyp = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); + if (extended) { + if (extended_version > 1) { + dbg->fatal("roadsign_reader_t::read_node()", "Incompatible Extended pak version %i", extended_version); + } + decode_uint8(p); // allow_underground + } } else if(version==2) { // Versioned node, version 2 diff --git a/descriptor/reader/sim_reader.cc b/descriptor/reader/sim_reader.cc index 9d42eb806e9..3770adb95e2 100644 --- a/descriptor/reader/sim_reader.cc +++ b/descriptor/reader/sim_reader.cc @@ -8,6 +8,7 @@ #include "image_reader.h" #include "imagelist_reader.h" #include "imagelist2d_reader.h" +#include "imagelist3d_reader.h" #include "bridge_reader.h" #include "tunnel_reader.h" #include "building_reader.h" @@ -41,6 +42,7 @@ text_reader_t text_reader_t::the_instance; image_reader_t image_reader_t::the_instance; imagelist_reader_t imagelist_reader_t::the_instance; imagelist2d_reader_t imagelist2d_reader_t::the_instance; +imagelist3d_reader_t imagelist3d_reader_t::the_instance; root_reader_t root_reader_t::the_instance; xref_reader_t xref_reader_t::the_instance; diff --git a/descriptor/reader/sound_reader.cc b/descriptor/reader/sound_reader.cc index 8987b4f2e61..c5987e3dc2b 100644 --- a/descriptor/reader/sound_reader.cc +++ b/descriptor/reader/sound_reader.cc @@ -3,6 +3,7 @@ * (see LICENSE.txt) */ +#include "../objversion.h" #include #include "../sound_desc.h" @@ -32,7 +33,14 @@ obj_desc_t * sound_reader_t::read_node(FILE *fp, obj_node_info_t &node) char *p = desc_buf.begin(); const uint16 v = decode_uint16(p); - const int version = v & 0x8000 ? v & 0x7FFF : 0; + int version = v & 0x8000 ? v & 0x7FFF : 0; + const bool extended = version > 0 ? (v & EX_VER) != 0 : false; + uint16 extended_version = 0; + if (extended) { + version = version & EX_VER ? version & 0x3FFF : 0; + while (version > 0x100) { version -= 0x100; extended_version++; } + extended_version--; + } sound_desc_t *desc = new sound_desc_t(); diff --git a/descriptor/reader/tree_reader.cc b/descriptor/reader/tree_reader.cc index 3b1f1494e43..0e8c766632e 100644 --- a/descriptor/reader/tree_reader.cc +++ b/descriptor/reader/tree_reader.cc @@ -3,6 +3,7 @@ * (see LICENSE.txt) */ +#include "../objversion.h" #include #include "../../obj/simobj.h" @@ -44,7 +45,14 @@ obj_desc_t * tree_reader_t::read_node(FILE *fp, obj_node_info_t &node) // old versions of PAK files have no version stamp. // But we know, the highest bit was always cleared. const uint16 v = decode_uint16(p); - const int version = v & 0x8000 ? v & 0x7FFF : 0; + int version = v & 0x8000 ? v & 0x7FFF : 0; + const bool extended = version > 0 ? (v & EX_VER) != 0 : false; + uint16 extended_version = 0; + if (extended) { + version = version & EX_VER ? version & 0x3FFF : 0; + while (version > 0x100) { version -= 0x100; extended_version++; } + extended_version--; + } tree_desc_t *desc = new tree_desc_t(); if(version==2) { diff --git a/descriptor/reader/tunnel_reader.cc b/descriptor/reader/tunnel_reader.cc index de6d43fe111..12013f1ac04 100644 --- a/descriptor/reader/tunnel_reader.cc +++ b/descriptor/reader/tunnel_reader.cc @@ -3,6 +3,7 @@ * (see LICENSE.txt) */ +#include "../objversion.h" #include #include "../../simdebug.h" @@ -73,10 +74,17 @@ obj_desc_t * tunnel_reader_t::read_node(FILE *fp, obj_node_info_t &node) char *p = desc_buf.begin(); const uint16 v = decode_uint16(p); - const int version = v & 0x8000 ? v & 0x7FFF : 0; + int version = v & 0x8000 ? v & 0x7FFF : 0; + const bool extended = version > 0 ? (v & EX_VER) != 0 : false; + uint16 extended_version = 0; + if (extended) { + version = version & EX_VER ? version & 0x3FFF : 0; + while (version > 0x100) { version -= 0x100; extended_version++; } + extended_version--; + } if (version == 6) { - // cost/maintenance as sint64 + // OTRP-only: cost/maintenance as sint64 desc->topspeed = decode_uint32(p); desc->price = decode_sint64(p); desc->maintenance = decode_sint64(p); @@ -96,10 +104,39 @@ obj_desc_t * tunnel_reader_t::read_node(FILE *fp, obj_node_info_t &node) desc->wtyp = decode_uint8(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); - desc->axle_load = decode_uint16(p); // new + desc->axle_load = decode_uint16(p); desc->number_of_seasons = decode_uint8(p); desc->has_way = decode_uint8(p); desc->broad_portals = decode_uint8(p); + if (extended) { + // skip Extended-specific way_constraints and gradient fields + decode_uint8(p); // permissive + decode_uint8(p); // prohibitive + if (extended_version >= 1) { + decode_uint16(p); // topspeed_gradient_1 + decode_uint16(p); // topspeed_gradient_2 + decode_sint8(p); // max_altitude + decode_uint8(p); // max_vehicles_on_tile + } + if (extended_version >= 2) { + // complex subsea cost flags block - just skip it safely + uint8 flags = decode_uint8(p); + decode_uint8(p); // is_half_height already consumed by flags byte + if (flags & 0x02) { decode_uint32(p); decode_uint32(p); } // subsea_cost, subsea_maintenance + if (flags & 0x04) { decode_uint32(p); } // subbuilding_cost + if (flags & 0x08) { decode_uint32(p); decode_uint32(p); } // subwaterline_cost, _maintenance + if (flags & 0x10) { + decode_uint32(p); decode_uint32(p); decode_uint32(p); // subway, depth, depth2 + uint8 dl = decode_uint8(p); // depth_limit + if (dl & 0x80) { /* underwater_limit encoded in dl */ } + } + if (flags & 0x20) { decode_uint8(p); } // underwater_limit + if (flags & 0x40) { decode_uint16(p); } // length_limit + } + if (extended_version > 2) { + dbg->fatal("tunnel_reader_t::read_node()", "Incompatible Extended pak version %i", extended_version); + } + } } else if( version == 4 ) { // versioned node, version 4 - broad portal support @@ -110,6 +147,20 @@ obj_desc_t * tunnel_reader_t::read_node(FILE *fp, obj_node_info_t &node) desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->number_of_seasons = decode_uint8(p); + if (extended) { + desc->axle_load = decode_uint32(p); + decode_uint8(p); // permissive + decode_uint8(p); // prohibitive + if (extended_version >= 1) { + decode_uint16(p); // topspeed_gradient_1 + decode_uint16(p); // topspeed_gradient_2 + decode_sint8(p); // max_altitude + decode_uint8(p); // max_vehicles_on_tile + } + if (extended_version > 1) { + dbg->fatal("tunnel_reader_t::read_node()", "Incompatible Extended pak version %i", extended_version); + } + } desc->has_way = decode_uint8(p); desc->broad_portals = decode_uint8(p); } @@ -123,6 +174,14 @@ obj_desc_t * tunnel_reader_t::read_node(FILE *fp, obj_node_info_t &node) desc->retire_date = decode_uint16(p); desc->number_of_seasons = decode_uint8(p); desc->has_way = decode_uint8(p); + if (extended) { + desc->axle_load = decode_uint32(p); + decode_uint8(p); // permissive + decode_uint8(p); // prohibitive + if (extended_version > 0) { + dbg->fatal("tunnel_reader_t::read_node()", "Incompatible Extended pak version %i", extended_version); + } + } desc->broad_portals = 0; } else if(version == 2) { @@ -134,6 +193,16 @@ obj_desc_t * tunnel_reader_t::read_node(FILE *fp, obj_node_info_t &node) desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); desc->number_of_seasons = decode_uint8(p); + if (extended) { + if (extended_version == 0) { + desc->axle_load = decode_uint32(p); + decode_uint8(p); // permissive + decode_uint8(p); // prohibitive + } + else { + dbg->fatal("tunnel_reader_t::read_node()", "Incompatible Extended pak version %i", extended_version); + } + } desc->has_way = 0; desc->broad_portals = 0; } diff --git a/descriptor/reader/vehicle_reader.cc b/descriptor/reader/vehicle_reader.cc index b71e9c1f79d..5e2a565adae 100644 --- a/descriptor/reader/vehicle_reader.cc +++ b/descriptor/reader/vehicle_reader.cc @@ -3,6 +3,7 @@ * (see LICENSE.txt) */ +#include "../objversion.h" #include #include "../../simdebug.h" #include "../../simconst.h" @@ -46,7 +47,14 @@ obj_desc_t *vehicle_reader_t::read_node(FILE *fp, obj_node_info_t &node) // old versions of PAK files have no version stamp. // But we know, the higher most bit was always cleared. const uint16 v = decode_uint16(p); - const int version = v & 0x8000 ? v & 0x7FFF : 0; + int version = v & 0x8000 ? v & 0x7FFF : 0; + const bool extended = version > 0 ? (v & EX_VER) != 0 : false; + uint16 extended_version = 0; + if (extended) { + version = version & EX_VER ? version & 0x3FFF : 0; + while (version > 0x100) { version -= 0x100; extended_version++; } + extended_version--; + } vehicle_desc_t *desc = new vehicle_desc_t(); @@ -155,7 +163,7 @@ obj_desc_t *vehicle_reader_t::read_node(FILE *fp, obj_node_info_t &node) desc->trailer_count = decode_uint8(p); } else if (version==8) { - // multiple freight images... + // multiple freight images; Extended v8 adds optional trailing block desc->price = decode_uint32(p); desc->capacity = decode_uint16(p); desc->topspeed = decode_uint16(p); @@ -174,78 +182,246 @@ obj_desc_t *vehicle_reader_t::read_node(FILE *fp, obj_node_info_t &node) desc->leader_count = decode_uint8(p); desc->trailer_count = decode_uint8(p); desc->freight_image_type = decode_uint8(p); + if (extended) { + // skip Extended-specific trailing block (is_tilting, way_constraints, catering, etc.) + if (extended_version > 6) { + dbg->fatal("vehicle_reader_t::read_node()", "Incompatible Extended pak version %i", extended_version); + } + decode_uint8(p); // is_tilting + decode_uint8(p); // way_constraints permissive + decode_uint8(p); // way_constraints prohibitive + decode_uint8(p); // catering_level + decode_uint8(p); // bidirectional + decode_uint8(p); // can_lead_from_rear + decode_uint8(p); // comfort[0] + decode_uint16(p); // overcrowded_capacity + decode_uint16(p); // min/max_loading_time + decode_uint8(p); // upgrades + decode_uint32(p); // base_upgrade_price + decode_uint8(p); // available_only_as_upgrade + if (extended_version == 1) { + decode_uint16(p); // base_fixed_cost + } else if (extended_version >= 2) { + decode_uint32(p); // base_fixed_cost + } + if (extended_version >= 3) { decode_uint16(p); } // tractive_effort + if (extended_version >= 4) { + decode_uint16(p); // air_resistance_hundreds + decode_uint8(p); // can_be_at_rear + decode_uint16(p); // increase_maintenance_after_years + decode_uint16(p); // increase_maintenance_by_percent + decode_uint8(p); // years_before_maintenance_max_reached + } + if (extended_version >= 5) { decode_uint8(p); } // livery_image_type + if (extended_version >= 6) { decode_uint16(p); decode_uint16(p); } // min/max_loading_time_seconds + } } else if (version==9) { - // new: fixed_cost, loading_time, axle_load - desc->price = decode_uint32(p); - desc->capacity = decode_uint16(p); - desc->loading_time = decode_uint16(p); - desc->topspeed = decode_uint16(p); - desc->weight = decode_uint16(p); - desc->axle_load = decode_uint16(p); - desc->power = decode_uint32(p); - desc->running_cost = decode_uint16(p); - desc->maintenance = decode_uint16(p); - - desc->intro_date = decode_uint16(p); - desc->retire_date = decode_uint16(p); - desc->gear = decode_uint16(p); - - desc->wtyp = decode_uint8(p); - desc->sound = decode_sint8(p); - desc->engine_type = decode_uint8(p); - desc->len = decode_uint8(p); - desc->leader_count = decode_uint8(p); - desc->trailer_count = decode_uint8(p); - desc->freight_image_type = decode_uint8(p); - } - else if (version==10) { - // new: weight in kgs - desc->price = decode_uint32(p); - desc->capacity = decode_uint16(p); - desc->loading_time = decode_uint16(p); - desc->topspeed = decode_uint16(p); - desc->weight = decode_uint32(p); - desc->axle_load = decode_uint16(p); - desc->power = decode_uint32(p); - desc->running_cost = decode_uint16(p); - desc->maintenance = decode_uint16(p); - - desc->intro_date = decode_uint16(p); - desc->retire_date = decode_uint16(p); - desc->gear = decode_uint16(p); - - desc->wtyp = decode_uint8(p); - desc->sound = decode_sint8(p); - desc->engine_type = decode_uint8(p); - desc->len = decode_uint8(p); - desc->leader_count = decode_uint8(p); - desc->trailer_count = decode_uint8(p); - desc->freight_image_type = decode_uint8(p); + // Extended v9 has different field layout than OTRP v9: + // Extended v9 omits loading_time and base_fixed_cost in the standard positions + if (extended) { + desc->price = decode_uint32(p); + desc->capacity = decode_uint16(p); + // Extended v9: no loading_time here (it's in Extended block) + desc->topspeed = decode_uint16(p); + desc->weight = decode_uint16(p); + desc->axle_load = decode_uint16(p); + desc->power = decode_uint32(p); + desc->running_cost = decode_uint16(p); + // Extended v9: no base_fixed_cost here (it's in Extended block) + desc->intro_date = decode_uint16(p); + desc->retire_date = decode_uint16(p); + desc->gear = decode_uint16(p); + desc->wtyp = decode_uint8(p); + desc->sound = decode_sint8(p); + desc->engine_type = decode_uint8(p); + desc->len = decode_uint8(p); + desc->leader_count = decode_uint8(p); + desc->trailer_count = decode_uint8(p); + desc->freight_image_type = decode_uint8(p); + // skip Extended trailing block + if (extended_version > 7) { + dbg->fatal("vehicle_reader_t::read_node()", "Incompatible Extended pak version %i", extended_version); + } + decode_uint8(p); // is_tilting + decode_uint8(p); // permissive + decode_uint8(p); // prohibitive + decode_uint8(p); // catering_level + decode_uint8(p); // bidirectional + decode_uint8(p); // can_lead_from_rear + decode_uint8(p); // comfort[0] + decode_uint16(p); // overcrowded_capacity + decode_uint16(p); // min/max_loading_time -> use as loading_time + desc->loading_time = 0; // already consumed above as dummy + decode_uint8(p); // upgrades + decode_uint32(p); // base_upgrade_price + decode_uint8(p); // available_only_as_upgrade + if (extended_version == 1) { + decode_uint16(p); + } else if (extended_version >= 2) { + decode_uint32(p); // base_fixed_cost + } + if (extended_version >= 3) { decode_uint16(p); } // tractive_effort + if (extended_version >= 4) { + decode_uint16(p); // air_resistance_hundreds + decode_uint8(p); // can_be_at_rear + decode_uint16(p); // increase_maintenance_after_years + decode_uint16(p); // increase_maintenance_by_percent + decode_uint8(p); // years_before_maintenance_max_reached + } + if (extended_version >= 5) { decode_uint8(p); } // livery_image_type + if (extended_version >= 6) { decode_uint16(p); decode_uint16(p); } // loading_time_seconds + if (extended_version >= 7) { + decode_uint16(p); // rolling_resistance + decode_uint16(p); // brake_force + decode_uint16(p); // minimum_runway_length + } + } + else { + // OTRP v9: new: fixed_cost, loading_time, axle_load + desc->price = decode_uint32(p); + desc->capacity = decode_uint16(p); + desc->loading_time = decode_uint16(p); + desc->topspeed = decode_uint16(p); + desc->weight = decode_uint16(p); + desc->axle_load = decode_uint16(p); + desc->power = decode_uint32(p); + desc->running_cost = decode_uint16(p); + desc->maintenance = decode_uint16(p); + desc->intro_date = decode_uint16(p); + desc->retire_date = decode_uint16(p); + desc->gear = decode_uint16(p); + desc->wtyp = decode_uint8(p); + desc->sound = decode_sint8(p); + desc->engine_type = decode_uint8(p); + desc->len = decode_uint8(p); + desc->leader_count = decode_uint8(p); + desc->trailer_count = decode_uint8(p); + desc->freight_image_type = decode_uint8(p); + } } - else if (version==11) { - // new: fix cost as uint32 - desc->price = decode_uint32(p); - desc->capacity = decode_uint16(p); - desc->loading_time = decode_uint16(p); - desc->topspeed = decode_uint16(p); - desc->weight = decode_uint32(p); - desc->axle_load = decode_uint16(p); - desc->power = decode_uint32(p); - desc->running_cost = decode_uint16(p); - desc->maintenance = decode_uint32(p); - - desc->intro_date = decode_uint16(p); - desc->retire_date = decode_uint16(p); - desc->gear = decode_uint16(p); - - desc->wtyp = decode_uint8(p); - desc->sound = decode_sint8(p); - desc->engine_type = decode_uint8(p); - desc->len = decode_uint8(p); - desc->leader_count = decode_uint8(p); - desc->trailer_count = decode_uint8(p); - desc->freight_image_type = decode_uint8(p); + else if (version==10 || version==11) { + // Extended v10/11 have different layout from OTRP v10/11 + if (extended) { + desc->price = decode_uint32(p); + // Extended v10/11 extended_version >= 4: has classes byte + uint8 classes = 1; + if (extended_version >= 4) { + classes = decode_uint8(p); + } + // Read first class capacity, skip rest + desc->capacity = decode_uint16(p); + for (uint8 i = 1; i < classes; i++) { decode_uint16(p); } + // Extended: no loading_time in standard position + desc->topspeed = decode_uint16(p); + desc->weight = decode_uint32(p); // already kg + desc->axle_load = decode_uint16(p); + desc->power = decode_uint32(p); + desc->running_cost = decode_uint16(p); + // Extended: no base_fixed_cost in standard position + desc->intro_date = decode_uint16(p); + desc->retire_date = decode_uint16(p); + desc->gear = decode_uint16(p); + desc->wtyp = decode_uint8(p); + if (extended_version >= 3) { + desc->sound = decode_sint16(p); + } else { + desc->sound = decode_sint8(p); + } + desc->engine_type = decode_uint8(p); + desc->len = decode_uint8(p); + desc->leader_count = decode_uint8(p); + desc->trailer_count = decode_uint8(p); + desc->freight_image_type = decode_uint8(p); + // skip Extended trailing block + if (extended_version >= 8) { + dbg->fatal("vehicle_reader_t::read_node()", "Incompatible Extended pak version %i", extended_version); + } + decode_uint8(p); // is_tilting + decode_uint8(p); // permissive + decode_uint8(p); // prohibitive + decode_uint8(p); // catering_level + decode_uint8(p); // bidirectional + if (extended_version >= 5) { + decode_uint8(p); // basic_constraint_prev + } else { + decode_uint8(p); // can_lead_from_rear + } + for (uint8 i = 0; i < classes; i++) { decode_uint8(p); } // comfort[i] + decode_uint16(p); // overcrowded_capacity + decode_uint16(p); // min/max_loading_time + decode_uint8(p); // upgrades + decode_uint32(p); // base_upgrade_price + decode_uint8(p); // available_only_as_upgrade + decode_uint32(p); // base_fixed_cost + decode_uint16(p); // tractive_effort + decode_uint16(p); // air_resistance_hundreds + if (extended_version >= 5) { + decode_uint8(p); // basic_constraint_next + } else { + decode_uint8(p); // can_be_at_rear + } + decode_uint16(p); // increase_maintenance_after_years + decode_uint16(p); // increase_maintenance_by_percent + decode_uint8(p); // years_before_maintenance_max_reached + decode_uint8(p); // livery_image_type + decode_uint16(p); // min_loading_time_seconds + decode_uint16(p); // max_loading_time_seconds + decode_uint16(p); // rolling_resistance + decode_uint16(p); // brake_force + decode_uint16(p); // minimum_runway_length + if (extended_version >= 1) { + decode_uint16(p); // range + decode_uint32(p); // way_wear_factor + } + if (extended_version >= 2) { decode_uint8(p); } // is_tall + if (extended_version >= 5) { decode_uint8(p); } // mixed_load_prohibition + if (extended_version >= 6) { decode_uint8(p); } // override_way_speed + } + else if (version == 10) { + // OTRP v10: weight in kgs + desc->price = decode_uint32(p); + desc->capacity = decode_uint16(p); + desc->loading_time = decode_uint16(p); + desc->topspeed = decode_uint16(p); + desc->weight = decode_uint32(p); + desc->axle_load = decode_uint16(p); + desc->power = decode_uint32(p); + desc->running_cost = decode_uint16(p); + desc->maintenance = decode_uint16(p); + desc->intro_date = decode_uint16(p); + desc->retire_date = decode_uint16(p); + desc->gear = decode_uint16(p); + desc->wtyp = decode_uint8(p); + desc->sound = decode_sint8(p); + desc->engine_type = decode_uint8(p); + desc->len = decode_uint8(p); + desc->leader_count = decode_uint8(p); + desc->trailer_count = decode_uint8(p); + desc->freight_image_type = decode_uint8(p); + } + else { // version == 11 + // OTRP v11: fix cost as uint32 + desc->price = decode_uint32(p); + desc->capacity = decode_uint16(p); + desc->loading_time = decode_uint16(p); + desc->topspeed = decode_uint16(p); + desc->weight = decode_uint32(p); + desc->axle_load = decode_uint16(p); + desc->power = decode_uint32(p); + desc->running_cost = decode_uint16(p); + desc->maintenance = decode_uint32(p); + desc->intro_date = decode_uint16(p); + desc->retire_date = decode_uint16(p); + desc->gear = decode_uint16(p); + desc->wtyp = decode_uint8(p); + desc->sound = decode_sint8(p); + desc->engine_type = decode_uint8(p); + desc->len = decode_uint8(p); + desc->leader_count = decode_uint8(p); + desc->trailer_count = decode_uint8(p); + desc->freight_image_type = decode_uint8(p); + } } else if (version == 12) { // Cost values as sint64 @@ -332,14 +508,22 @@ obj_desc_t *vehicle_reader_t::read_node(FILE *fp, obj_node_info_t &node) desc->freight_image_type=0; } - if(version<9) { + if (version < 9 && !extended) { desc->maintenance = 0; desc->axle_load = 0; desc->loading_time = 1000; } + else if (extended && version < 9) { + desc->maintenance = 0; + desc->axle_load = desc->weight; + desc->loading_time = 1000; + } - // old weights were tons - if(version<10) { + // old weights were tons (OTRP v<10, or Extended v<=9) + if (version < 10 && !extended) { + desc->weight *= 1000; + } + else if (extended && version <= 9) { desc->weight *= 1000; } diff --git a/descriptor/reader/way_obj_reader.cc b/descriptor/reader/way_obj_reader.cc index 514a292b5d4..75665f8aec1 100644 --- a/descriptor/reader/way_obj_reader.cc +++ b/descriptor/reader/way_obj_reader.cc @@ -3,6 +3,7 @@ * (see LICENSE.txt) */ +#include "../objversion.h" #include #include "../../simdebug.h" #include "../../utils/simstring.h" @@ -44,7 +45,14 @@ obj_desc_t * way_obj_reader_t::read_node(FILE *fp, obj_node_info_t &node) // old versions of PAK files have no version stamp. // But we know, the higher most bit was always cleared. const uint16 v = decode_uint16(p); - const uint16 version = v & 0x7FFF; + int version = v & 0x7FFF; + const bool extended = version > 0 ? (v & EX_VER) != 0 : false; + uint16 extended_version = 0; + if (extended) { + version = version & EX_VER ? version & 0x3FFF : 0; + while (version > 0x100) { version -= 0x100; extended_version++; } + extended_version--; + } way_obj_desc_t *desc = new way_obj_desc_t(); diff --git a/descriptor/reader/way_reader.cc b/descriptor/reader/way_reader.cc index 565c8a59424..b45f4eefe31 100644 --- a/descriptor/reader/way_reader.cc +++ b/descriptor/reader/way_reader.cc @@ -3,6 +3,7 @@ * (see LICENSE.txt) */ +#include "../objversion.h" #include #include "../../simdebug.h" #include "../../utils/simstring.h" @@ -65,9 +66,16 @@ obj_desc_t * way_reader_t::read_node(FILE *fp, obj_node_info_t &node) else { const uint16 v = decode_uint16(p); version = v & 0x7FFF; + const bool extended = version > 0 ? (v & EX_VER) != 0 : false; + uint16 extended_version = 0; + if (extended) { + version = version & EX_VER ? version & 0x3FFF : 0; + while (version > 0x100) { version -= 0x100; extended_version++; } + extended_version--; + } if (version == 7) { - // cost/maintenance as sint64 + // OTRP-only: cost/maintenance as sint64 desc->price = decode_sint64(p); desc->maintenance = decode_sint64(p); desc->topspeed = decode_uint32(p); @@ -85,20 +93,46 @@ obj_desc_t * way_reader_t::read_node(FILE *fp, obj_node_info_t &node) desc->price = decode_uint32(p); desc->maintenance = decode_uint32(p); desc->topspeed = decode_uint32(p); - desc->max_weight = decode_uint32(p); + if (!extended) { + // OTRP has separate max_weight field here; Extended does not + desc->max_weight = decode_uint32(p); + } desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); - desc->axle_load = decode_uint16(p); // new + desc->axle_load = decode_uint16(p); desc->wtyp = decode_uint8(p); desc->styp = decode_uint8(p); desc->draw_as_obj = decode_uint8(p); desc->number_of_seasons = decode_sint8(p); + if (extended) { + desc->max_weight = 9999; + // skip Extended-specific way_constraints and gradient fields + decode_uint8(p); // permissive + decode_uint8(p); // prohibitive + if (extended_version >= 1) { + decode_sint32(p); // topspeed_gradient_1 + decode_sint32(p); // topspeed_gradient_2 + decode_sint8(p); // max_altitude + decode_uint8(p); // max_vehicles_on_tile + decode_uint32(p); // wear_capacity + decode_uint32(p); // way_only_cost + decode_uint8(p); // upgrade_group + decode_uint32(p); // monthly_base_wear + } + if (extended_version >= 2) { + decode_uint32(p); // deck_mask + } + if (extended_version > 2) { + dbg->fatal("way_reader_t::read_node()", "Incompatible Extended pak version %i", extended_version); + } + } } else if(version==4 || version==5) { // Versioned node, version 4+5 desc->price = decode_uint32(p); desc->maintenance = decode_uint32(p); desc->topspeed = decode_uint32(p); + // Extended reads axle_load here (uint32); OTRP calls it max_weight desc->max_weight = decode_uint32(p); desc->intro_date = decode_uint16(p); desc->retire_date = decode_uint16(p); @@ -106,6 +140,23 @@ obj_desc_t * way_reader_t::read_node(FILE *fp, obj_node_info_t &node) desc->styp = decode_uint8(p); desc->draw_as_obj = decode_uint8(p); desc->number_of_seasons = decode_sint8(p); + if (extended) { + // skip Extended-specific way_constraints fields + decode_uint8(p); // permissive + decode_uint8(p); // prohibitive + if (extended_version >= 1) { + decode_sint32(p); // topspeed_gradient_1 + decode_sint32(p); // topspeed_gradient_2 + decode_sint8(p); // max_altitude + decode_uint8(p); // max_vehicles_on_tile + decode_uint32(p); // wear_capacity + decode_uint32(p); // way_only_cost + decode_uint8(p); // upgrade_group + } + if (extended_version > 1) { + dbg->fatal("way_reader_t::read_node()", "Incompatible Extended pak version %i", extended_version); + } + } } else if(version==3) { // Versioned node, version 3 From 47e5a4be2ecd66ef4cf065b4b8fd2875ac960967 Mon Sep 17 00:00:00 2001 From: simu_poppo Date: Fri, 10 Apr 2026 22:21:15 +0900 Subject: [PATCH 2/4] read imagelist3d --- descriptor/reader/building_reader.cc | 37 ++++++++++++++++++++++++++-- descriptor/reader/tunnel_reader.cc | 3 +-- descriptor/reader/vehicle_reader.cc | 11 ++++++--- descriptor/vehicle_desc.h | 25 +++++++++++++------ 4 files changed, 62 insertions(+), 14 deletions(-) diff --git a/descriptor/reader/building_reader.cc b/descriptor/reader/building_reader.cc index b7d72b4ff43..7296b60b543 100644 --- a/descriptor/reader/building_reader.cc +++ b/descriptor/reader/building_reader.cc @@ -296,8 +296,9 @@ obj_desc_t * building_reader_t::read_node(FILE *fp, obj_node_info_t &node) desc->preservation_year_month = decode_uint16(p); } else if(version == 8 || version == 9) { - // Versioned node, version 8 + // Versioned node, version 8/9 // station price, maintenance and capacity added + // Extended adds trailing block: control_tower, population, employment, mail, radius, class proportions, pier masks btyp = (old_btyp::typ)decode_uint8(p); desc->type = (building_desc_t::btype)decode_uint8(p); desc->level = decode_uint16(p); @@ -306,7 +307,14 @@ obj_desc_t * building_reader_t::read_node(FILE *fp, obj_node_info_t &node) desc->size.y = decode_uint16(p); desc->layouts = decode_uint8(p); desc->allowed_climates = (climate_bits)(decode_uint16(p) & ALL_CLIMATES); - desc->enables = decode_uint8(p); + if (extended && extended_version >= 5) { + decode_uint16(p); // allowed_regions - not used in OTRP + } + if (extended && extended_version >= 3) { + desc->enables = decode_uint16(p); // uint16 in Extended ext_ver >= 3 + } else { + desc->enables = decode_uint8(p); + } desc->flags = (building_desc_t::flag_t)decode_uint8(p); desc->distribution_weight = decode_uint8(p); desc->intro_date = decode_uint16(p); @@ -316,6 +324,31 @@ obj_desc_t * building_reader_t::read_node(FILE *fp, obj_node_info_t &node) desc->maintenance = decode_sint32(p); desc->price = decode_sint32(p); desc->allow_underground = decode_uint8(p); + if (extended) { + if (extended_version > 6) { + dbg->fatal("building_reader_t::read_node()", "Incompatible Extended pak version %i", extended_version); + } + // skip Extended-specific trailing fields not present in OTRP + decode_uint8(p); // is_control_tower + decode_uint16(p); // population_and_visitor_demand_capacity + decode_uint16(p); // employment_capacity + decode_uint16(p); // mail_demand_and_production_capacity + if (extended_version >= 1) { + decode_uint32(p); // radius + } + if (extended_version >= 4) { + uint8 nclasses = decode_uint8(p); + for (uint8 i = 0; i < nclasses; i++) { decode_uint16(p); } + uint8 nclasses_jobs = decode_uint8(p); + for (uint8 i = 0; i < nclasses_jobs; i++) { decode_uint16(p); } + } + if (extended_version >= 6) { + decode_uint32(p); // pier_deck_mask + decode_uint32(p); // pier_sub_1_mask + decode_uint32(p); // pier_sub_2_mask + decode_uint8(p); // pier_sub_needed + } + } } else if(version == 7) { // Versioned node, version 7 diff --git a/descriptor/reader/tunnel_reader.cc b/descriptor/reader/tunnel_reader.cc index 12013f1ac04..1c067d2638a 100644 --- a/descriptor/reader/tunnel_reader.cc +++ b/descriptor/reader/tunnel_reader.cc @@ -119,9 +119,8 @@ obj_desc_t * tunnel_reader_t::read_node(FILE *fp, obj_node_info_t &node) decode_uint8(p); // max_vehicles_on_tile } if (extended_version >= 2) { - // complex subsea cost flags block - just skip it safely + // flags byte: bit0=is_half_height, other bits select optional cost fields uint8 flags = decode_uint8(p); - decode_uint8(p); // is_half_height already consumed by flags byte if (flags & 0x02) { decode_uint32(p); decode_uint32(p); } // subsea_cost, subsea_maintenance if (flags & 0x04) { decode_uint32(p); } // subbuilding_cost if (flags & 0x08) { decode_uint32(p); decode_uint32(p); } // subwaterline_cost, _maintenance diff --git a/descriptor/reader/vehicle_reader.cc b/descriptor/reader/vehicle_reader.cc index 5e2a565adae..32f54ab879e 100644 --- a/descriptor/reader/vehicle_reader.cc +++ b/descriptor/reader/vehicle_reader.cc @@ -212,7 +212,7 @@ obj_desc_t *vehicle_reader_t::read_node(FILE *fp, obj_node_info_t &node) decode_uint16(p); // increase_maintenance_by_percent decode_uint8(p); // years_before_maintenance_max_reached } - if (extended_version >= 5) { decode_uint8(p); } // livery_image_type + if (extended_version >= 5) { desc->livery_image_type = decode_uint8(p); } // livery_image_type if (extended_version >= 6) { decode_uint16(p); decode_uint16(p); } // min/max_loading_time_seconds } } @@ -269,7 +269,7 @@ obj_desc_t *vehicle_reader_t::read_node(FILE *fp, obj_node_info_t &node) decode_uint16(p); // increase_maintenance_by_percent decode_uint8(p); // years_before_maintenance_max_reached } - if (extended_version >= 5) { decode_uint8(p); } // livery_image_type + if (extended_version >= 5) { desc->livery_image_type = decode_uint8(p); } // livery_image_type if (extended_version >= 6) { decode_uint16(p); decode_uint16(p); } // loading_time_seconds if (extended_version >= 7) { decode_uint16(p); // rolling_resistance @@ -364,7 +364,7 @@ obj_desc_t *vehicle_reader_t::read_node(FILE *fp, obj_node_info_t &node) decode_uint16(p); // increase_maintenance_after_years decode_uint16(p); // increase_maintenance_by_percent decode_uint8(p); // years_before_maintenance_max_reached - decode_uint8(p); // livery_image_type + desc->livery_image_type = decode_uint8(p); // livery_image_type decode_uint16(p); // min_loading_time_seconds decode_uint16(p); // max_loading_time_seconds decode_uint16(p); // rolling_resistance @@ -508,6 +508,11 @@ obj_desc_t *vehicle_reader_t::read_node(FILE *fp, obj_node_info_t &node) desc->freight_image_type=0; } + // non-extended vehicles never have livery images + if(!extended) { + desc->livery_image_type = 0; + } + if (version < 9 && !extended) { desc->maintenance = 0; desc->axle_load = 0; diff --git a/descriptor/vehicle_desc.h b/descriptor/vehicle_desc.h index 2cc783675a0..8b32ff1507b 100644 --- a/descriptor/vehicle_desc.h +++ b/descriptor/vehicle_desc.h @@ -11,6 +11,7 @@ #include "goods_desc.h" #include "image_list.h" #include "image_array.h" +#include "image_array_3d.h" #include "skin_desc.h" #include "sound_desc.h" #include "../dataobj/ribi.h" @@ -80,6 +81,7 @@ class vehicle_desc_t : public obj_desc_transport_related_t { uint8 engine_type; // diesel, steam, electric (requires electrified ways), fuel_cell, etc. sint8 freight_image_type; // number of freight images (displayed for different goods) + uint8 livery_image_type; // number of livery variants (Extended); 0 in standard OTRP vehicles public: // dummy vehicle for the XREF reader @@ -91,7 +93,7 @@ class vehicle_desc_t : public obj_desc_transport_related_t { // default vehicle (used for way search and similar tasks) // since it has no images and not even a name node any calls to this will case a crash vehicle_desc_t(uint8 wtype, uint16 speed, engine_t engine) { - maintenance = freight_image_type = price = capacity = axle_load = running_cost = intro_date = leader_count = trailer_count = 0; + maintenance = freight_image_type = livery_image_type = price = capacity = axle_load = running_cost = intro_date = leader_count = trailer_count = 0; power = weight = 1; loading_time = 1000; gear = 64; @@ -129,7 +131,7 @@ class vehicle_desc_t : public obj_desc_transport_related_t { sint8 goods_index=-1; // freight images: if not found use first freight // special images. this is for reversed image or electric-train image without catenary. // if special_image_index<0, this vehicle do not have freightimagetype="Reverse", "No_Electric" or "Reverse_No_Electric". - sint8 special_image_index=-1; + sint8 special_image_index=-1; for( sint8 i=0; i(6 + trailer_count + leader_count + i)) { // searching freight image @@ -149,8 +151,6 @@ class vehicle_desc_t : public obj_desc_transport_related_t { } } - // vehicle has freight images and we want to use - get appropriate one (if no list then fallback to empty image) - image_array_t const* const list2d = get_child(5); // no freight and non reversed->use EmptyImage if(special_image_index>-1) { goods_index = special_image_index;//find the reverse images, we use reversed one. @@ -159,9 +159,20 @@ class vehicle_desc_t : public obj_desc_transport_related_t { goods_index = 0; } if(goods_index>-1) { - image=list2d->get_image(dir, goods_index); - if(!image) { - if(dir>3) { + if(livery_image_type > 0) { + // Extended pak with both freight and livery images: child 5 is image_array_3d_t + // indexed as [dir][livery_index][goods_index]; OTRP always uses livery_index=0 + image_array_3d_t const* const list3d = get_child(5); + image = list3d->get_image(dir, 0, goods_index); + if(!image && dir>3) { + image = list3d->get_image(dir - 4, 0, goods_index); + } + } + else { + // Normal 2D freight images + image_array_t const* const list2d = get_child(5); + image=list2d->get_image(dir, goods_index); + if(!image && dir>3) { image = list2d->get_image(dir - 4, goods_index); } } From c6518970617cacadc7908913ec07cfddfc368312 Mon Sep 17 00:00:00 2001 From: simu_poppo Date: Sat, 30 May 2026 23:26:21 +0900 Subject: [PATCH 3/4] factory update --- descriptor/factory_desc.h | 8 ++++++ descriptor/reader/factory_reader.cc | 38 +++++++++++++++++++++++++++++ simfab.cc | 18 +++++++++++--- simfab.h | 2 ++ simworld.cc | 33 +++++++++++++++++++++++++ 5 files changed, 96 insertions(+), 3 deletions(-) diff --git a/descriptor/factory_desc.h b/descriptor/factory_desc.h index 59576cb28a3..2137744878f 100644 --- a/descriptor/factory_desc.h +++ b/descriptor/factory_desc.h @@ -235,6 +235,11 @@ class factory_desc_t : public obj_desc_t { sint8 sound_id; uint32 sound_interval; + // Extended pak: automatic upgrade when retiring + uint16 upgrade_probability; // 0 = never upgrade; 1-10000 = chance out of 10000 + char* upgrade_to_name; // temporary during loading; freed after resolution + const factory_desc_t* upgrade_to; // resolved target, NULL = no upgrade + public: const char *get_name() const { return get_building()->get_name(); } const char *get_copyright() const { return get_building()->get_copyright(); } @@ -300,6 +305,9 @@ class factory_desc_t : public obj_desc_t { sint8 get_sound() const { return sound_id; } uint32 get_sound_interval_ms() const { return sound_interval; } + uint16 get_upgrade_probability() const { return upgrade_probability; } + const factory_desc_t* get_upgrade_to() const { return upgrade_to; } + void calc_checksum(checksum_t *chk) const; }; diff --git a/descriptor/reader/factory_reader.cc b/descriptor/reader/factory_reader.cc index f13f9161955..360aa71fdb6 100644 --- a/descriptor/reader/factory_reader.cc +++ b/descriptor/reader/factory_reader.cc @@ -307,6 +307,9 @@ obj_desc_t *factory_reader_t::read_node(FILE *fp, obj_node_info_t &node) desc->sound_id = NO_SOUND; desc->sound_interval = 0xFFFFFFFFul; desc->smokerotations = 0; + desc->upgrade_probability = 0; + desc->upgrade_to_name = NULL; + desc->upgrade_to = NULL; typedef factory_desc_t::site_t site_t; if(version == 5) { @@ -460,6 +463,23 @@ obj_desc_t *factory_reader_t::read_node(FILE *fp, obj_node_info_t &node) desc->mail_demand = 65535; } + // Extended pak fields (appended after any standard version block) + if( extended ) { + if( extended_version > 1 ) { + dbg->fatal("factory_reader_t::read_node()", "Incompatible Extended pak version %i", extended_version); + } + if( extended_version >= 1 ) { + desc->upgrade_probability = decode_uint16(p); + const uint8 len = decode_uint8(p); + if( len > 0 ) { + char *name = new char[len + 1]; + for( uint8 i = 0; i < len; i++ ) { name[i] = decode_sint8(p); } + name[len] = '\0'; + desc->upgrade_to_name = name; + } + } + } + DBG_DEBUG("factory_reader_t::read_node()", "version=%i, place=%i, productivity=%i, suppliers=%i, products=%i, fields=%i, range=%i, level=%i" "Demands: pax=%i / mail=%i / electric=%i," @@ -509,6 +529,9 @@ DBG_MESSAGE("vehicle_reader_t::register_obj()","old sound %i to %i",old_id,desc- } +// Factories that have an upgrade_to_name needing resolution after all descs are loaded. +static vector_tpl pending_upgrade_resolution; + void factory_reader_t::register_obj(obj_desc_t *&data) { factory_desc_t* desc = static_cast(data); @@ -516,10 +539,25 @@ void factory_reader_t::register_obj(obj_desc_t *&data) desc->electricity_producer = ( fab_name_len>11 && (strcmp(desc->get_name()+fab_name_len-9, "kraftwerk")==0 || strcmp(desc->get_name()+fab_name_len-11, "Power Plant")==0) ); desc->correct_smoke(); factory_builder_t::register_desc(desc); + if( desc->upgrade_to_name ) { + pending_upgrade_resolution.append(desc); + } } bool factory_reader_t::successfully_loaded() const { + // Resolve upgrade targets now that all factory descs are registered. + FOR(vector_tpl, desc, pending_upgrade_resolution) { + desc->upgrade_to = factory_builder_t::get_desc(desc->upgrade_to_name); + if( !desc->upgrade_to ) { + dbg->warning("factory_reader_t::successfully_loaded()", + "upgrade target '%s' not found for factory '%s'", + desc->upgrade_to_name, desc->get_name()); + } + delete[] desc->upgrade_to_name; + desc->upgrade_to_name = NULL; + } + pending_upgrade_resolution.clear(); return factory_builder_t::successfully_loaded(); } diff --git a/simfab.cc b/simfab.cc index 1f591ea754d..3d124799087 100644 --- a/simfab.cc +++ b/simfab.cc @@ -2827,14 +2827,26 @@ void fabrik_t::new_month() const uint16 timeline_month = welt->get_timeline_year_month(); // This will be 0 if timeline is disabled. const uint16 retire_month = desc->get_building()->get_retire_year_month(); const uint32 latest_retire_month = retire_month + (12 * welt->get_settings().get_factory_max_years_obsolete());//welt->get_settings().get_factory_max_years_obsolete()); - if(welt->get_settings().is_close_old_factory() && !no_close_factory && !no_close_factory && timeline_month > retire_month) + if(welt->get_settings().is_close_old_factory() && !no_close_factory && timeline_month > retire_month) { if(latest_retire_month <= timeline_month || simrand(latest_retire_month - timeline_month) == 0) { + // Check whether this factory upgrades to a successor instead of simply closing. + pending_upgrade = NULL; + const factory_desc_t* upgrade_desc = desc->get_upgrade_to(); + if( upgrade_desc && desc->get_upgrade_probability() > 0 + && simrand(10000) < desc->get_upgrade_probability() ) { + pending_upgrade = upgrade_desc; + } welt->closed_factories_this_month.append(this); cbuffer_t buf; - buf.printf( translator::translate("Factory %s has closed."), get_name()); - welt->get_message()->add_message( (const char *)buf, get_pos().get_2d(), message_t::warnings, CITY_KI, get_desc()->get_building()->get_tile(0)->get_background(0, 0, 0)); + if( pending_upgrade ) { + buf.printf( translator::translate("Factory %s has been upgraded."), get_name()); + welt->get_message()->add_message( (const char *)buf, get_pos().get_2d(), message_t::industry, CITY_KI, get_desc()->get_building()->get_tile(0)->get_background(0, 0, 0)); + } else { + buf.printf( translator::translate("Factory %s has closed."), get_name()); + welt->get_message()->add_message( (const char *)buf, get_pos().get_2d(), message_t::warnings, CITY_KI, get_desc()->get_building()->get_tile(0)->get_background(0, 0, 0)); + } } else { cbuffer_t buf; buf.printf( translator::translate("Factory %s is retired! Close soon!"), get_name()); diff --git a/simfab.h b/simfab.h index ac22ac2cdcb..4de6cb4e429 100644 --- a/simfab.h +++ b/simfab.h @@ -481,6 +481,7 @@ class fabrik_t * if this flag is true, this factory cannot close even after retire year */ bool no_close_factory=false; + const factory_desc_t* pending_upgrade=NULL; // set when upgrading instead of closing /** * the shipment size @@ -742,6 +743,7 @@ class fabrik_t */ const bool is_no_close_factory() {return no_close_factory;} void set_no_close_factory(bool yesno) { no_close_factory = yesno;} + const factory_desc_t* get_pending_upgrade() const { return pending_upgrade; } /** * get and set shipment size diff --git a/simworld.cc b/simworld.cc index a379e107369..3a2262a7221 100644 --- a/simworld.cc +++ b/simworld.cc @@ -3935,8 +3935,41 @@ void karte_t::new_month() { if(fab_list.is_contained(fab)) { + const factory_desc_t* upgrade_desc = fab->get_pending_upgrade(); + const koord3d old_pos = fab->get_pos(); gebaeude_t* gb = lookup_kartenboden( fab->get_pos().get_2d() )->find(); + const uint8 old_rot = gb ? gb->get_tile()->get_layout() : 0; + + // Save connection lists before the factory is destroyed. + vector_tpl old_lieferziele; + vector_tpl old_suppliers; + if( upgrade_desc ) { + old_lieferziele = fab->get_lieferziele(); + old_suppliers = fab->get_suppliers(); + } + hausbauer_t::remove(get_public_player(), gb); + // fab pointer is invalid from here on. + + if( upgrade_desc ) { + fabrik_t* new_fab = factory_builder_t::build_factory(NULL, upgrade_desc, -1, old_rot, old_pos, get_public_player()); + if( new_fab ) { + // Re-link to former consumers: check if new factory produces what each consumer needs. + FOR(vector_tpl, consumer_pos, old_lieferziele) { + fabrik_t* consumer = fabrik_t::get_fab(consumer_pos); + if( consumer ) { + consumer->add_supplier(new_fab); + } + } + // Re-link to former suppliers: check if new factory needs what each supplier produces. + FOR(vector_tpl, supplier_pos, old_suppliers) { + fabrik_t* supplier = fabrik_t::get_fab(supplier_pos); + if( supplier ) { + new_fab->add_supplier(supplier); + } + } + } + } } } INT_CHECK("simworld 1278"); From 3da06004600eca3f4417aee1e0b2c11bb90dcb9a Mon Sep 17 00:00:00 2001 From: simu_poppo Date: Sat, 30 May 2026 23:56:27 +0900 Subject: [PATCH 4/4] fix build error --- simworld.cc | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/simworld.cc b/simworld.cc index 3a2262a7221..6058d666596 100644 --- a/simworld.cc +++ b/simworld.cc @@ -3941,12 +3941,8 @@ void karte_t::new_month() const uint8 old_rot = gb ? gb->get_tile()->get_layout() : 0; // Save connection lists before the factory is destroyed. - vector_tpl old_lieferziele; - vector_tpl old_suppliers; - if( upgrade_desc ) { - old_lieferziele = fab->get_lieferziele(); - old_suppliers = fab->get_suppliers(); - } + vector_tpl old_lieferziele( upgrade_desc ? fab->get_lieferziele() : vector_tpl() ); + vector_tpl old_suppliers( upgrade_desc ? fab->get_suppliers() : vector_tpl() ); hausbauer_t::remove(get_public_player(), gb); // fab pointer is invalid from here on.