diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj index 59716882a3..da52f330b7 100644 --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -1183,6 +1183,10 @@ RelativePath=".\..\src\newgrf_config.h" > + + @@ -1851,6 +1855,10 @@ RelativePath=".\..\src\network\network_gui.cpp" > + + diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj index 1454737513..6d0e652b70 100644 --- a/projects/openttd_vs90.vcproj +++ b/projects/openttd_vs90.vcproj @@ -1180,6 +1180,10 @@ RelativePath=".\..\src\newgrf_config.h" > + + @@ -1848,6 +1852,10 @@ RelativePath=".\..\src\network\network_gui.cpp" > + + diff --git a/source.list b/source.list index ea94146449..b32af81765 100644 --- a/source.list +++ b/source.list @@ -205,6 +205,7 @@ newgrf_canal.h newgrf_cargo.h newgrf_commons.h newgrf_config.h +newgrf_debug.h newgrf_engine.h newgrf_generic.h newgrf_house.h @@ -389,6 +390,7 @@ music_gui.cpp network/network_chat_gui.cpp network/network_content_gui.cpp network/network_gui.cpp +newgrf_debug_gui.cpp newgrf_gui.cpp news_gui.cpp order_gui.cpp diff --git a/src/lang/english.txt b/src/lang/english.txt index 09acfe9074..ba1fedece1 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2365,6 +2365,16 @@ STR_NEWGRF_ADD_FILE_TOOLTIP :{BLACK}Add the STR_NEWGRF_ADD_RESCAN_FILES :{BLACK}Rescan files STR_NEWGRF_ADD_RESCAN_FILES_TOOLTIP :{BLACK}Update the list of available NewGRF files +# NewGRF inspect window +STR_NEWGRF_INSPECT_CAPTION :{WHITE}Inspect - {STRING5} +STR_NEWGRF_INSPECT_PARENT_BUTTON :{BLACK}Parent +STR_NEWGRF_INSPECT_PARENT_TOOLTIP :{BLACK}Inspect the object of the parent scope + +STR_NEWGRF_INSPECT_CAPTION_OBJECT_AT :{STRING1} at {HEX} +STR_NEWGRF_INSPECT_CAPTION_OBJECT_AT_RAIL_TYPE :Rail type + +STR_NEWGRF_INSPECT_QUERY_CAPTION :{WHITE}NewGRF variable 60+x parameter (hexadecimal) + # NewGRF (self) generated warnings/errors STR_NEWGRF_ERROR_MSG_INFO :{SILVER}{RAW_STRING} STR_NEWGRF_ERROR_MSG_WARNING :{RED}Warning: {SILVER}{RAW_STRING} diff --git a/src/newgrf.h b/src/newgrf.h index 3f61402cb2..688f6c0b2e 100644 --- a/src/newgrf.h +++ b/src/newgrf.h @@ -60,6 +60,11 @@ enum GrfSpecFeature { GSF_RAILTYPES, GSF_AIRPORTTILES, GSF_END, + + GSF_FAKE_TOWNS = GSF_END, ///< Fake town GrfSpecFeature for NewGRF debugging (parent scope) + GSF_FAKE_END, ///< End of the fake features + + GSF_INVALID = 0xFF ///< An invalid spec feature }; static const uint32 INVALID_GRFID = 0xFFFFFFFF; diff --git a/src/newgrf_debug.h b/src/newgrf_debug.h new file mode 100644 index 0000000000..91f504a939 --- /dev/null +++ b/src/newgrf_debug.h @@ -0,0 +1,60 @@ +/* $Id$ */ + +/* + * 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_debug.h Functions/types related to NewGRF debugging. */ + +#ifndef NEWGRF_DEBUG_H +#define NEWGRF_DEBUG_H + +#include "newgrf.h" + +/** + * Can we inspect the data given a certain feature and index. + * The index is normally an in-game location/identifier, such + * as a TileIndex or an IndustryID depending on the feature + * we want to inspect. + * @param feature The feature we want to inspect. + * @param index The index/identifier of the feature to inspect. + * @return true if there is something to show. + */ +bool IsNewGRFInspectable(GrfSpecFeature feature, uint index); + +/** + * Show the inspect window for a given feature and index. + * The index is normally an in-game location/identifier, such + * as a TileIndex or an IndustryID depending on the feature + * we want to inspect. + * @param feature The feature we want to inspect. + * @param index The index/identifier of the feature to inspect. + */ +void ShowNewGRFInspectWindow(GrfSpecFeature feature, uint index); + +/** + * Delete inspect window for a given feature and index. + * The index is normally an in-game location/identifier, such + * as a TileIndex or an IndustryID depending on the feature + * we want to inspect. + * @param feature The feature we want to delete the window for. + * @param index The index/identifier of the feature to delete. + */ +void DeleteNewGRFInspectWindow(GrfSpecFeature feature, uint index); + +/** + * Get the GrfSpecFeature associated with the tile. + * @return the GrfSpecFeature. + */ +GrfSpecFeature GetGrfSpecFeature(TileIndex tile); + +/** + * Get the GrfSpecFeature associated with the vehicle. + * @return the GrfSpecFeature. + */ +GrfSpecFeature GetGrfSpecFeature(VehicleType type); + +#endif /* NEWGRF_DEBUG_H */ diff --git a/src/newgrf_debug_gui.cpp b/src/newgrf_debug_gui.cpp new file mode 100644 index 0000000000..f40a11e543 --- /dev/null +++ b/src/newgrf_debug_gui.cpp @@ -0,0 +1,530 @@ +/* $Id$ */ + +/* + * 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_debug_gui.cpp GUIs for debugging NewGRFs. */ + +#include "stdafx.h" +#include +#include "window_gui.h" +#include "window_func.h" +#include "gfx_func.h" +#include "string_func.h" +#include "strings_func.h" +#include "textbuf_gui.h" +#include "tunnelbridge_map.h" + +#include "engine_base.h" +#include "house.h" +#include "industry.h" +#include "rail.h" +#include "station_base.h" +#include "town.h" +#include "vehicle_base.h" + +#include "newgrf_airporttiles.h" +#include "newgrf_debug.h" +#include "newgrf_spritegroup.h" +#include "newgrf_station.h" +#include "newgrf_town.h" + +#include "table/strings.h" + +/** + * Get the feature index related to the window number. + * @param window_number The window to get the feature index from. + * @return the feature index + */ +static inline uint GetFeatureIndex(uint window_number) +{ + return GB(window_number, 0, 24); +} + +/** + * Get the window number for the inspect window given a + * feature and index. + * @param feature The feature we want to inspect. + * @param index The index/identifier of the feature to inspect. + * @return the InspectWindow (Window)Number + */ +static inline uint GetInspectWindowNumber(GrfSpecFeature feature, uint index) +{ + assert((index >> 24) == 0); + return (feature << 24) | index; +} + +/** + * The type of a property to show. This is used to + * provide an appropriate represenation in the GUI. + */ +enum NIType { + NIT_INT, ///< The property is a simple integer + NIT_CARGO, ///< The property is a cargo +}; + +/** Representation of the data from a NewGRF property. */ +struct NIProperty { + const char *name; ///< A (human readable) name for the property + ptrdiff_t offset; ///< Offset of the variable in the class + byte read_size; ///< Number of bytes (i.e. byte, word, dword etc) + byte prop; ///< The number of the property + byte type; +}; + + +/** + * Representation of the available callbacks with + * information on when they actually apply. + */ +struct NICallback { + const char *name; ///< The human readable name of the callback + ptrdiff_t offset; ///< Offset of the variable in the class + byte read_size; ///< The number of bytes (i.e. byte, word, dword etc) to read + byte cb_bit; ///< The bit that needs to be set for this callback to be enabled + uint16 cb_id; ///< The number of the callback +}; +/** Mask to show no bit needs to be enabled for the callback. */ +static const int CBM_NO_BIT = UINT8_MAX; + +/** Representation on the NewGRF variables. */ +struct NIVariable { + const char *name; + byte var; +}; + +/** Helper class to wrap some functionality/queries in. */ +class NIHelper { +public: + /** + * Is the item with the given index inspectable? + * @param index the index to check. + * @return true iff the index is inspectable. + */ + virtual bool IsInspectable(uint index) const = 0; + + /** + * Get the parent "window_number" of a given instance. + * @param index the instance to get the parent for. + * @return the parent's window_number or UINT32_MAX if there is none. + */ + virtual uint GetParent(uint index) const = 0; + + /** + * Get the instance given an index. + * @param index the index to get the instance for. + * @return the instance. + */ + virtual const void *GetInstance(uint index) const = 0; + + /** + * Get (NewGRF) specs given an index. + * @param index the index to get the specs for for. + * @return the specs. + */ + virtual const void *GetSpec(uint index) const = 0; + + /** + * Set the string parameters to write the right data for a STRINGn. + * @param index the index to get the string parameters for. + */ + virtual void SetStringParameters(uint index) const = 0; + + /** + * Resolve (action2) variable for a given index. + * @param index The (instance) index to resolve the variable for. + * @param var The variable to actually resolve. + * @param param The varaction2 0x60+x parameter to pass. + * @param avail Return whether the variable is available. + * @return The resolved variable's value. + */ + virtual uint Resolve(uint index, uint var, uint param, bool *avail) const + { + ResolverObject ro; + memset(&ro, 0, sizeof(ro)); + this->Resolve(&ro, index); + return ro.GetVariable(&ro, var, param, avail); + } + +protected: + /** + * Actually execute the real resolving for a given (instance) index. + * @param ro The resolver object to fill with everything + * needed to be able to resolve a variable. + * @param index The (instance) index of the to-be-resolved variable. + */ + virtual void Resolve(ResolverObject *ro, uint index) const {} + + /** + * Helper to make setting the strings easier. + * @param string the string to actually draw. + * @param index the (instance) index for the string. + */ + void SetSimpleStringParameters(StringID string, uint32 index) const + { + SetDParam(0, string); + SetDParam(1, index); + } + + + /** + * Helper to make setting the strings easier for objects at a specific tile. + * @param string the string to draw the object's name + * @param index the (instance) index for the string. + * @param tile the tile the object is at + */ + void SetObjectAtStringParameters(StringID string, uint32 index, TileIndex tile) const + { + SetDParam(0, STR_NEWGRF_INSPECT_CAPTION_OBJECT_AT); + SetDParam(1, string); + SetDParam(2, index); + SetDParam(3, tile); + } +}; + + +/** Container for all information for a given feature. */ +struct NIFeature { + const NIProperty *properties; ///< The properties associated with this feature. + const NICallback *callbacks; ///< The callbacks associated with this feature. + const NIVariable *variables; ///< The variables associated with this feature. + const NIHelper *helper; ///< The class container all helper functions. + uint psa_size; ///< The size of the persistent storage in indices. + size_t psa_offset; ///< Offset to the array in the PSA. +}; + +/* Load all the NewGRF debug data; externalised as it is just a huge bunch of tables. */ +#include "table/newgrf_debug_data.h" + +/** + * Get the NIFeature related to the window number. + * @param window_number The window to get the NIFeature for. + * @return the NIFeature, or NULL is there isn't one. + */ +static inline const NIFeature *GetFeature(uint window_number) +{ + byte idx = GB(window_number, 24, 8); + return idx < GSF_FAKE_END ? _nifeatures[idx] : NULL; +} + +/** + * Get the NIHelper related to the window number. + * @param window_number The window to get the NIHelper for. + * @pre GetFeature(window_number) != NULL + * @return the NIHelper + */ +static inline const NIHelper *GetFeatureHelper(uint window_number) +{ + return GetFeature(window_number)->helper; +} + + +/** Widget numbers of settings window */ +enum NewGRFInspectWidgets { + NIW_CAPTION, ///< The caption bar ofcourse + NIW_PARENT, ///< Inspect the parent + NIW_MAINPANEL, ///< Panel widget containing the actual data + NIW_SCROLLBAR, ///< Scrollbar +}; + +/** Window used for inspecting NewGRFs. */ +struct NewGRFInspectWindow : Window { + static const int LEFT_OFFSET = 5; ///< Position of left edge + static const int RIGHT_OFFSET = 5; ///< Position of right edge + static const int TOP_OFFSET = 5; ///< Position of top edge + static const int BOTTOM_OFFSET = 5; ///< Position of bottom edge + + /** The value for the variable 60 parameters. */ + static byte var60params[0x20]; + + /** The currently editted parameter, to update the right one. */ + byte current_edit_param; + + /** + * Check whether the given variable has a parameter. + * @param variable the variable to check. + * @return true iff the variable has a parameter. + */ + static bool HasVariableParameter(uint variable) + { + return IsInsideBS(variable, 0x60, 0x20); + } + + NewGRFInspectWindow(const WindowDesc *desc, WindowNumber wno) : Window() + { + this->InitNested(desc, wno); + + this->vscroll.SetCount(0); + this->SetWidgetDisabledState(NIW_PARENT, GetFeatureHelper(this->window_number)->GetParent(GetFeatureIndex(this->window_number)) == UINT32_MAX); + } + + virtual void SetStringParameters(int widget) const + { + if (widget != NIW_CAPTION) return; + + GetFeatureHelper(this->window_number)->SetStringParameters(GetFeatureIndex(this->window_number)); + } + + virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) + { + if (widget != NIW_MAINPANEL) return; + + resize->height = max(11, FONT_HEIGHT_NORMAL + 1); + resize->width = 1; + + size->height = 5 * resize->height + TOP_OFFSET + BOTTOM_OFFSET; + } + + /** + * Helper function to draw a string (line) in the window. + * @param r The (screen) rectangle we must draw within + * @param offset The offset (in lines) we want to draw for + * @param format The format string + */ + void WARN_FORMAT(4, 5) DrawString(const Rect &r, int offset, const char *format, ...) const + { + char buf[1024]; + + va_list va; + va_start(va, format); + vsnprintf(buf, lengthof(buf), format, va); + va_end(va); + + offset -= this->vscroll.GetPosition(); + if (offset < 0 || offset >= this->vscroll.GetCapacity()) return; + + ::DrawString(r.left + LEFT_OFFSET, r.right + RIGHT_OFFSET, r.top + TOP_OFFSET + (offset * this->resize.step_height), buf, TC_BLACK); + } + + virtual void DrawWidget(const Rect &r, int widget) const + { + if (widget != NIW_MAINPANEL) return; + + uint index = GetFeatureIndex(this->window_number); + const NIFeature *nif = GetFeature(this->window_number); + const NIHelper *nih = nif->helper; + const void *base = nih->GetInstance(index); + const void *base_spec = nih->GetSpec(index); + + uint i = 0; + if (nif->variables != NULL) { + this->DrawString(r, i++, "Variables:"); + for (const NIVariable *niv = nif->variables; niv->name != NULL; niv++) { + bool avail = true; + uint param = HasVariableParameter(niv->var) ? NewGRFInspectWindow::var60params[niv->var - 0x60] : 0; + uint value = nih->Resolve(index, niv->var, param, &avail); + + if (!avail) continue; + + if (HasVariableParameter(niv->var)) { + this->DrawString(r, i++, " %02x[%02x]: %08x (%s)", niv->var, param, value, niv->name); + } else { + this->DrawString(r, i++, " %02x: %08x (%s)", niv->var, value, niv->name); + } + } + } + + if (nif->psa_size != 0) { + this->DrawString(r, i++, "Persistent storage:"); + assert(nif->psa_size % 4 == 0); + int32 *psa = (int32*)((byte*)base + nif->psa_offset); + for (uint j = 0; j < nif->psa_size; j += 4, psa += 4) { + this->DrawString(r, i++, " %i: %i %i %i %i", j, psa[0], psa[1], psa[2], psa[3]); + } + } + + if (nif->properties != NULL) { + this->DrawString(r, i++, "Properties:"); + for (const NIProperty *nip = nif->properties; nip->name != NULL; nip++) { + void *ptr = (byte*)base + nip->offset; + uint value; + switch (nip->read_size) { + case 1: value = *(uint8 *)ptr; break; + case 2: value = *(uint16 *)ptr; break; + case 4: value = *(uint32 *)ptr; break; + default: NOT_REACHED(); + } + + StringID string; + SetDParam(0, value); + switch (nip->type) { + case NIT_INT: + string = STR_JUST_INT; + break; + + case NIT_CARGO: + string = value != INVALID_CARGO ? CargoSpec::Get(value)->name : STR_QUANTITY_N_A; + break; + + default: + NOT_REACHED(); + } + + char buffer[64]; + GetString(buffer, string, lastof(buffer)); + this->DrawString(r, i++, " %02x: %s (%s)", nip->prop, buffer, nip->name); + } + } + + if (nif->callbacks != NULL) { + this->DrawString(r, i++, "Callbacks:"); + for (const NICallback *nic = nif->callbacks; nic->name != NULL; nic++) { + if (nic->cb_bit != CBM_NO_BIT) { + void *ptr = (byte*)base_spec + nic->offset; + uint value; + switch (nic->read_size) { + case 1: value = *(uint8 *)ptr; break; + case 2: value = *(uint16 *)ptr; break; + case 4: value = *(uint32 *)ptr; break; + default: NOT_REACHED(); + } + + if (!HasBit(value, nic->cb_bit)) continue; + this->DrawString(r, i++, " %03x: %s", nic->cb_id, nic->name); + } else { + this->DrawString(r, i++, " %03x: %s (unmasked)", nic->cb_id, nic->name); + } + } + } + + /* Not nice and certainly a hack, but it beats duplicating + * this whole function just to count the actual number of + * elements. Especially because they need to be redrawn. */ + const_cast(this)->vscroll.SetCount(i); + } + + virtual void OnPaint() + { + this->DrawWidgets(); + } + + virtual void OnClick(Point pt, int widget, int click_count) + { + switch (widget) { + case NIW_PARENT: { + uint index = GetFeatureHelper(this->window_number)->GetParent(GetFeatureIndex(this->window_number)); + ::ShowNewGRFInspectWindow((GrfSpecFeature)GB(index, 24, 8), GetFeatureIndex(index)); + } break; + + case NIW_MAINPANEL: { + /* Does this feature have variables? */ + const NIFeature *nif = GetFeature(this->window_number); + if (nif->variables == NULL) return; + + /* Get the line, make sure it's within the boundaries. */ + int line = (pt.y - this->GetWidget(NIW_MAINPANEL)->pos_y - TOP_OFFSET) / this->resize.step_height; + if (line >= this->vscroll.GetCapacity()) return; + line += this->vscroll.GetPosition(); + + /* Find the variable related to the line */ + for (const NIVariable *niv = nif->variables; niv->name != NULL; niv++, line--) { + if (line != 1) continue; // 1 because of the "Variables:" line + + if (!HasVariableParameter(niv->var)) break; + + this->current_edit_param = niv->var; + ShowQueryString(STR_EMPTY, STR_NEWGRF_INSPECT_QUERY_CAPTION, 3, 100, this, CS_HEXADECIMAL, QSF_NONE); + } + } + } + } + + virtual void OnQueryTextFinished(char *str) + { + if (StrEmpty(str)) return; + + NewGRFInspectWindow::var60params[this->current_edit_param - 0x60] = strtol(str, NULL, 16); + this->SetDirty(); + } + + virtual void OnResize() + { + this->vscroll.SetCapacityFromWidget(this, NIW_MAINPANEL, TOP_OFFSET + BOTTOM_OFFSET); + } +}; + +/* static */ byte NewGRFInspectWindow::var60params[0x20] = { 0 }; // Use spec to have 0s in whole array + +static const NWidgetPart _nested_newgrf_inspect_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_GREY), + NWidget(WWT_CAPTION, COLOUR_GREY, NIW_CAPTION), SetDataTip(STR_NEWGRF_INSPECT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, NIW_PARENT), SetDataTip(STR_NEWGRF_INSPECT_PARENT_BUTTON, STR_NEWGRF_INSPECT_PARENT_TOOLTIP), + NWidget(WWT_SHADEBOX, COLOUR_GREY), + NWidget(WWT_STICKYBOX, COLOUR_GREY), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PANEL, COLOUR_GREY, NIW_MAINPANEL), SetMinimalSize(300, 0), EndContainer(), + NWidget(NWID_VERTICAL), + NWidget(WWT_SCROLLBAR, COLOUR_GREY, NIW_SCROLLBAR), + NWidget(WWT_RESIZEBOX, COLOUR_GREY), + EndContainer(), + EndContainer(), +}; + +static const WindowDesc _newgrf_inspect_desc( + WDP_AUTO, 400, 300, + WC_NEWGRF_INSPECT, WC_NONE, + WDF_UNCLICK_BUTTONS, + _nested_newgrf_inspect_widgets, lengthof(_nested_newgrf_inspect_widgets) +); + +void ShowNewGRFInspectWindow(GrfSpecFeature feature, uint index) +{ + if (!IsNewGRFInspectable(feature, index)) return; + + WindowNumber wno = GetInspectWindowNumber(feature, index); + AllocateWindowDescFront(&_newgrf_inspect_desc, wno); +} + +void DeleteNewGRFInspectWindow(GrfSpecFeature feature, uint index) +{ + if (feature == GSF_INVALID) return; + + WindowNumber wno = GetInspectWindowNumber(feature, index); + DeleteWindowById(WC_NEWGRF_INSPECT, wno); + + /* Reinitialise the land information window to remove the "debug" sprite if needed. */ + Window *w = FindWindowById(WC_LAND_INFO, 0); + if (w != NULL) w->ReInit(); +} + +bool IsNewGRFInspectable(GrfSpecFeature feature, uint index) +{ + const NIFeature *nif = GetFeature(GetInspectWindowNumber(feature, index)); + if (nif == NULL) return false; + return nif->helper->IsInspectable(index); +} + +GrfSpecFeature GetGrfSpecFeature(TileIndex tile) +{ + switch (GetTileType(tile)) { + default: return GSF_INVALID; + case MP_RAILWAY: return GSF_RAILTYPES; + case MP_ROAD: return IsLevelCrossing(tile) ? GSF_RAILTYPES : GSF_INVALID; + case MP_HOUSE: return GSF_HOUSES; + case MP_INDUSTRY: return GSF_INDUSTRYTILES; + + case MP_STATION: + switch (GetStationType(tile)) { + case STATION_RAIL: return GSF_STATIONS; + case STATION_AIRPORT: return GSF_AIRPORTTILES; + default: return GSF_INVALID; + } + } +} + +GrfSpecFeature GetGrfSpecFeature(VehicleType type) +{ + switch (type) { + case VEH_TRAIN: return GSF_TRAINS; + case VEH_ROAD: return GSF_ROADVEHICLES; + case VEH_SHIP: return GSF_SHIPS; + case VEH_AIRCRAFT: return GSF_AIRCRAFT; + default: return GSF_INVALID; + } +} diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h new file mode 100644 index 0000000000..99ba723373 --- /dev/null +++ b/src/table/newgrf_debug_data.h @@ -0,0 +1,426 @@ +/* $Id$ */ + +/* + * 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_debug_data.cpp Data 'tables' for NewGRF debugging. */ + +/* Helper for filling property tables */ +#define NIP(prop, base, variable, type, name) { name, cpp_offsetof(base, variable), cpp_sizeof(base, variable), prop, type } +#define NIP_END() { NULL, 0, 0, 0, 0 } + +/* Helper for filling callback tables */ +#define NIC(cb_id, base, variable, bit) { #cb_id, cpp_offsetof(base, variable), cpp_sizeof(base, variable), bit, cb_id } +#define NIC_END() { NULL, 0, 0, 0, 0 } + +/* Helper for filling variable tables */ +#define NIV(var, name) { name, var } +#define NIV_END() { NULL, 0 } + + +/*** NewGRF Vehicles ***/ + +#define NICV(cb_id, bit) NIC(cb_id, Engine, info.callback_mask, bit) +static const NICallback _nic_vehicles[] = { + NICV(CBID_TRAIN_WAGON_POWER, CBM_TRAIN_WAGON_POWER), + NICV(CBID_VEHICLE_LENGTH, CBM_VEHICLE_LENGTH), + NICV(CBID_VEHICLE_LOAD_AMOUNT, CBM_VEHICLE_LOAD_AMOUNT), + NICV(CBID_VEHICLE_REFIT_CAPACITY, CBM_VEHICLE_REFIT_CAPACITY), + NICV(CBID_VEHICLE_ARTIC_ENGINE, CBM_VEHICLE_ARTIC_ENGINE), + NICV(CBID_VEHICLE_CARGO_SUFFIX, CBM_VEHICLE_CARGO_SUFFIX), + NICV(CBID_TRAIN_ALLOW_WAGON_ATTACH, CBM_NO_BIT), + NICV(CBID_VEHICLE_ADDITIONAL_TEXT, CBM_NO_BIT), + NICV(CBID_VEHICLE_COLOUR_MAPPING, CBM_VEHICLE_COLOUR_REMAP), + NICV(CBID_VEHICLE_START_STOP_CHECK, CBM_NO_BIT), + NICV(CBID_VEHICLE_32DAY_CALLBACK, CBM_NO_BIT), + NICV(CBID_VEHICLE_SOUND_EFFECT, CBM_VEHICLE_SOUND_EFFECT), + NICV(CBID_VEHICLE_AUTOREPLACE_SELECTION, CBM_NO_BIT), + NICV(CBID_VEHICLE_MODIFY_PROPERTY, CBM_NO_BIT), + NIC_END() +}; + + +static const NIVariable _niv_vehicles[] = { + NIV(0x40, "position in consist and length"), + NIV(0x41, "position and length of chain of same vehicles"), + NIV(0x42, "transported cargo types"), + NIV(0x43, "player info"), + NIV(0x44, "aircraft info"), + NIV(0x45, "curvature info"), + NIV(0x46, "motion counter"), + NIV(0x47, "vehicle cargo info"), + NIV(0x48, "vehicle type info"), + NIV(0x49, "year of construction"), + NIV(0x60, "count vehicle id occurrences"), + NIV_END() +}; + +class NIHVehicle : public NIHelper { + bool IsInspectable(uint index) const { return Engine::Get(Vehicle::Get(index)->engine_type)->grffile != NULL; } + uint GetParent(uint index) const { const Vehicle *first = Vehicle::Get(index)->First(); return GetInspectWindowNumber(GetGrfSpecFeature(first->type), first->index); } + const void *GetInstance(uint index)const { return Vehicle::Get(index); } + const void *GetSpec(uint index) const { return Engine::Get(Vehicle::Get(index)->engine_type); } + void SetStringParameters(uint index) const { this->SetSimpleStringParameters(STR_VEHICLE_NAME, index); } + void Resolve(ResolverObject *ro, uint32 index) const { extern void GetVehicleResolver(ResolverObject *ro, uint index); GetVehicleResolver(ro, index); } +}; + +static const NIFeature _nif_vehicle = { + NULL, + _nic_vehicles, + _niv_vehicles, + new NIHVehicle(), + 0, + 0 +}; + + +/*** NewGRF station (tiles) ***/ + +#define NICS(cb_id, bit) NIC(cb_id, StationSpec, callback_mask, bit) +static const NICallback _nic_stations[] = { + NICS(CBID_STATION_AVAILABILITY, CBM_STATION_AVAIL), + NICS(CBID_STATION_SPRITE_LAYOUT, CBM_NO_BIT), + NICS(CBID_STATION_TILE_LAYOUT, CBM_STATION_SPRITE_LAYOUT), + NICS(CBID_STATION_ANIM_START_STOP, CBM_NO_BIT), + NICS(CBID_STATION_ANIM_NEXT_FRAME, CBM_STATION_ANIMATION_NEXT_FRAME), + NICS(CBID_STATION_ANIMATION_SPEED, CBM_STATION_ANIMATION_SPEED), + NICS(CBID_STATION_LAND_SLOPE_CHECK, CBM_STATION_SLOPE_CHECK), + NIC_END() +}; + +static const NIVariable _niv_stations[] = { + NIV(0x40, "platform info and relative position"), + NIV(0x41, "platform info and relative position for individually built sections"), + NIV(0x42, "terrain and track type"), + NIV(0x43, "player info"), + NIV(0x44, "path signalling info"), + NIV(0x45, "rail continuation info"), + NIV(0x46, "platform info and relative position from middle"), + NIV(0x47, "platform info and relative position from middle for individually built sections"), + NIV(0x48, "bitmask of accepted cargoes"), + NIV(0x49, "platform info and relative position of same-direction section"), + NIV(0x4A, "current animation frame"), + NIV(0x60, "amount of cargo waiting"), + NIV(0x61, "time since last cargo pickup"), + NIV(0x62, "rating of cargo"), + NIV(0x63, "time spent on route"), + NIV(0x64, "information about last vehicle picking cargo up"), + NIV(0x65, "amount of cargo acceptance"), + NIV(0x66, "animation frame of nearby tile"), + NIV(0x67, "land info of nearby tiles"), + NIV(0x68, "station info of nearby tiles"), + NIV(0x69, "information about cargo accepted in the past"), + NIV_END() +}; + +class NIHStation : public NIHelper { + bool IsInspectable(uint index) const { return GetStationSpec(index) != NULL; } + uint GetParent(uint index) const { return GetInspectWindowNumber(GSF_FAKE_TOWNS, Station::GetByTile(index)->town->index); } + const void *GetInstance(uint index)const { return NULL; } + const void *GetSpec(uint index) const { return GetStationSpec(index); } + void SetStringParameters(uint index) const { this->SetObjectAtStringParameters(STR_STATION_NAME, GetStationIndex(index), index); } + void Resolve(ResolverObject *ro, uint32 index) const { extern void GetStationResolver(ResolverObject *ro, uint index); GetStationResolver(ro, index); } +}; + +static const NIFeature _nif_station = { + NULL, + _nic_stations, + _niv_stations, + new NIHStation(), + 0, + 0 +}; + + +/*** NewGRF house tiles ***/ + +#define NICH(cb_id, bit) NIC(cb_id, HouseSpec, callback_mask, bit) +static const NICallback _nic_house[] = { + NICH(CBID_HOUSE_ALLOW_CONSTRUCTION, CBM_HOUSE_ALLOW_CONSTRUCTION), + NICH(CBID_HOUSE_ANIMATION_NEXT_FRAME, CBM_HOUSE_ANIMATION_NEXT_FRAME), + NICH(CBID_HOUSE_ANIMATION_START_STOP, CBM_HOUSE_ANIMATION_START_STOP), + NICH(CBID_HOUSE_CONSTRUCTION_STATE_CHANGE, CBM_HOUSE_CONSTRUCTION_STATE_CHANGE), + NICH(CBID_HOUSE_COLOUR, CBM_HOUSE_COLOUR), + NICH(CBID_HOUSE_CARGO_ACCEPTANCE, CBM_HOUSE_CARGO_ACCEPTANCE), + NICH(CBID_HOUSE_ANIMATION_SPEED, CBM_HOUSE_ANIMATION_SPEED), + NICH(CBID_HOUSE_DESTRUCTION, CBM_HOUSE_DESTRUCTION), + NICH(CBID_HOUSE_ACCEPT_CARGO, CBM_HOUSE_ACCEPT_CARGO), + NICH(CBID_HOUSE_PRODUCE_CARGO, CBM_HOUSE_PRODUCE_CARGO), + NICH(CBID_HOUSE_DENY_DESTRUCTION, CBM_HOUSE_DENY_DESTRUCTION), + NICH(CBID_HOUSE_WATCHED_CARGO_ACCEPTED, CBM_NO_BIT), + NICH(CBID_HOUSE_CUSTOM_NAME, CBM_NO_BIT), + NICH(CBID_HOUSE_DRAW_FOUNDATIONS, CBM_HOUSE_DRAW_FOUNDATIONS), + NICH(CBID_HOUSE_AUTOSLOPE, CBM_HOUSE_AUTOSLOPE), + NIC_END() +}; + +static const NIVariable _niv_house[] = { + NIV(0x40, "construction state of tile and pseudo-random value"), + NIV(0x41, "age of building in years"), + NIV(0x42, "town zone"), + NIV(0x43, "terrain type"), + NIV(0x44, "building counts"), + NIV(0x45, "town expansion bits"), + NIV(0x46, "current animation frame"), + NIV(0x47, "xy coordinate of the building"), + NIV(0x60, "other building counts (old house type)"), + NIV(0x61, "other building counts (new house type)"), + NIV(0x62, "land info of nearby tiles"), + NIV(0x63, "current animation frame of nearby house tile"), + NIV(0x64, "cargo acceptance history of nearby stations"), + NIV(0x65, "distance of nearest house matching a given criterion"), + NIV(0x66, "class and ID of nearby house tile"), + NIV(0x67, "GRFID of nearby house tile"), + NIV_END() +}; + +class NIHHouse : public NIHelper { + bool IsInspectable(uint index) const { return HouseSpec::Get(GetHouseType(index))->grffile != NULL; } + uint GetParent(uint index) const { return GetInspectWindowNumber(GSF_FAKE_TOWNS, GetTownIndex(index)); } + const void *GetInstance(uint index)const { return NULL; } + const void *GetSpec(uint index) const { return HouseSpec::Get(GetHouseType(index)); } + void SetStringParameters(uint index) const { this->SetObjectAtStringParameters(STR_TOWN_NAME, GetTownIndex(index), index); } + void Resolve(ResolverObject *ro, uint32 index) const { extern void GetHouseResolver(ResolverObject *ro, uint index); GetHouseResolver(ro, index); } +}; + +static const NIFeature _nif_house = { + NULL, + _nic_house, + _niv_house, + new NIHHouse(), + 0, + 0 +}; + + +/*** NewGRF industry tiles ***/ + +#define NICIT(cb_id, bit) NIC(cb_id, IndustryTileSpec, callback_mask, bit) +static const NICallback _nic_industrytiles[] = { + NICIT(CBID_INDTILE_ANIM_START_STOP, CBM_NO_BIT), + NICIT(CBID_INDTILE_ANIM_NEXT_FRAME, CBM_INDT_ANIM_NEXT_FRAME), + NICIT(CBID_INDTILE_ANIMATION_SPEED, CBM_INDT_ANIM_SPEED), + NICIT(CBID_INDTILE_CARGO_ACCEPTANCE, CBM_INDT_CARGO_ACCEPTANCE), + NICIT(CBID_INDTILE_ACCEPT_CARGO, CBM_INDT_ACCEPT_CARGO), + NICIT(CBID_INDTILE_SHAPE_CHECK, CBM_INDT_SHAPE_CHECK), + NICIT(CBID_INDTILE_DRAW_FOUNDATIONS, CBM_INDT_DRAW_FOUNDATIONS), + NICIT(CBID_INDTILE_AUTOSLOPE, CBM_INDT_AUTOSLOPE), + NIC_END() +}; + +static const NIVariable _niv_industrytiles[] = { + NIV(0x40, "construction state of tile"), + NIV(0x41, "ground type"), + NIV(0x42, "current town zone in nearest town"), + NIV(0x43, "relative position"), + NIV(0x44, "animation frame"), + NIV(0x60, "land info of nearby tiles"), + NIV(0x61, "animation stage of nearby tiles"), + NIV(0x62, "get industry or airport tile ID at offset"), + NIV_END() +}; + +class NIHIndustryTile : public NIHelper { + bool IsInspectable(uint index) const { return GetIndustryTileSpec(GetIndustryGfx(index))->grf_prop.grffile != NULL; } + uint GetParent(uint index) const { return GetInspectWindowNumber(GSF_INDUSTRIES, GetIndustryIndex(index)); } + const void *GetInstance(uint index)const { return NULL; } + const void *GetSpec(uint index) const { return GetIndustryTileSpec(GetIndustryGfx(index)); } + void SetStringParameters(uint index) const { this->SetObjectAtStringParameters(STR_INDUSTRY_NAME, GetIndustryIndex(index), index); } + void Resolve(ResolverObject *ro, uint32 index) const { extern void GetIndustryTileResolver(ResolverObject *ro, uint index); GetIndustryTileResolver(ro, index); } +}; + +static const NIFeature _nif_industrytile = { + NULL, + _nic_industrytiles, + _niv_industrytiles, + new NIHIndustryTile(), + 0, + 0 +}; + + +/*** NewGRF industries ***/ + +static const NIProperty _nip_industries[] = { + NIP(0x10, Industry, produced_cargo[0], NIT_CARGO, "produced cargo 0"), + NIP(0x10, Industry, produced_cargo[1], NIT_CARGO, "produced cargo 1"), + NIP(0x11, Industry, accepts_cargo[0], NIT_CARGO, "accepted cargo 0"), + NIP(0x11, Industry, accepts_cargo[1], NIT_CARGO, "accepted cargo 1"), + NIP(0x11, Industry, accepts_cargo[2], NIT_CARGO, "accepted cargo 2"), + NIP_END() +}; + +#define NICI(cb_id, bit) NIC(cb_id, IndustrySpec, callback_mask, bit) +static const NICallback _nic_industries[] = { + NICI(CBID_INDUSTRY_AVAILABLE, CBM_IND_AVAILABLE), + NICI(CBID_INDUSTRY_LOCATION, CBM_IND_LOCATION), + NICI(CBID_INDUSTRY_PRODUCTION_CHANGE, CBM_IND_PRODUCTION_CHANGE), + NICI(CBID_INDUSTRY_MONTHLYPROD_CHANGE, CBM_IND_MONTHLYPROD_CHANGE), + NICI(CBID_INDUSTRY_CARGO_SUFFIX, CBM_IND_CARGO_SUFFIX), + NICI(CBID_INDUSTRY_FUND_MORE_TEXT, CBM_IND_FUND_MORE_TEXT), + NICI(CBID_INDUSTRY_WINDOW_MORE_TEXT, CBM_IND_WINDOW_MORE_TEXT), + NICI(CBID_INDUSTRY_SPECIAL_EFFECT, CBM_IND_SPECIAL_EFFECT), + NICI(CBID_INDUSTRY_REFUSE_CARGO, CBM_IND_REFUSE_CARGO), + NICI(CBID_INDUSTRY_DECIDE_COLOUR, CBM_IND_DECIDE_COLOUR), + NICI(CBID_INDUSTRY_INPUT_CARGO_TYPES, CBM_IND_INPUT_CARGO_TYPES), + NICI(CBID_INDUSTRY_OUTPUT_CARGO_TYPES, CBM_IND_OUTPUT_CARGO_TYPES), + NIC_END() +}; + +static const NIVariable _niv_industries[] = { + NIV(0x40, "waiting cargo 0"), + NIV(0x41, "waiting cargo 1"), + NIV(0x42, "waiting cargo 2"), + NIV(0x43, "distance to closest dry/land tile"), + NIV(0x44, "layout number"), + NIV(0x45, "player info"), + NIV(0x46, "industry construction date"), + NIV(0x60, "get industry tile ID at offset"), + NIV(0x61, "get random tile bits at offset"), + NIV(0x62, "land info of nearby tiles"), + NIV(0x63, "animation stage of nearby tiles"), + NIV(0x64, "distance on nearest industry with given type"), + NIV(0x65, "get town zone and Manhattan distance of closest town"), + NIV(0x66, "get square of Euclidean distance of closes town"), + NIV(0x67, "count of industry and distance of closest instance"), + NIV(0x68, "count of industry and distance of closest instance with layout filter"), + NIV_END() +}; + +class NIHIndustry : public NIHelper { + bool IsInspectable(uint index) const { return GetIndustrySpec(Industry::Get(index)->type)->grf_prop.grffile != NULL; } + uint GetParent(uint index) const { return GetInspectWindowNumber(GSF_FAKE_TOWNS, Industry::Get(index)->town->index); } + const void *GetInstance(uint index)const { return Industry::Get(index); } + const void *GetSpec(uint index) const { return GetIndustrySpec(Industry::Get(index)->type); } + void SetStringParameters(uint index) const { this->SetSimpleStringParameters(STR_INDUSTRY_NAME, index); } + void Resolve(ResolverObject *ro, uint32 index) const { extern void GetIndustryResolver(ResolverObject *ro, uint index); GetIndustryResolver(ro, index); } +}; + +static const NIFeature _nif_industry = { + _nip_industries, + _nic_industries, + _niv_industries, + new NIHIndustry(), + cpp_lengthof(Industry, psa.storage), + cpp_offsetof(Industry, psa.storage) +}; + + +/*** NewGRF rail types ***/ + +static const NIVariable _niv_railtypes[] = { + NIV(0x40, "terrain type"), + NIV(0x41, "enhanced tunnels"), + NIV(0x42, "level crossing status"), + NIV_END() +}; + +class NIHRailType : public NIHelper { + bool IsInspectable(uint index) const { return true; } + uint GetParent(uint index) const { return UINT32_MAX; } + const void *GetInstance(uint index)const { return NULL; } + const void *GetSpec(uint index) const { return NULL; } + void SetStringParameters(uint index) const { this->SetObjectAtStringParameters(STR_NEWGRF_INSPECT_CAPTION_OBJECT_AT_RAIL_TYPE, INVALID_STRING_ID, index); } + void Resolve(ResolverObject *ro, uint32 index) const { extern void GetRailTypeResolver(ResolverObject *ro, uint index); GetRailTypeResolver(ro, index); } +}; + +static const NIFeature _nif_railtype = { + NULL, + NULL, + _niv_railtypes, + new NIHRailType(), + 0, + 0 +}; + + +/*** NewGRF airport tiles ***/ + +#define NICAT(cb_id, bit) NIC(cb_id, AirportTileSpec, callback_flags, bit) +static const NICallback _nic_airporttiles[] = { + NICAT(CBID_AIRPTILE_DRAW_FOUNDATIONS, CBM_AIRT_DRAW_FOUNDATIONS), + NICAT(CBID_AIRPTILE_ANIM_START_STOP, CBM_NO_BIT), + NICAT(CBID_AIRPTILE_ANIM_NEXT_FRAME, CBM_AIRT_ANIM_NEXT_FRAME), + NICAT(CBID_AIRPTILE_ANIMATION_SPEED, CBM_AIRT_ANIM_SPEED), + NIC_END() +}; + +class NIHAirportTile : public NIHelper { + bool IsInspectable(uint index) const { return AirportTileSpec::Get(GetAirportGfx(index))->grf_prop.grffile != NULL; } + uint GetParent(uint index) const { return GetInspectWindowNumber(GSF_FAKE_TOWNS, Station::GetByTile(index)->town->index); } + const void *GetInstance(uint index)const { return NULL; } + const void *GetSpec(uint index) const { return AirportTileSpec::Get(GetAirportGfx(index)); } + void SetStringParameters(uint index) const { this->SetObjectAtStringParameters(STR_STATION_NAME, GetStationIndex(index), index); } + void Resolve(ResolverObject *ro, uint32 index) const { extern void GetAirportTileTypeResolver(ResolverObject *ro, uint index); GetAirportTileTypeResolver(ro, index); } +}; + +static const NIFeature _nif_airporttile = { + NULL, + _nic_airporttiles, + _niv_industrytiles, // Yes, they share this (at least now) + new NIHAirportTile(), + 0, + 0 +}; + + +/*** NewGRF towns ***/ + +static const NIVariable _niv_towns[] = { + NIV(0x40, "larger town effect on this town"), + NIV(0x41, "town index"), + NIV(0x82, "population"), + NIV(0x94, "zone radius 0"), + NIV(0x96, "zone radius 1"), + NIV(0x98, "zone radius 2"), + NIV(0x9A, "zone radius 3"), + NIV(0x9C, "zone radius 4"), + NIV(0xB6, "number of buildings"), + NIV_END() +}; + +class NIHTown : public NIHelper { + bool IsInspectable(uint index) const { return false; } + uint GetParent(uint index) const { return UINT32_MAX; } + const void *GetInstance(uint index)const { return Town::Get(index); } + const void *GetSpec(uint index) const { return NULL; } + void SetStringParameters(uint index) const { this->SetSimpleStringParameters(STR_TOWN_NAME, index); } + uint Resolve(uint index, uint var, uint param, bool *avail) const { return TownGetVariable(var, param, avail, Town::Get(index)); } +}; + +static const NIFeature _nif_town = { + NULL, + NULL, + _niv_towns, + new NIHTown(), + 0, + 0 +}; + +/** Table with all NIFeatures. */ +static const NIFeature * const _nifeatures[] = { + &_nif_vehicle, // GSF_TRAINS + &_nif_vehicle, // GSF_ROADVEHICLES + &_nif_vehicle, // GSF_SHIPS + &_nif_vehicle, // GSF_AIRCRAFT + &_nif_station, // GSF_STATIONS + NULL, // GSF_CANALS (no callbacks/action2 implemented) + NULL, // GSF_BRIDGES (no callbacks/action2) + &_nif_house, // GSF_HOUSES + NULL, // GSF_GLOBALVAR (has no "physical" objects) + &_nif_industrytile, // GSF_INDUSTRYTILES + &_nif_industry, // GSF_INDUSTRIES + NULL, // GSF_CARGOS (has no "physical" objects) + NULL, // GSF_SOUNDFX (has no "physical" objects) + NULL, // GSF_AIRPORTS (feature not implemented) + NULL, // GSF_SIGNALS (feature not implemented) + NULL, // GSF_OBJECTS (feature not implemented) + &_nif_railtype, // GSF_RAILTYPES + &_nif_airporttile, // GSF_AIRPORTTILES + &_nif_town, // GSF_FAKE_TOWNS +}; +assert_compile(lengthof(_nifeatures) == GSF_FAKE_END); diff --git a/src/window_type.h b/src/window_type.h index 7178f930bc..dd6982909e 100644 --- a/src/window_type.h +++ b/src/window_type.h @@ -106,6 +106,7 @@ enum WindowClass { WC_AI_DEBUG, WC_AI_LIST, WC_AI_SETTINGS, + WC_NEWGRF_INSPECT, WC_INVALID = 0xFFFF };