Debug: Add mode to sprite dump window to show unoptimised

pull/393/head
Jonathan G Rennison 2 years ago
parent 22e10462dc
commit 86324e0fda

@ -3056,6 +3056,7 @@ DEF_CONSOLE_CMD(ConMiscDebug)
IConsoleHelp(" 2: MDF_ZONING_RS_WATER_FLOOD_STATE");
IConsoleHelp(" 4: MDF_ZONING_RS_TROPIC_ZONE");
IConsoleHelp(" 8: MDF_ZONING_RS_ANIMATED_TILE");
IConsoleHelp(" 10: MDF_NEWGRF_SG_SAVE_RAW");
return true;
}

@ -45,6 +45,7 @@ enum MiscDebugFlags {
MDF_ZONING_RS_WATER_FLOOD_STATE,
MDF_ZONING_RS_TROPIC_ZONE,
MDF_ZONING_RS_ANIMATED_TILE,
MDF_NEWGRF_SG_SAVE_RAW,
};
extern uint32 _misc_debug_flags;

@ -4216,6 +4216,8 @@ STR_NEWGRF_INSPECT_DUPLICATE :{BLACK}D
STR_NEWGRF_INSPECT_DUPLICATE_TOOLTIP :{BLACK}Duplicate this window
STR_NEWGRF_INSPECT_SPRITE_DUMP :{BLACK}S
STR_NEWGRF_INSPECT_SPRITE_DUMP_TOOLTIP :{BLACK}Display current sprite chain
STR_NEWGRF_INSPECT_SPRITE_DUMP_UNOPT :{BLACK}U
STR_NEWGRF_INSPECT_SPRITE_DUMP_UNOPT_TOOLTIP :{BLACK}Display sprite groups without any optimisations applied.{}{}Requires reloading NewGRFs if not previously enabled (misc_debug 10).
STR_NEWGRF_INSPECT_SPRITE_DUMP_PANEL_TOOLTIP :{BLACK}Click to highlight sprite group{}Shift+Click to collapse sprite group{}Ctrl+Click to highlight temporary storage register
STR_NEWGRF_INSPECT_CAPTION_OBJECT_AT :{STRING1} at {HEX}

@ -5544,20 +5544,8 @@ static const CallbackResultSpriteGroup *NewCallbackResultSpriteGroup(uint16 grou
return ptr;
}
/* Helper function to either create a callback or link to a previously
* defined spritegroup. */
static const SpriteGroup *GetGroupFromGroupID(byte setid, byte type, uint16 groupid)
static const SpriteGroup *PruneTargetSpriteGroup(const SpriteGroup *result)
{
if (HasBit(groupid, 15)) {
return NewCallbackResultSpriteGroup(groupid);
}
if (groupid > MAX_SPRITEGROUP || _cur.spritegroups[groupid] == nullptr) {
grfmsg(1, "GetGroupFromGroupID(0x%02X:0x%02X): Groupid 0x%04X does not exist, leaving empty", setid, type, groupid);
return nullptr;
}
const SpriteGroup *result = _cur.spritegroups[groupid];
if (HasGrfOptimiserFlag(NGOF_NO_OPT_VARACT2) || HasGrfOptimiserFlag(NGOF_NO_OPT_VARACT2_GROUP_PRUNE)) return result;
while (result != nullptr) {
if (result->type == SGT_DETERMINISTIC) {
@ -5580,6 +5568,24 @@ static const SpriteGroup *GetGroupFromGroupID(byte setid, byte type, uint16 grou
return result;
}
/* Helper function to either create a callback or link to a previously
* defined spritegroup. */
static const SpriteGroup *GetGroupFromGroupID(byte setid, byte type, uint16 groupid)
{
if (HasBit(groupid, 15)) {
return NewCallbackResultSpriteGroup(groupid);
}
if (groupid > MAX_SPRITEGROUP || _cur.spritegroups[groupid] == nullptr) {
grfmsg(1, "GetGroupFromGroupID(0x%02X:0x%02X): Groupid 0x%04X does not exist, leaving empty", setid, type, groupid);
return nullptr;
}
const SpriteGroup *result = _cur.spritegroups[groupid];
if (likely(!HasBit(_misc_debug_flags, MDF_NEWGRF_SG_SAVE_RAW))) result = PruneTargetSpriteGroup(result);
return result;
}
static const SpriteGroup *GetGroupByID(uint16 groupid)
{
const SpriteGroup *result = _cur.spritegroups[groupid];
@ -6793,6 +6799,46 @@ static void HandleVarAction2OptimisationPasses()
}
}
static void ProcessDeterministicSpriteGroupRanges(const std::vector<DeterministicSpriteGroupRange> &ranges, std::vector<DeterministicSpriteGroupRange> &ranges_out, const SpriteGroup *default_group)
{
/* Sort ranges ascending. When ranges overlap, this may required clamping or splitting them */
std::vector<uint32> bounds;
for (uint i = 0; i < ranges.size(); i++) {
bounds.push_back(ranges[i].low);
if (ranges[i].high != UINT32_MAX) bounds.push_back(ranges[i].high + 1);
}
std::sort(bounds.begin(), bounds.end());
bounds.erase(std::unique(bounds.begin(), bounds.end()), bounds.end());
std::vector<const SpriteGroup *> target;
for (uint j = 0; j < bounds.size(); ++j) {
uint32 v = bounds[j];
const SpriteGroup *t = default_group;
for (uint i = 0; i < ranges.size(); i++) {
if (ranges[i].low <= v && v <= ranges[i].high) {
t = ranges[i].group;
break;
}
}
target.push_back(t);
}
assert(target.size() == bounds.size());
for (uint j = 0; j < bounds.size(); ) {
if (target[j] != default_group) {
DeterministicSpriteGroupRange &r = ranges_out.emplace_back();
r.group = target[j];
r.low = bounds[j];
while (j < bounds.size() && target[j] == r.group) {
j++;
}
r.high = j < bounds.size() ? bounds[j] - 1 : UINT32_MAX;
} else {
j++;
}
}
}
/* Action 0x02 */
static void NewSpriteGroup(ByteReader *buf)
{
@ -6849,6 +6895,11 @@ static void NewSpriteGroup(ByteReader *buf)
case 2: group->size = DSG_SIZE_DWORD; varsize = 4; break;
}
DeterministicSpriteGroupShadowCopy *shadow = nullptr;
if (unlikely(HasBit(_misc_debug_flags, MDF_NEWGRF_SG_SAVE_RAW))) {
shadow = &(_deterministic_sg_shadows[group]);
}
VarAction2OptimiseState va2_opt_state;
/* The initial value is always the constant 0 */
va2_opt_state.inference = VA2AIF_SIGNED_NON_NEGATIVE | VA2AIF_ONE_OR_ZERO | VA2AIF_HAVE_CONSTANT;
@ -6904,6 +6955,11 @@ static void NewSpriteGroup(ByteReader *buf)
adjust.add_val = 0;
adjust.divmod_val = 0;
}
if (unlikely(shadow != nullptr)) {
shadow->adjusts.push_back(adjust);
/* Pruning was turned off so that the unpruned target could be saved in the shadow, prune now */
if (adjust.subroutine != nullptr) adjust.subroutine = PruneTargetSpriteGroup(adjust.subroutine);
}
OptimiseVarAction2Adjust(va2_opt_state, feature, varsize, group, adjust);
@ -6919,47 +6975,24 @@ static void NewSpriteGroup(ByteReader *buf)
}
group->default_group = GetGroupFromGroupID(setid, type, buf->ReadWord());
group->error_group = ranges.size() > 0 ? ranges[0].group : group->default_group;
/* nvar == 0 is a special case -- we turn our value into a callback result */
group->calculated_result = ranges.size() == 0;
/* Sort ranges ascending. When ranges overlap, this may required clamping or splitting them */
std::vector<uint32> bounds;
for (uint i = 0; i < ranges.size(); i++) {
bounds.push_back(ranges[i].low);
if (ranges[i].high != UINT32_MAX) bounds.push_back(ranges[i].high + 1);
}
std::sort(bounds.begin(), bounds.end());
bounds.erase(std::unique(bounds.begin(), bounds.end()), bounds.end());
std::vector<const SpriteGroup *> target;
for (uint j = 0; j < bounds.size(); ++j) {
uint32 v = bounds[j];
const SpriteGroup *t = group->default_group;
for (uint i = 0; i < ranges.size(); i++) {
if (ranges[i].low <= v && v <= ranges[i].high) {
t = ranges[i].group;
break;
}
}
target.push_back(t);
}
assert(target.size() == bounds.size());
if (unlikely(shadow != nullptr)) {
ProcessDeterministicSpriteGroupRanges(ranges, shadow->ranges, group->default_group);
shadow->default_group = group->default_group;
for (uint j = 0; j < bounds.size(); ) {
if (target[j] != group->default_group) {
DeterministicSpriteGroupRange &r = group->ranges.emplace_back();
r.group = target[j];
r.low = bounds[j];
while (j < bounds.size() && target[j] == r.group) {
j++;
}
r.high = j < bounds.size() ? bounds[j] - 1 : UINT32_MAX;
} else {
j++;
/* Pruning was turned off so that the unpruned targets could be saved in the shadow ranges, prune now */
for (DeterministicSpriteGroupRange &range : ranges) {
range.group = PruneTargetSpriteGroup(range.group);
}
group->default_group = PruneTargetSpriteGroup(group->default_group);
}
group->error_group = ranges.size() > 0 ? ranges[0].group : group->default_group;
/* nvar == 0 is a special case -- we turn our value into a callback result */
group->calculated_result = ranges.size() == 0;
ProcessDeterministicSpriteGroupRanges(ranges, group->ranges, group->default_group);
OptimiseVarAction2DeterministicSpriteGroup(va2_opt_state, feature, varsize, group);
break;
}
@ -6994,6 +7027,16 @@ static void NewSpriteGroup(ByteReader *buf)
group->groups.push_back(GetGroupFromGroupID(setid, type, buf->ReadWord()));
}
if (unlikely(HasBit(_misc_debug_flags, MDF_NEWGRF_SG_SAVE_RAW))) {
RandomizedSpriteGroupShadowCopy *shadow = &(_randomized_sg_shadows[group]);
shadow->groups = group->groups;
/* Pruning was turned off so that the unpruned targets could be saved in the shadow groups, prune now */
for (const SpriteGroup *&group : group->groups) {
group = PruneTargetSpriteGroup(group);
}
}
break;
}
@ -11289,6 +11332,9 @@ void ResetNewGRFData()
InitializeSoundPool();
_spritegroup_pool.CleanPool();
_callback_result_cache.clear();
_deterministic_sg_shadows.clear();
_randomized_sg_shadows.clear();
_grfs_loaded_with_sg_shadow_enable = HasBit(_misc_debug_flags, MDF_NEWGRF_SG_SAVE_RAW);
}
/**

@ -20,6 +20,7 @@
#include "vehicle_gui.h"
#include "zoom_func.h"
#include "scope.h"
#include "debug_settings.h"
#include "engine_base.h"
#include "industry.h"
@ -317,6 +318,7 @@ struct NewGRFInspectWindow : Window {
bool auto_refresh = false;
bool log_console = false;
bool sprite_dump = false;
bool sprite_dump_unopt = false;
uint32 extra_info_flags = 0;
btree::btree_map<int, uint> extra_info_click_flag_toggles;
@ -389,7 +391,10 @@ struct NewGRFInspectWindow : Window {
{
this->CreateNestedTree();
this->vscroll = this->GetScrollbar(WID_NGRFI_SCROLLBAR);
this->GetWidget<NWidgetStacked>(WID_NGRFI_SPRITE_DUMP_SEL)->SetDisplayedPlane(GetFeatureHelper(wno)->ShowSpriteDumpButton(::GetFeatureIndex(wno)) ? 0 : SZSP_NONE);
bool show_sprite_dump_button = GetFeatureHelper(wno)->ShowSpriteDumpButton(::GetFeatureIndex(wno));
this->GetWidget<NWidgetStacked>(WID_NGRFI_SPRITE_DUMP_SEL)->SetDisplayedPlane(show_sprite_dump_button ? 0 : SZSP_NONE);
this->GetWidget<NWidgetStacked>(WID_NGRFI_SPRITE_DUMP_UNOPT_SEL)->SetDisplayedPlane(show_sprite_dump_button ? 0 : SZSP_NONE);
this->SetWidgetDisabledState(WID_NGRFI_SPRITE_DUMP_UNOPT, true);
this->FinishInitNested(wno);
this->vscroll->SetCount(0);
@ -535,6 +540,7 @@ struct NewGRFInspectWindow : Window {
};
const_cast<NewGRFInspectWindow *>(this)->sprite_group_lines.clear();
if (this->sprite_dump) {
SpriteGroupDumper::use_shadows = this->sprite_dump_unopt;
bool collapsed = false;
const SpriteGroup *collapse_group = nullptr;
uint collapse_lines = 0;
@ -572,6 +578,7 @@ struct NewGRFInspectWindow : Window {
if (highlight_tag != 0 && this->selected_highlight_tag == highlight_tag) colour = TC_YELLOW;
::DrawString(r.left + LEFT_OFFSET, r.right - RIGHT_OFFSET, r.top + TOP_OFFSET + (scroll_offset * this->resize.step_height), buf, colour);
});
SpriteGroupDumper::use_shadows = false;
return;
} else {
NewGRFInspectWindow *this_mutable = const_cast<NewGRFInspectWindow *>(this);
@ -731,6 +738,20 @@ struct NewGRFInspectWindow : Window {
}
}
bool UnOptimisedSpriteDumpOK() const
{
if (_grfs_loaded_with_sg_shadow_enable) return true;
if (_networking && !_network_server) return false;
extern uint NetworkClientCount();
if (_networking && NetworkClientCount() > 1) {
return false;
}
return true;
}
void OnClick(Point pt, int widget, int click_count) override
{
switch (widget) {
@ -847,8 +868,34 @@ struct NewGRFInspectWindow : Window {
case WID_NGRFI_SPRITE_DUMP: {
this->sprite_dump = !this->sprite_dump;
this->SetWidgetLoweredState(WID_NGRFI_SPRITE_DUMP, this->sprite_dump);
this->SetWidgetDisabledState(WID_NGRFI_SPRITE_DUMP_UNOPT, !this->sprite_dump || !UnOptimisedSpriteDumpOK());
this->GetWidget<NWidgetCore>(WID_NGRFI_MAINPANEL)->SetToolTip(this->sprite_dump ? STR_NEWGRF_INSPECT_SPRITE_DUMP_PANEL_TOOLTIP : STR_NULL);
this->SetWidgetDirty(WID_NGRFI_SPRITE_DUMP);
this->SetWidgetDirty(WID_NGRFI_SPRITE_DUMP_UNOPT);
this->SetWidgetDirty(WID_NGRFI_MAINPANEL);
this->SetWidgetDirty(WID_NGRFI_SCROLLBAR);
break;
}
case WID_NGRFI_SPRITE_DUMP_UNOPT: {
if (!this->sprite_dump_unopt) {
if (!UnOptimisedSpriteDumpOK()) {
this->SetWidgetDisabledState(WID_NGRFI_SPRITE_DUMP_UNOPT, true);
this->SetWidgetDirty(WID_NGRFI_SPRITE_DUMP_UNOPT);
return;
}
if (!_grfs_loaded_with_sg_shadow_enable) {
SetBit(_misc_debug_flags, MDF_NEWGRF_SG_SAVE_RAW);
ReloadNewGRFData();
extern void PostCheckNewGRFLoadWarnings();
PostCheckNewGRFLoadWarnings();
}
}
this->sprite_dump_unopt = !this->sprite_dump_unopt;
this->SetWidgetLoweredState(WID_NGRFI_SPRITE_DUMP_UNOPT, this->sprite_dump_unopt);
this->SetWidgetDirty(WID_NGRFI_SPRITE_DUMP_UNOPT);
this->SetWidgetDirty(WID_NGRFI_MAINPANEL);
this->SetWidgetDirty(WID_NGRFI_SCROLLBAR);
break;
@ -904,6 +951,9 @@ static const NWidgetPart _nested_newgrf_inspect_chain_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
NWidget(WWT_CAPTION, COLOUR_GREY, WID_NGRFI_CAPTION), SetDataTip(STR_NEWGRF_INSPECT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NGRFI_SPRITE_DUMP_UNOPT_SEL),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_NGRFI_SPRITE_DUMP_UNOPT), SetDataTip(STR_NEWGRF_INSPECT_SPRITE_DUMP_UNOPT, STR_NEWGRF_INSPECT_SPRITE_DUMP_UNOPT_TOOLTIP),
EndContainer(),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NGRFI_SPRITE_DUMP_SEL),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_NGRFI_SPRITE_DUMP), SetDataTip(STR_NEWGRF_INSPECT_SPRITE_DUMP, STR_NEWGRF_INSPECT_SPRITE_DUMP_TOOLTIP),
EndContainer(),
@ -935,6 +985,9 @@ static const NWidgetPart _nested_newgrf_inspect_widgets[] = {
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
NWidget(WWT_CAPTION, COLOUR_GREY, WID_NGRFI_CAPTION), SetDataTip(STR_NEWGRF_INSPECT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_NGRFI_PARENT), SetDataTip(STR_NEWGRF_INSPECT_PARENT_BUTTON, STR_NEWGRF_INSPECT_PARENT_TOOLTIP),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NGRFI_SPRITE_DUMP_UNOPT_SEL),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_NGRFI_SPRITE_DUMP_UNOPT), SetDataTip(STR_NEWGRF_INSPECT_SPRITE_DUMP_UNOPT, STR_NEWGRF_INSPECT_SPRITE_DUMP_UNOPT_TOOLTIP),
EndContainer(),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NGRFI_SPRITE_DUMP_SEL),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_NGRFI_SPRITE_DUMP), SetDataTip(STR_NEWGRF_INSPECT_SPRITE_DUMP, STR_NEWGRF_INSPECT_SPRITE_DUMP_TOOLTIP),
EndContainer(),

@ -26,6 +26,10 @@ INSTANTIATE_POOL_METHODS(SpriteGroup)
TemporaryStorageArray<int32, 0x110> _temp_store;
std::map<const DeterministicSpriteGroup *, DeterministicSpriteGroupShadowCopy> _deterministic_sg_shadows;
std::map<const RandomizedSpriteGroup *, RandomizedSpriteGroupShadowCopy> _randomized_sg_shadows;
bool _grfs_loaded_with_sg_shadow_enable = false;
/**
* ResolverObject (re)entry point.
@ -702,6 +706,8 @@ static char *GetAdjustOperationName(char *str, const char *last, DeterministicSp
return str + seprintf(str, last, "\?\?\?(0x%X)", operation);
}
bool SpriteGroupDumper::use_shadows = false;
void SpriteGroupDumper::DumpSpriteGroup(const SpriteGroup *sg, int padding, uint flags)
{
uint32 highlight_tag = 0;
@ -748,8 +754,22 @@ void SpriteGroupDumper::DumpSpriteGroup(const SpriteGroup *sg, int padding, uint
}
case SGT_DETERMINISTIC: {
const DeterministicSpriteGroup *dsg = (const DeterministicSpriteGroup*)sg;
if (padding == 0 && !dsg->calculated_result && dsg->default_group != nullptr) {
this->top_default_group = dsg->default_group;
const SpriteGroup *default_group = dsg->default_group;
const std::vector<DeterministicSpriteGroupAdjust> *adjusts = &(dsg->adjusts);
const std::vector<DeterministicSpriteGroupRange> *ranges = &(dsg->ranges);
if (SpriteGroupDumper::use_shadows) {
auto iter = _deterministic_sg_shadows.find(dsg);
if (iter != _deterministic_sg_shadows.end()) {
default_group = iter->second.default_group;
adjusts = &(iter->second.adjusts);
ranges = &(iter->second.ranges);
}
}
if (padding == 0 && !dsg->calculated_result && default_group != nullptr) {
this->top_default_group = default_group;
}
if (dsg == this->top_default_group && !(padding == 4 && (flags & SGDF_DEFAULT))) {
seprintf(this->buffer, lastof(this->buffer), "%*sTOP LEVEL DEFAULT GROUP: Deterministic (%s, %s), [%u]",
@ -769,7 +789,7 @@ void SpriteGroupDumper::DumpSpriteGroup(const SpriteGroup *sg, int padding, uint
print();
emit_start();
padding += 2;
for (const auto &adjust : dsg->adjusts) {
for (const auto &adjust : (*adjusts)) {
char *p = this->buffer;
if (adjust.variable == 0x7D) {
/* Temp storage load */
@ -832,27 +852,37 @@ void SpriteGroupDumper::DumpSpriteGroup(const SpriteGroup *sg, int padding, uint
seprintf(this->buffer, lastof(this->buffer), "%*scalculated_result", padding, "");
print();
} else {
for (const auto &range : dsg->ranges) {
for (const auto &range : (*ranges)) {
seprintf(this->buffer, lastof(this->buffer), "%*srange: %X -> %X", padding, "", range.low, range.high);
print();
this->DumpSpriteGroup(range.group, padding + 2, 0);
}
if (dsg->default_group != nullptr) {
if (default_group != nullptr) {
seprintf(this->buffer, lastof(this->buffer), "%*sdefault", padding, "");
print();
this->DumpSpriteGroup(dsg->default_group, padding + 2, SGDF_DEFAULT);
this->DumpSpriteGroup(default_group, padding + 2, SGDF_DEFAULT);
}
}
break;
}
case SGT_RANDOMIZED: {
const RandomizedSpriteGroup *rsg = (const RandomizedSpriteGroup*)sg;
const std::vector<const SpriteGroup *> *groups = &(rsg->groups);
if (SpriteGroupDumper::use_shadows) {
auto iter = _randomized_sg_shadows.find(rsg);
if (iter != _randomized_sg_shadows.end()) {
groups = &(iter->second.groups);
}
}
seprintf(this->buffer, lastof(this->buffer), "%*sRandom (%s, %s, triggers: %X, count: %X, lowest_randbit: %X, groups: %u) [%u]",
padding, "", _sg_scope_names[rsg->var_scope], rsg->cmp_mode == RSG_CMP_ANY ? "ANY" : "ALL",
rsg->triggers, rsg->count, rsg->lowest_randbit, (uint)rsg->groups.size(), rsg->nfo_line);
print();
emit_start();
for (const auto &group : rsg->groups) {
for (const auto &group : (*groups)) {
this->DumpSpriteGroup(group, padding + 2, 0);
}
break;

@ -22,6 +22,8 @@
#include "3rdparty/cpp-btree/btree_set.h"
#include <map>
/**
* Gets the value of a so-called newgrf "register".
* @param i index of the register
@ -360,6 +362,12 @@ enum DeterministicSpriteGroupFlags : uint8 {
};
DECLARE_ENUM_AS_BIT_SET(DeterministicSpriteGroupFlags)
struct DeterministicSpriteGroupShadowCopy {
std::vector<DeterministicSpriteGroupAdjust> adjusts;
std::vector<DeterministicSpriteGroupRange> ranges;
const SpriteGroup *default_group;
};
struct DeterministicSpriteGroup : SpriteGroup {
DeterministicSpriteGroup() : SpriteGroup(SGT_DETERMINISTIC) {}
@ -387,6 +395,10 @@ enum RandomizedSpriteGroupCompareMode {
RSG_CMP_ALL,
};
struct RandomizedSpriteGroupShadowCopy {
std::vector<const SpriteGroup *> groups;
};
struct RandomizedSpriteGroup : SpriteGroup {
RandomizedSpriteGroup() : SpriteGroup(SGT_RANDOMIZED) {}
@ -406,6 +418,9 @@ protected:
const SpriteGroup *Resolve(ResolverObject &object) const override;
};
extern std::map<const DeterministicSpriteGroup *, DeterministicSpriteGroupShadowCopy> _deterministic_sg_shadows;
extern std::map<const RandomizedSpriteGroup *, RandomizedSpriteGroupShadowCopy> _randomized_sg_shadows;
extern bool _grfs_loaded_with_sg_shadow_enable;
/* This contains a callback result. A failed callback has a value of
* CALLBACK_FAILED */
@ -631,6 +646,8 @@ enum DumpSpriteGroupPrintOp {
using DumpSpriteGroupPrinter = std::function<void(const SpriteGroup *, DumpSpriteGroupPrintOp, uint32, const char *)>;
struct SpriteGroupDumper {
static bool use_shadows;
private:
char buffer[1024];
DumpSpriteGroupPrinter print_fn;

@ -24,6 +24,8 @@ enum NewGRFInspectWidgets {
WID_NGRFI_DUPLICATE, ///< Duplicate window
WID_NGRFI_SPRITE_DUMP, ///< Dump current sprite group
WID_NGRFI_SPRITE_DUMP_SEL, ///< Selection widget for WID_NGRFI_SPRITE_DUMP
WID_NGRFI_SPRITE_DUMP_UNOPT, ///< Dump unoptimised sprite group
WID_NGRFI_SPRITE_DUMP_UNOPT_SEL, ///< Selection widget for WID_NGRFI_SPRITE_DUMP_UNOPT
};
/** Widgets of the #SpriteAlignerWindow class. */

Loading…
Cancel
Save