mirror of https://github.com/OpenTTD/OpenTTD
Codechange: Preprocess text ref stack parameters. (#13642)
NewGRF text ref stack is now processed in advance, creating parameters as necessary, and then encoding this into an EncodedString.pull/13625/head
parent
4ac81656ee
commit
b28dca2222
|
@ -804,10 +804,7 @@ static std::optional<std::string> GetNewGRFAdditionalText(EngineID engine)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
StartTextRefStackUsage(grffile, 6);
|
return GetGRFStringWithTextStack(grffile, GRFSTR_MISC_GRF_TEXT + callback, 6);
|
||||||
std::string result = GetString(GetGRFStringID(grffile->grfid, GRFSTR_MISC_GRF_TEXT + callback));
|
|
||||||
StopTextRefStackUsage();
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -407,32 +407,8 @@ void CommandCost::AddCost(const CommandCost &ret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Values to put on the #TextRefStack for the error message.
|
|
||||||
* There is only one static instance of the array, just like there is only one
|
|
||||||
* instance of normal DParams.
|
|
||||||
*/
|
|
||||||
/* static */ uint32_t CommandCost::textref_stack[16];
|
|
||||||
|
|
||||||
/* static */ EncodedString CommandCost::encoded_message;
|
/* static */ EncodedString CommandCost::encoded_message;
|
||||||
|
|
||||||
/**
|
|
||||||
* Activate usage of the NewGRF #TextRefStack for the error message.
|
|
||||||
* @param grffile NewGRF that provides the #TextRefStack
|
|
||||||
* @param num_registers number of entries to copy from the temporary NewGRF registers
|
|
||||||
*/
|
|
||||||
void CommandCost::UseTextRefStack(const GRFFile *grffile, uint num_registers)
|
|
||||||
{
|
|
||||||
extern TemporaryStorageArray<int32_t, 0x110> _temp_store;
|
|
||||||
|
|
||||||
assert(num_registers < lengthof(textref_stack));
|
|
||||||
this->textref_stack_grffile = grffile;
|
|
||||||
this->textref_stack_size = num_registers;
|
|
||||||
for (uint i = 0; i < num_registers; i++) {
|
|
||||||
textref_stack[i] = _temp_store.GetValue(0x100 + i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return an error status, with string and parameter.
|
* Return an error status, with string and parameter.
|
||||||
* @param str StringID of error.
|
* @param str StringID of error.
|
||||||
|
|
|
@ -27,12 +27,8 @@ class CommandCost {
|
||||||
ExpensesType expense_type; ///< the type of expence as shown on the finances view
|
ExpensesType expense_type; ///< the type of expence as shown on the finances view
|
||||||
bool success; ///< Whether the command went fine up to this moment
|
bool success; ///< Whether the command went fine up to this moment
|
||||||
Owner owner = CompanyID::Invalid(); ///< Originator owner of error.
|
Owner owner = CompanyID::Invalid(); ///< Originator owner of error.
|
||||||
const GRFFile *textref_stack_grffile = nullptr; ///< NewGRF providing the #TextRefStack content.
|
|
||||||
uint textref_stack_size = 0; ///< Number of uint32_t values to put on the #TextRefStack for the error message.
|
|
||||||
StringID extra_message = INVALID_STRING_ID; ///< Additional warning message for when success is unset
|
StringID extra_message = INVALID_STRING_ID; ///< Additional warning message for when success is unset
|
||||||
|
|
||||||
static uint32_t textref_stack[16];
|
|
||||||
|
|
||||||
static EncodedString encoded_message; ///< Encoded error message, used if the error message includes parameters.
|
static EncodedString encoded_message; ///< Encoded error message, used if the error message includes parameters.
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -146,35 +142,6 @@ public:
|
||||||
this->extra_message = INVALID_STRING_ID;
|
this->extra_message = INVALID_STRING_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UseTextRefStack(const GRFFile *grffile, uint num_registers);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the NewGRF providing the #TextRefStack of the error message.
|
|
||||||
* @return the NewGRF.
|
|
||||||
*/
|
|
||||||
const GRFFile *GetTextRefStackGRF() const
|
|
||||||
{
|
|
||||||
return this->textref_stack_grffile;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of uint32_t values for the #TextRefStack of the error message.
|
|
||||||
* @return number of uint32_t values.
|
|
||||||
*/
|
|
||||||
uint GetTextRefStackSize() const
|
|
||||||
{
|
|
||||||
return this->textref_stack_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a pointer to the values for the #TextRefStack of the error message.
|
|
||||||
* @return uint32_t values for the #TextRefStack
|
|
||||||
*/
|
|
||||||
const uint32_t *GetTextRefStack() const
|
|
||||||
{
|
|
||||||
return textref_stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the error message of a command
|
* Returns the error message of a command
|
||||||
* @return the error message, if succeeded #INVALID_STRING_ID
|
* @return the error message, if succeeded #INVALID_STRING_ID
|
||||||
|
|
11
src/error.h
11
src/error.h
|
@ -31,9 +31,6 @@ enum WarningLevel : uint8_t {
|
||||||
class ErrorMessageData {
|
class ErrorMessageData {
|
||||||
protected:
|
protected:
|
||||||
bool is_critical; ///< Whether the error message is critical.
|
bool is_critical; ///< Whether the error message is critical.
|
||||||
const GRFFile *textref_stack_grffile; ///< NewGRF that filled the #TextRefStack for the error message.
|
|
||||||
uint textref_stack_size; ///< Number of uint32_t values to put on the #TextRefStack for the error message.
|
|
||||||
uint32_t textref_stack[16]; ///< Values to put on the #TextRefStack for the error message.
|
|
||||||
EncodedString summary_msg; ///< General error message showed in first line. Must be valid.
|
EncodedString summary_msg; ///< General error message showed in first line. Must be valid.
|
||||||
EncodedString detailed_msg; ///< Detailed error message showed in second line. Can be #INVALID_STRING_ID.
|
EncodedString detailed_msg; ///< Detailed error message showed in second line. Can be #INVALID_STRING_ID.
|
||||||
EncodedString extra_msg; ///< Extra error message shown in third line. Can be #INVALID_STRING_ID.
|
EncodedString extra_msg; ///< Extra error message shown in third line. Can be #INVALID_STRING_ID.
|
||||||
|
@ -41,11 +38,7 @@ protected:
|
||||||
CompanyID company; ///< Company belonging to the face being shown. #CompanyID::Invalid() if no face present.
|
CompanyID company; ///< Company belonging to the face being shown. #CompanyID::Invalid() if no face present.
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ErrorMessageData(const ErrorMessageData &data);
|
ErrorMessageData(EncodedString &&summary_msg, EncodedString &&detailed_msg, bool is_critical = false, int x = 0, int y = 0, EncodedString &&extra_msg = {}, CompanyID company = CompanyID::Invalid());
|
||||||
ErrorMessageData(EncodedString &&summary_msg, EncodedString &&detailed_msg, bool is_critical = false, int x = 0, int y = 0, const GRFFile *textref_stack_grffile = nullptr, uint textref_stack_size = 0, const uint32_t *textref_stack = nullptr, EncodedString &&extra_msg = {}, CompanyID company = CompanyID::Invalid());
|
|
||||||
|
|
||||||
/* Remove the copy assignment, as the default implementation will not do the right thing. */
|
|
||||||
ErrorMessageData &operator=(ErrorMessageData &rhs) = delete;
|
|
||||||
|
|
||||||
/** Check whether error window shall display a company manager face */
|
/** Check whether error window shall display a company manager face */
|
||||||
bool HasFace() const { return company != CompanyID::Invalid(); }
|
bool HasFace() const { return company != CompanyID::Invalid(); }
|
||||||
|
@ -58,7 +51,7 @@ void ScheduleErrorMessage(ErrorList &datas);
|
||||||
void ScheduleErrorMessage(const ErrorMessageData &data);
|
void ScheduleErrorMessage(const ErrorMessageData &data);
|
||||||
|
|
||||||
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, const CommandCost &cc);
|
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, const CommandCost &cc);
|
||||||
void ShowErrorMessage(EncodedString &&summary_msg, EncodedString &&detailed_msg, WarningLevel wl, int x = 0, int y = 0, const GRFFile *textref_stack_grffile = nullptr, uint textref_stack_size = 0, const uint32_t *textref_stack = nullptr, EncodedString &&extra_msg = {}, CompanyID company = CompanyID::Invalid());
|
void ShowErrorMessage(EncodedString &&summary_msg, EncodedString &&detailed_msg, WarningLevel wl, int x = 0, int y = 0, EncodedString &&extra_msg = {}, CompanyID company = CompanyID::Invalid());
|
||||||
bool HideActiveErrorMessage();
|
bool HideActiveErrorMessage();
|
||||||
|
|
||||||
void ClearErrorMessages();
|
void ClearErrorMessages();
|
||||||
|
|
|
@ -70,17 +70,6 @@ static WindowDesc _errmsg_face_desc(
|
||||||
_nested_errmsg_face_widgets
|
_nested_errmsg_face_widgets
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
|
||||||
* Copy the given data into our instance.
|
|
||||||
* @param data The data to copy.
|
|
||||||
*/
|
|
||||||
ErrorMessageData::ErrorMessageData(const ErrorMessageData &data) :
|
|
||||||
is_critical(data.is_critical), textref_stack_grffile(data.textref_stack_grffile), textref_stack_size(data.textref_stack_size),
|
|
||||||
summary_msg(data.summary_msg), detailed_msg(data.detailed_msg), extra_msg(data.extra_msg), position(data.position), company(data.company)
|
|
||||||
{
|
|
||||||
memcpy(this->textref_stack, data.textref_stack, sizeof(this->textref_stack));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display an error message in a window.
|
* Display an error message in a window.
|
||||||
* @param summary_msg General error message showed in first line. Must be valid.
|
* @param summary_msg General error message showed in first line. Must be valid.
|
||||||
|
@ -88,25 +77,16 @@ ErrorMessageData::ErrorMessageData(const ErrorMessageData &data) :
|
||||||
* @param is_critical Whether the error is critical. Critical messages never go away on their own.
|
* @param is_critical Whether the error is critical. Critical messages never go away on their own.
|
||||||
* @param x World X position (TileVirtX) of the error location. Set both x and y to 0 to just center the message when there is no related error tile.
|
* @param x World X position (TileVirtX) of the error location. Set both x and y to 0 to just center the message when there is no related error tile.
|
||||||
* @param y World Y position (TileVirtY) of the error location. Set both x and y to 0 to just center the message when there is no related error tile.
|
* @param y World Y position (TileVirtY) of the error location. Set both x and y to 0 to just center the message when there is no related error tile.
|
||||||
* @param textref_stack_grffile NewGRF that provides the #TextRefStack for the error message.
|
|
||||||
* @param textref_stack_size Number of uint32_t values to put on the #TextRefStack for the error message; 0 if the #TextRefStack shall not be used.
|
|
||||||
* @param textref_stack Values to put on the #TextRefStack.
|
|
||||||
* @param extra_msg Extra error message showed in third line. Can be empty.
|
* @param extra_msg Extra error message showed in third line. Can be empty.
|
||||||
*/
|
*/
|
||||||
ErrorMessageData::ErrorMessageData(EncodedString &&summary_msg, EncodedString &&detailed_msg, bool is_critical, int x, int y, const GRFFile *textref_stack_grffile, uint textref_stack_size, const uint32_t *textref_stack, EncodedString &&extra_msg, CompanyID company) :
|
ErrorMessageData::ErrorMessageData(EncodedString &&summary_msg, EncodedString &&detailed_msg, bool is_critical, int x, int y, EncodedString &&extra_msg, CompanyID company) :
|
||||||
is_critical(is_critical),
|
is_critical(is_critical),
|
||||||
textref_stack_grffile(textref_stack_grffile),
|
|
||||||
textref_stack_size(textref_stack_size),
|
|
||||||
summary_msg(std::move(summary_msg)),
|
summary_msg(std::move(summary_msg)),
|
||||||
detailed_msg(std::move(detailed_msg)),
|
detailed_msg(std::move(detailed_msg)),
|
||||||
extra_msg(std::move(extra_msg)),
|
extra_msg(std::move(extra_msg)),
|
||||||
|
position(x, y),
|
||||||
company(company)
|
company(company)
|
||||||
{
|
{
|
||||||
this->position.x = x;
|
|
||||||
this->position.y = y;
|
|
||||||
|
|
||||||
if (textref_stack_size > 0) MemCpyT(this->textref_stack, textref_stack, textref_stack_size);
|
|
||||||
|
|
||||||
assert(!this->summary_msg.empty());
|
assert(!this->summary_msg.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,14 +123,10 @@ public:
|
||||||
{
|
{
|
||||||
switch (widget) {
|
switch (widget) {
|
||||||
case WID_EM_MESSAGE: {
|
case WID_EM_MESSAGE: {
|
||||||
if (this->textref_stack_size > 0) StartTextRefStackUsage(this->textref_stack_grffile, this->textref_stack_size, this->textref_stack);
|
|
||||||
|
|
||||||
this->height_summary = GetStringHeight(this->summary_msg.GetDecodedString(), size.width);
|
this->height_summary = GetStringHeight(this->summary_msg.GetDecodedString(), size.width);
|
||||||
this->height_detailed = (this->detailed_msg.empty()) ? 0 : GetStringHeight(this->detailed_msg.GetDecodedString(), size.width);
|
this->height_detailed = (this->detailed_msg.empty()) ? 0 : GetStringHeight(this->detailed_msg.GetDecodedString(), size.width);
|
||||||
this->height_extra = (this->extra_msg.empty()) ? 0 : GetStringHeight(this->extra_msg.GetDecodedString(), size.width);
|
this->height_extra = (this->extra_msg.empty()) ? 0 : GetStringHeight(this->extra_msg.GetDecodedString(), size.width);
|
||||||
|
|
||||||
if (this->textref_stack_size > 0) StopTextRefStackUsage();
|
|
||||||
|
|
||||||
uint panel_height = this->height_summary;
|
uint panel_height = this->height_summary;
|
||||||
if (!this->detailed_msg.empty()) panel_height += this->height_detailed + WidgetDimensions::scaled.vsep_wide;
|
if (!this->detailed_msg.empty()) panel_height += this->height_detailed + WidgetDimensions::scaled.vsep_wide;
|
||||||
if (!this->extra_msg.empty()) panel_height += this->height_extra + WidgetDimensions::scaled.vsep_wide;
|
if (!this->extra_msg.empty()) panel_height += this->height_extra + WidgetDimensions::scaled.vsep_wide;
|
||||||
|
@ -215,8 +191,6 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
case WID_EM_MESSAGE:
|
case WID_EM_MESSAGE:
|
||||||
if (this->textref_stack_size > 0) StartTextRefStackUsage(this->textref_stack_grffile, this->textref_stack_size, this->textref_stack);
|
|
||||||
|
|
||||||
if (this->detailed_msg.empty()) {
|
if (this->detailed_msg.empty()) {
|
||||||
DrawStringMultiLine(r, this->summary_msg.GetDecodedString(), TC_FROMSTRING, SA_CENTER);
|
DrawStringMultiLine(r, this->summary_msg.GetDecodedString(), TC_FROMSTRING, SA_CENTER);
|
||||||
} else if (this->extra_msg.empty()) {
|
} else if (this->extra_msg.empty()) {
|
||||||
|
@ -239,7 +213,6 @@ public:
|
||||||
DrawStringMultiLine(bottom_section, this->extra_msg.GetDecodedString(), TC_WHITE, SA_CENTER);
|
DrawStringMultiLine(bottom_section, this->extra_msg.GetDecodedString(), TC_WHITE, SA_CENTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->textref_stack_size > 0) StopTextRefStackUsage();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -310,7 +283,7 @@ void UnshowCriticalError()
|
||||||
* @param summary_msg General error message showed in first line. Must be valid.
|
* @param summary_msg General error message showed in first line. Must be valid.
|
||||||
* @param x World X position (TileVirtX) of the error location. Set both x and y to 0 to just center the message when there is no related error tile.
|
* @param x World X position (TileVirtX) of the error location. Set both x and y to 0 to just center the message when there is no related error tile.
|
||||||
* @param y World Y position (TileVirtY) of the error location. Set both x and y to 0 to just center the message when there is no related error tile.
|
* @param y World Y position (TileVirtY) of the error location. Set both x and y to 0 to just center the message when there is no related error tile.
|
||||||
* @param cc CommandCost containing the optional detailed and extra error messages shown in the second and third lines (can be empty) and TextRefStack info.
|
* @param cc CommandCost containing the optional detailed and extra error messages shown in the second and third lines (can be empty).
|
||||||
*/
|
*/
|
||||||
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, const CommandCost &cc)
|
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, const CommandCost &cc)
|
||||||
{
|
{
|
||||||
|
@ -318,7 +291,6 @@ void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, const CommandCo
|
||||||
if (error.empty()) error = GetEncodedStringIfValid(cc.GetErrorMessage());
|
if (error.empty()) error = GetEncodedStringIfValid(cc.GetErrorMessage());
|
||||||
|
|
||||||
ShowErrorMessage(std::move(summary_msg), std::move(error), WL_INFO, x, y,
|
ShowErrorMessage(std::move(summary_msg), std::move(error), WL_INFO, x, y,
|
||||||
cc.GetTextRefStackGRF(), cc.GetTextRefStackSize(), cc.GetTextRefStack(),
|
|
||||||
GetEncodedStringIfValid(cc.GetExtraErrorMessage()), cc.GetErrorOwner());
|
GetEncodedStringIfValid(cc.GetExtraErrorMessage()), cc.GetErrorOwner());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,20 +301,13 @@ void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, const CommandCo
|
||||||
* @param wl Message severity.
|
* @param wl Message severity.
|
||||||
* @param x World X position (TileVirtX) of the error location. Set both x and y to 0 to just center the message when there is no related error tile.
|
* @param x World X position (TileVirtX) of the error location. Set both x and y to 0 to just center the message when there is no related error tile.
|
||||||
* @param y World Y position (TileVirtY) of the error location. Set both x and y to 0 to just center the message when there is no related error tile.
|
* @param y World Y position (TileVirtY) of the error location. Set both x and y to 0 to just center the message when there is no related error tile.
|
||||||
* @param textref_stack_grffile NewGRF providing the #TextRefStack for the error message.
|
|
||||||
* @param textref_stack_size Number of uint32_t values to put on the #TextRefStack for the error message; 0 if the #TextRefStack shall not be used.
|
|
||||||
* @param textref_stack Values to put on the #TextRefStack.
|
|
||||||
* @param extra_msg Extra error message shown in third line. Can be empty.
|
* @param extra_msg Extra error message shown in third line. Can be empty.
|
||||||
*/
|
*/
|
||||||
void ShowErrorMessage(EncodedString &&summary_msg, EncodedString &&detailed_msg, WarningLevel wl, int x, int y, const GRFFile *textref_stack_grffile, uint textref_stack_size, const uint32_t *textref_stack, EncodedString &&extra_msg, CompanyID company)
|
void ShowErrorMessage(EncodedString &&summary_msg, EncodedString &&detailed_msg, WarningLevel wl, int x, int y, EncodedString &&extra_msg, CompanyID company)
|
||||||
{
|
{
|
||||||
assert(textref_stack_size == 0 || (textref_stack_grffile != nullptr && textref_stack != nullptr));
|
|
||||||
|
|
||||||
if (wl != WL_INFO) {
|
if (wl != WL_INFO) {
|
||||||
/* Print message to console */
|
/* Print message to console */
|
||||||
|
|
||||||
if (textref_stack_size > 0) StartTextRefStackUsage(textref_stack_grffile, textref_stack_size, textref_stack);
|
|
||||||
|
|
||||||
std::string message = summary_msg.GetDecodedString();
|
std::string message = summary_msg.GetDecodedString();
|
||||||
if (!detailed_msg.empty()) {
|
if (!detailed_msg.empty()) {
|
||||||
message += " ";
|
message += " ";
|
||||||
|
@ -353,8 +318,6 @@ void ShowErrorMessage(EncodedString &&summary_msg, EncodedString &&detailed_msg,
|
||||||
message += extra_msg.GetDecodedString();
|
message += extra_msg.GetDecodedString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (textref_stack_size > 0) StopTextRefStackUsage();
|
|
||||||
|
|
||||||
IConsolePrint(wl == WL_WARNING ? CC_WARNING : CC_ERROR, message);
|
IConsolePrint(wl == WL_WARNING ? CC_WARNING : CC_ERROR, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,7 +326,7 @@ void ShowErrorMessage(EncodedString &&summary_msg, EncodedString &&detailed_msg,
|
||||||
if (_game_mode == GM_BOOTSTRAP) return;
|
if (_game_mode == GM_BOOTSTRAP) return;
|
||||||
if (_settings_client.gui.errmsg_duration == 0 && !is_critical) return;
|
if (_settings_client.gui.errmsg_duration == 0 && !is_critical) return;
|
||||||
|
|
||||||
ErrorMessageData data(std::move(summary_msg), std::move(detailed_msg), is_critical, x, y, textref_stack_grffile, textref_stack_size, textref_stack, std::move(extra_msg), company);
|
ErrorMessageData data(std::move(summary_msg), std::move(detailed_msg), is_critical, x, y, std::move(extra_msg), company);
|
||||||
|
|
||||||
ErrmsgWindow *w = dynamic_cast<ErrmsgWindow *>(FindWindowById(WC_ERRMSG, 0));
|
ErrmsgWindow *w = dynamic_cast<ErrmsgWindow *>(FindWindowById(WC_ERRMSG, 0));
|
||||||
if (w != nullptr) {
|
if (w != nullptr) {
|
||||||
|
|
|
@ -104,9 +104,7 @@ static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind,
|
||||||
if (indspec->grf_prop.grffile->grf_version < 8) {
|
if (indspec->grf_prop.grffile->grf_version < 8) {
|
||||||
if (GB(callback, 0, 8) == 0xFF) return;
|
if (GB(callback, 0, 8) == 0xFF) return;
|
||||||
if (callback < 0x400) {
|
if (callback < 0x400) {
|
||||||
StartTextRefStackUsage(indspec->grf_prop.grffile, 6);
|
suffix.text = GetGRFStringWithTextStack(indspec->grf_prop.grffile, GRFSTR_MISC_GRF_TEXT + callback, 6);
|
||||||
suffix.text = GetString(GetGRFStringID(indspec->grf_prop.grfid, GRFSTR_MISC_GRF_TEXT + callback));
|
|
||||||
StopTextRefStackUsage();
|
|
||||||
suffix.display = CSD_CARGO_AMOUNT_TEXT;
|
suffix.display = CSD_CARGO_AMOUNT_TEXT;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -120,16 +118,12 @@ static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (callback < 0x400) {
|
if (callback < 0x400) {
|
||||||
StartTextRefStackUsage(indspec->grf_prop.grffile, 6);
|
suffix.text = GetGRFStringWithTextStack(indspec->grf_prop.grffile, GRFSTR_MISC_GRF_TEXT + callback, 6);
|
||||||
suffix.text = GetString(GetGRFStringID(indspec->grf_prop.grfid, GRFSTR_MISC_GRF_TEXT + callback));
|
|
||||||
StopTextRefStackUsage();
|
|
||||||
suffix.display = CSD_CARGO_AMOUNT_TEXT;
|
suffix.display = CSD_CARGO_AMOUNT_TEXT;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (callback >= 0x800 && callback < 0xC00) {
|
if (callback >= 0x800 && callback < 0xC00) {
|
||||||
StartTextRefStackUsage(indspec->grf_prop.grffile, 6);
|
suffix.text = GetGRFStringWithTextStack(indspec->grf_prop.grffile, GRFSTR_MISC_GRF_TEXT + callback - 0x800, 6);
|
||||||
suffix.text = GetString(GetGRFStringID(indspec->grf_prop.grfid, GRFSTR_MISC_GRF_TEXT + callback - 0x800));
|
|
||||||
StopTextRefStackUsage();
|
|
||||||
suffix.display = CSD_CARGO_TEXT;
|
suffix.display = CSD_CARGO_TEXT;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -604,11 +598,9 @@ public:
|
||||||
if (callback_res > 0x400) {
|
if (callback_res > 0x400) {
|
||||||
ErrorUnknownCallbackResult(indsp->grf_prop.grfid, CBID_INDUSTRY_FUND_MORE_TEXT, callback_res);
|
ErrorUnknownCallbackResult(indsp->grf_prop.grfid, CBID_INDUSTRY_FUND_MORE_TEXT, callback_res);
|
||||||
} else {
|
} else {
|
||||||
StringID str = GetGRFStringID(indsp->grf_prop.grfid, GRFSTR_MISC_GRF_TEXT + callback_res); // No. here's the new string
|
std::string str = GetGRFStringWithTextStack(indsp->grf_prop.grffile, GRFSTR_MISC_GRF_TEXT + callback_res, 6);
|
||||||
if (str != STR_UNDEFINED) {
|
if (!str.empty()) {
|
||||||
StartTextRefStackUsage(indsp->grf_prop.grffile, 6);
|
|
||||||
DrawStringMultiLine(ir, str, TC_YELLOW);
|
DrawStringMultiLine(ir, str, TC_YELLOW);
|
||||||
StopTextRefStackUsage();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1006,13 +998,10 @@ public:
|
||||||
if (callback_res > 0x400) {
|
if (callback_res > 0x400) {
|
||||||
ErrorUnknownCallbackResult(ind->grf_prop.grfid, CBID_INDUSTRY_WINDOW_MORE_TEXT, callback_res);
|
ErrorUnknownCallbackResult(ind->grf_prop.grfid, CBID_INDUSTRY_WINDOW_MORE_TEXT, callback_res);
|
||||||
} else {
|
} else {
|
||||||
StringID message = GetGRFStringID(ind->grf_prop.grfid, GRFSTR_MISC_GRF_TEXT + callback_res);
|
std::string str = GetGRFStringWithTextStack(ind->grf_prop.grffile, GRFSTR_MISC_GRF_TEXT + callback_res, 6);
|
||||||
if (message != STR_NULL && message != STR_UNDEFINED) {
|
if (!str.empty()) {
|
||||||
ir.top += WidgetDimensions::scaled.vsep_wide;
|
ir.top += WidgetDimensions::scaled.vsep_wide;
|
||||||
|
ir.top = DrawStringMultiLine(ir, str, TC_YELLOW);
|
||||||
StartTextRefStackUsage(ind->grf_prop.grffile, 6);
|
|
||||||
ir.top = DrawStringMultiLine(ir, message, TC_BLACK);
|
|
||||||
StopTextRefStackUsage();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -473,6 +473,13 @@ CommandCost GetErrorMessageFromLocationCallbackResult(uint16_t cb_res, const GRF
|
||||||
|
|
||||||
if (cb_res < 0x400) {
|
if (cb_res < 0x400) {
|
||||||
res = CommandCost(GetGRFStringID(grffile->grfid, GRFSTR_MISC_GRF_TEXT + cb_res));
|
res = CommandCost(GetGRFStringID(grffile->grfid, GRFSTR_MISC_GRF_TEXT + cb_res));
|
||||||
|
|
||||||
|
/* If this error isn't for the local player then it won't be seen, so don't bother encoding anything. */
|
||||||
|
if (!IsLocalCompany()) return res;
|
||||||
|
|
||||||
|
StringID stringid = GetGRFStringID(grffile->grfid, GRFSTR_MISC_GRF_TEXT + cb_res);
|
||||||
|
auto params = GetGRFSringTextStackParameters(grffile, stringid, 4);
|
||||||
|
res.SetEncodedMessage(GetEncodedStringWithArgs(stringid, params));
|
||||||
} else {
|
} else {
|
||||||
switch (cb_res) {
|
switch (cb_res) {
|
||||||
case 0x400: return res; // No error.
|
case 0x400: return res; // No error.
|
||||||
|
@ -490,14 +497,6 @@ CommandCost GetErrorMessageFromLocationCallbackResult(uint16_t cb_res, const GRF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If this error isn't for the local player then it won't be seen, so don't bother encoding anything. */
|
|
||||||
if (!IsLocalCompany()) return res;
|
|
||||||
|
|
||||||
/* Copy some parameters from the registers to the error message text ref. stack */
|
|
||||||
std::array<StringParameter, 20> params{};
|
|
||||||
res.UseTextRefStack(grffile, 4);
|
|
||||||
res.SetEncodedMessage(GetEncodedStringWithArgs(res.GetErrorMessage(), params));
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
#include "debug.h"
|
||||||
#include "newgrf.h"
|
#include "newgrf.h"
|
||||||
#include "strings_internal.h"
|
#include "strings_internal.h"
|
||||||
#include "newgrf_storage.h"
|
#include "newgrf_storage.h"
|
||||||
|
@ -685,12 +686,24 @@ void CleanUpStrings()
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TextRefStack {
|
struct TextRefStack {
|
||||||
std::array<uint8_t, 0x30> stack;
|
std::array<uint8_t, 0x30> stack{};
|
||||||
uint8_t position;
|
uint8_t position = 0;
|
||||||
const GRFFile *grffile;
|
const GRFFile *grffile = nullptr;
|
||||||
bool used;
|
|
||||||
|
|
||||||
TextRefStack() : position(0), grffile(nullptr), used(false) {}
|
TextRefStack(const GRFFile *grffile, uint8_t num_entries) : grffile(grffile)
|
||||||
|
{
|
||||||
|
extern TemporaryStorageArray<int32_t, 0x110> _temp_store;
|
||||||
|
|
||||||
|
assert(num_entries < sizeof(uint32_t) * std::size(stack));
|
||||||
|
|
||||||
|
auto stack_it = this->stack.begin();
|
||||||
|
for (uint i = 0; i < num_entries; i++) {
|
||||||
|
uint32_t value = _temp_store.GetValue(0x100 + i);
|
||||||
|
for (uint j = 0; j < 32; j += 8) {
|
||||||
|
*stack_it++ = GB(value, j, 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t PopUnsignedByte() { assert(this->position < this->stack.size()); return this->stack[this->position++]; }
|
uint8_t PopUnsignedByte() { assert(this->position < this->stack.size()); return this->stack[this->position++]; }
|
||||||
int8_t PopSignedByte() { return (int8_t)this->PopUnsignedByte(); }
|
int8_t PopSignedByte() { return (int8_t)this->PopUnsignedByte(); }
|
||||||
|
@ -736,213 +749,95 @@ struct TextRefStack {
|
||||||
this->stack[this->position] = GB(word, 0, 8);
|
this->stack[this->position] = GB(word, 0, 8);
|
||||||
this->stack[this->position + 1] = GB(word, 8, 8);
|
this->stack[this->position + 1] = GB(word, 8, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResetStack(const GRFFile *grffile)
|
|
||||||
{
|
|
||||||
assert(grffile != nullptr);
|
|
||||||
this->position = 0;
|
|
||||||
this->grffile = grffile;
|
|
||||||
this->used = true;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** The stack that is used for TTDP compatible string code parsing */
|
static void HandleNewGRFStringControlCodes(const char *str, TextRefStack &stack, std::vector<StringParameter> ¶ms);
|
||||||
static TextRefStack _newgrf_textrefstack;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether the NewGRF text stack is in use.
|
* Process NewGRF string control code instructions.
|
||||||
* @return True iff the NewGRF text stack is used.
|
* @param scc The string control code that has been read.
|
||||||
|
* @param str The string that we are reading from.
|
||||||
|
* @param stack The TextRefStack.
|
||||||
|
* @param[out] params Output parameters
|
||||||
*/
|
*/
|
||||||
bool UsingNewGRFTextStack()
|
static void RemapNewGRFStringControlCode(char32_t scc, const char **str, TextRefStack &stack, std::vector<StringParameter> ¶ms)
|
||||||
{
|
{
|
||||||
return _newgrf_textrefstack.used;
|
auto it = std::back_inserter(params);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/* There is data on the NewGRF text stack, and we want to move them to OpenTTD's string stack.
|
||||||
* Create a backup of the current NewGRF text stack.
|
* After this call, a new call is made with `modify_parameters` set to false when the string is finally formatted. */
|
||||||
* @return A copy of the current text stack.
|
|
||||||
*/
|
|
||||||
struct TextRefStack *CreateTextRefStackBackup()
|
|
||||||
{
|
|
||||||
return new TextRefStack(_newgrf_textrefstack);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restore a copy of the text stack to the used stack.
|
|
||||||
* @param backup The copy to restore.
|
|
||||||
*/
|
|
||||||
void RestoreTextRefStackBackup(struct TextRefStack *backup)
|
|
||||||
{
|
|
||||||
_newgrf_textrefstack = *backup;
|
|
||||||
delete backup;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start using the TTDP compatible string code parsing.
|
|
||||||
*
|
|
||||||
* On start a number of values is copied on the #TextRefStack.
|
|
||||||
* You can then use #GetString() and the normal string drawing functions,
|
|
||||||
* and they will use the #TextRefStack for NewGRF string codes.
|
|
||||||
*
|
|
||||||
* However, when you want to draw a string multiple times using the same stack,
|
|
||||||
* you have to call #RewindTextRefStack() between draws.
|
|
||||||
*
|
|
||||||
* After you are done with drawing, you must disable usage of the #TextRefStack
|
|
||||||
* by calling #StopTextRefStackUsage(), so NewGRF string codes operate on the
|
|
||||||
* normal string parameters again.
|
|
||||||
*
|
|
||||||
* @param grffile the NewGRF providing the stack data
|
|
||||||
* @param numEntries number of entries to copy from the registers
|
|
||||||
* @param values values to copy onto the stack; if nullptr the temporary NewGRF registers will be used instead
|
|
||||||
*/
|
|
||||||
void StartTextRefStackUsage(const GRFFile *grffile, uint8_t numEntries, const uint32_t *values)
|
|
||||||
{
|
|
||||||
extern TemporaryStorageArray<int32_t, 0x110> _temp_store;
|
|
||||||
|
|
||||||
_newgrf_textrefstack.ResetStack(grffile);
|
|
||||||
|
|
||||||
auto stack_it = _newgrf_textrefstack.stack.begin();
|
|
||||||
for (uint i = 0; i < numEntries; i++) {
|
|
||||||
uint32_t value = values != nullptr ? values[i] : _temp_store.GetValue(0x100 + i);
|
|
||||||
for (uint j = 0; j < 32; j += 8) {
|
|
||||||
*stack_it = GB(value, j, 8);
|
|
||||||
stack_it++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Stop using the TTDP compatible string code parsing */
|
|
||||||
void StopTextRefStackUsage()
|
|
||||||
{
|
|
||||||
_newgrf_textrefstack.used = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* FormatString for NewGRF specific "magic" string control codes
|
|
||||||
* @param scc the string control code that has been read
|
|
||||||
* @param str the string that we need to write
|
|
||||||
* @param parameters the OpenTTD string formatting parameters
|
|
||||||
* @param modify_parameters When true, modify the OpenTTD string formatting parameters.
|
|
||||||
* @return the string control code to "execute" now
|
|
||||||
*/
|
|
||||||
char32_t RemapNewGRFStringControlCode(char32_t scc, const char **str, StringParameters ¶meters, bool modify_parameters)
|
|
||||||
{
|
|
||||||
switch (scc) {
|
switch (scc) {
|
||||||
default: break;
|
default: return;
|
||||||
|
case SCC_NEWGRF_PRINT_BYTE_SIGNED: *it = stack.PopSignedByte(); break;
|
||||||
|
case SCC_NEWGRF_PRINT_QWORD_CURRENCY: *it = stack.PopSignedQWord(); break;
|
||||||
|
|
||||||
/* These control codes take one string parameter, check there are at least that many available. */
|
|
||||||
case SCC_NEWGRF_PRINT_DWORD_SIGNED:
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_SIGNED:
|
|
||||||
case SCC_NEWGRF_PRINT_BYTE_SIGNED:
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_UNSIGNED:
|
|
||||||
case SCC_NEWGRF_PRINT_BYTE_HEX:
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_HEX:
|
|
||||||
case SCC_NEWGRF_PRINT_DWORD_HEX:
|
|
||||||
case SCC_NEWGRF_PRINT_QWORD_HEX:
|
|
||||||
case SCC_NEWGRF_PRINT_DWORD_CURRENCY:
|
case SCC_NEWGRF_PRINT_DWORD_CURRENCY:
|
||||||
case SCC_NEWGRF_PRINT_QWORD_CURRENCY:
|
case SCC_NEWGRF_PRINT_DWORD_SIGNED: *it = stack.PopSignedDWord(); break;
|
||||||
case SCC_NEWGRF_PRINT_WORD_STRING_ID:
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_DATE_LONG:
|
case SCC_NEWGRF_PRINT_BYTE_HEX: *it = stack.PopUnsignedByte(); break;
|
||||||
case SCC_NEWGRF_PRINT_DWORD_DATE_LONG:
|
case SCC_NEWGRF_PRINT_QWORD_HEX: *it = stack.PopUnsignedQWord(); break;
|
||||||
case SCC_NEWGRF_PRINT_WORD_DATE_SHORT:
|
|
||||||
case SCC_NEWGRF_PRINT_DWORD_DATE_SHORT:
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_SPEED:
|
case SCC_NEWGRF_PRINT_WORD_SPEED:
|
||||||
case SCC_NEWGRF_PRINT_WORD_VOLUME_LONG:
|
case SCC_NEWGRF_PRINT_WORD_VOLUME_LONG:
|
||||||
case SCC_NEWGRF_PRINT_WORD_VOLUME_SHORT:
|
case SCC_NEWGRF_PRINT_WORD_VOLUME_SHORT:
|
||||||
|
case SCC_NEWGRF_PRINT_WORD_SIGNED: *it = stack.PopSignedWord(); break;
|
||||||
|
|
||||||
|
case SCC_NEWGRF_PRINT_WORD_HEX:
|
||||||
case SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG:
|
case SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG:
|
||||||
case SCC_NEWGRF_PRINT_WORD_WEIGHT_SHORT:
|
case SCC_NEWGRF_PRINT_WORD_WEIGHT_SHORT:
|
||||||
case SCC_NEWGRF_PRINT_WORD_POWER:
|
case SCC_NEWGRF_PRINT_WORD_POWER:
|
||||||
case SCC_NEWGRF_PRINT_DWORD_FORCE:
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_STATION_NAME:
|
case SCC_NEWGRF_PRINT_WORD_STATION_NAME:
|
||||||
case SCC_NEWGRF_PRINT_WORD_CARGO_NAME:
|
case SCC_NEWGRF_PRINT_WORD_UNSIGNED: *it = stack.PopUnsignedWord(); break;
|
||||||
if (parameters.GetDataLeft() < 1) {
|
|
||||||
Debug(misc, 0, "Too many NewGRF string parameters.");
|
case SCC_NEWGRF_PRINT_DWORD_FORCE:
|
||||||
return 0;
|
case SCC_NEWGRF_PRINT_DWORD_DATE_LONG:
|
||||||
}
|
case SCC_NEWGRF_PRINT_DWORD_DATE_SHORT:
|
||||||
break;
|
case SCC_NEWGRF_PRINT_DWORD_HEX: *it = stack.PopUnsignedDWord(); break;
|
||||||
|
|
||||||
|
/* Dates from NewGRFs have 1920-01-01 as their zero point, convert it to OpenTTD's epoch. */
|
||||||
|
case SCC_NEWGRF_PRINT_WORD_DATE_LONG:
|
||||||
|
case SCC_NEWGRF_PRINT_WORD_DATE_SHORT: *it = CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR + stack.PopUnsignedWord(); break;
|
||||||
|
|
||||||
|
case SCC_NEWGRF_DISCARD_WORD: stack.PopUnsignedWord(); break;
|
||||||
|
|
||||||
|
case SCC_NEWGRF_ROTATE_TOP_4_WORDS: stack.RotateTop4Words(); break;
|
||||||
|
case SCC_NEWGRF_PUSH_WORD: stack.PushWord(Utf8Consume(str)); break;
|
||||||
|
|
||||||
/* These string code take two string parameters, check there are at least that many available. */
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_CARGO_LONG:
|
case SCC_NEWGRF_PRINT_WORD_CARGO_LONG:
|
||||||
case SCC_NEWGRF_PRINT_WORD_CARGO_SHORT:
|
case SCC_NEWGRF_PRINT_WORD_CARGO_SHORT:
|
||||||
case SCC_NEWGRF_PRINT_WORD_CARGO_TINY:
|
case SCC_NEWGRF_PRINT_WORD_CARGO_TINY:
|
||||||
if (parameters.GetDataLeft() < 2) {
|
*it = GetCargoTranslation(stack.PopUnsignedWord(), stack.grffile);
|
||||||
Debug(misc, 0, "Too many NewGRF string parameters.");
|
*it = stack.PopUnsignedWord();
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
if (_newgrf_textrefstack.used && modify_parameters) {
|
case SCC_NEWGRF_PRINT_WORD_STRING_ID: {
|
||||||
/* There is data on the NewGRF text stack, and we want to move them to OpenTTD's string stack.
|
StringID stringid = MapGRFStringID(stack.grffile->grfid, GRFStringID{stack.PopUnsignedWord()});
|
||||||
* After this call, a new call is made with `modify_parameters` set to false when the string is finally formatted. */
|
*it = stringid;
|
||||||
switch (scc) {
|
/* We also need to handle the substring's stack usage. */
|
||||||
default: NOT_REACHED();
|
HandleNewGRFStringControlCodes(GetStringPtr(stringid), stack, params);
|
||||||
case SCC_NEWGRF_PRINT_BYTE_SIGNED: parameters.SetParam(0, _newgrf_textrefstack.PopSignedByte()); break;
|
break;
|
||||||
case SCC_NEWGRF_PRINT_QWORD_CURRENCY: parameters.SetParam(0, _newgrf_textrefstack.PopSignedQWord()); break;
|
|
||||||
|
|
||||||
case SCC_NEWGRF_PRINT_DWORD_CURRENCY:
|
|
||||||
case SCC_NEWGRF_PRINT_DWORD_SIGNED: parameters.SetParam(0, _newgrf_textrefstack.PopSignedDWord()); break;
|
|
||||||
|
|
||||||
case SCC_NEWGRF_PRINT_BYTE_HEX: parameters.SetParam(0, _newgrf_textrefstack.PopUnsignedByte()); break;
|
|
||||||
case SCC_NEWGRF_PRINT_QWORD_HEX: parameters.SetParam(0, _newgrf_textrefstack.PopUnsignedQWord()); break;
|
|
||||||
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_SPEED:
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_VOLUME_LONG:
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_VOLUME_SHORT:
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_SIGNED: parameters.SetParam(0, _newgrf_textrefstack.PopSignedWord()); break;
|
|
||||||
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_HEX:
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG:
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_WEIGHT_SHORT:
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_POWER:
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_STATION_NAME:
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_UNSIGNED: parameters.SetParam(0, _newgrf_textrefstack.PopUnsignedWord()); break;
|
|
||||||
|
|
||||||
case SCC_NEWGRF_PRINT_DWORD_FORCE:
|
|
||||||
case SCC_NEWGRF_PRINT_DWORD_DATE_LONG:
|
|
||||||
case SCC_NEWGRF_PRINT_DWORD_DATE_SHORT:
|
|
||||||
case SCC_NEWGRF_PRINT_DWORD_HEX: parameters.SetParam(0, _newgrf_textrefstack.PopUnsignedDWord()); break;
|
|
||||||
|
|
||||||
/* Dates from NewGRFs have 1920-01-01 as their zero point, convert it to OpenTTD's epoch. */
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_DATE_LONG:
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_DATE_SHORT: parameters.SetParam(0, CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR + _newgrf_textrefstack.PopUnsignedWord()); break;
|
|
||||||
|
|
||||||
case SCC_NEWGRF_DISCARD_WORD: _newgrf_textrefstack.PopUnsignedWord(); break;
|
|
||||||
|
|
||||||
case SCC_NEWGRF_ROTATE_TOP_4_WORDS: _newgrf_textrefstack.RotateTop4Words(); break;
|
|
||||||
case SCC_NEWGRF_PUSH_WORD: _newgrf_textrefstack.PushWord(Utf8Consume(str)); break;
|
|
||||||
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_CARGO_LONG:
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_CARGO_SHORT:
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_CARGO_TINY:
|
|
||||||
parameters.SetParam(0, GetCargoTranslation(_newgrf_textrefstack.PopUnsignedWord(), _newgrf_textrefstack.grffile));
|
|
||||||
parameters.SetParam(1, _newgrf_textrefstack.PopUnsignedWord());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_STRING_ID:
|
|
||||||
parameters.SetParam(0, MapGRFStringID(_newgrf_textrefstack.grffile->grfid, GRFStringID{_newgrf_textrefstack.PopUnsignedWord()}));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SCC_NEWGRF_PRINT_WORD_CARGO_NAME: {
|
|
||||||
CargoType cargo = GetCargoTranslation(_newgrf_textrefstack.PopUnsignedWord(), _newgrf_textrefstack.grffile);
|
|
||||||
parameters.SetParam(0, cargo < NUM_CARGO ? 1ULL << cargo : 0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
/* Consume additional parameter characters that follow the NewGRF string code. */
|
|
||||||
switch (scc) {
|
|
||||||
default: break;
|
|
||||||
|
|
||||||
case SCC_NEWGRF_PUSH_WORD:
|
case SCC_NEWGRF_PRINT_WORD_CARGO_NAME: {
|
||||||
Utf8Consume(str);
|
CargoType cargo = GetCargoTranslation(stack.PopUnsignedWord(), stack.grffile);
|
||||||
break;
|
*it = cargo < NUM_CARGO ? 1ULL << cargo : 0;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Emit OpenTTD's internal string code for the different NewGRF variants. */
|
/**
|
||||||
|
* Emit OpenTTD's internal string code for the different NewGRF string codes.
|
||||||
|
* @param scc NewGRF string code.
|
||||||
|
* @param[in,out] str String iterator, moved forward if SCC_NEWGRF_PUSH_WORD is found.
|
||||||
|
* @returns String code to use.
|
||||||
|
*/
|
||||||
|
char32_t RemapNewGRFStringControlCode(char32_t scc, const char **str)
|
||||||
|
{
|
||||||
switch (scc) {
|
switch (scc) {
|
||||||
default: NOT_REACHED();
|
default:
|
||||||
|
return scc;
|
||||||
|
|
||||||
case SCC_NEWGRF_PRINT_DWORD_SIGNED:
|
case SCC_NEWGRF_PRINT_DWORD_SIGNED:
|
||||||
case SCC_NEWGRF_PRINT_WORD_SIGNED:
|
case SCC_NEWGRF_PRINT_WORD_SIGNED:
|
||||||
case SCC_NEWGRF_PRINT_BYTE_SIGNED:
|
case SCC_NEWGRF_PRINT_BYTE_SIGNED:
|
||||||
|
@ -1007,9 +902,66 @@ char32_t RemapNewGRFStringControlCode(char32_t scc, const char **str, StringPara
|
||||||
return SCC_STATION_NAME;
|
return SCC_STATION_NAME;
|
||||||
|
|
||||||
/* These NewGRF string codes modify the NewGRF stack or otherwise do not map to OpenTTD string codes. */
|
/* These NewGRF string codes modify the NewGRF stack or otherwise do not map to OpenTTD string codes. */
|
||||||
|
case SCC_NEWGRF_PUSH_WORD:
|
||||||
|
Utf8Consume(str);
|
||||||
|
return 0;
|
||||||
|
|
||||||
case SCC_NEWGRF_DISCARD_WORD:
|
case SCC_NEWGRF_DISCARD_WORD:
|
||||||
case SCC_NEWGRF_ROTATE_TOP_4_WORDS:
|
case SCC_NEWGRF_ROTATE_TOP_4_WORDS:
|
||||||
case SCC_NEWGRF_PUSH_WORD:
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle control codes in a NewGRF string, processing the stack and filling parameters.
|
||||||
|
* @param str String to process.
|
||||||
|
* @param[in,out] stack Stack to use.
|
||||||
|
* @param[out] params Parameters to fill.
|
||||||
|
*/
|
||||||
|
static void HandleNewGRFStringControlCodes(const char *str, TextRefStack &stack, std::vector<StringParameter> ¶ms)
|
||||||
|
{
|
||||||
|
if (str == nullptr) return;
|
||||||
|
|
||||||
|
for (const char *p = str; *p != '\0'; /* nothing */) {
|
||||||
|
char32_t scc;
|
||||||
|
p += Utf8Decode(&scc, p);
|
||||||
|
RemapNewGRFStringControlCode(scc, &p, stack, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process the text ref stack for a GRF String and return its parameters.
|
||||||
|
* @param grffile GRFFile of string.
|
||||||
|
* @param stringid StringID of string.
|
||||||
|
* @param num_entries Number of temporary storage registers to import.
|
||||||
|
* @returns Parameters for GRF string.
|
||||||
|
*/
|
||||||
|
std::vector<StringParameter> GetGRFSringTextStackParameters(const GRFFile *grffile, StringID stringid, uint8_t num_entries)
|
||||||
|
{
|
||||||
|
if (stringid == INVALID_STRING_ID) return {};
|
||||||
|
|
||||||
|
const char *str = GetStringPtr(stringid);
|
||||||
|
if (str == nullptr) return {};
|
||||||
|
|
||||||
|
std::vector<StringParameter> params;
|
||||||
|
params.reserve(20);
|
||||||
|
|
||||||
|
TextRefStack stack{grffile, num_entries};
|
||||||
|
HandleNewGRFStringControlCodes(str, stack, params);
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a GRF string using the text ref stack for parameters.
|
||||||
|
* @param grffile GRFFile of string.
|
||||||
|
* @param grfstringid GRFStringID of string.
|
||||||
|
* @param num_entries Number of temporary storage registers to import.
|
||||||
|
* @returns Formatted string.
|
||||||
|
*/
|
||||||
|
std::string GetGRFStringWithTextStack(const struct GRFFile *grffile, GRFStringID grfstringid, uint8_t num_entries)
|
||||||
|
{
|
||||||
|
StringID stringid = GetGRFStringID(grffile->grfid, grfstringid);
|
||||||
|
auto params = GetGRFSringTextStackParameters(grffile, stringid, num_entries);
|
||||||
|
return GetStringWithArgs(stringid, params);
|
||||||
|
}
|
||||||
|
|
|
@ -28,10 +28,7 @@ void AddGRFTextToList(GRFTextWrapper &list, std::string_view text_to_add);
|
||||||
|
|
||||||
bool CheckGrfLangID(uint8_t lang_id, uint8_t grf_version);
|
bool CheckGrfLangID(uint8_t lang_id, uint8_t grf_version);
|
||||||
|
|
||||||
void StartTextRefStackUsage(const struct GRFFile *grffile, uint8_t numEntries, const uint32_t *values = nullptr);
|
std::vector<StringParameter> GetGRFSringTextStackParameters(const struct GRFFile *grffile, StringID stringid, uint8_t num_entries);
|
||||||
void StopTextRefStackUsage();
|
std::string GetGRFStringWithTextStack(const struct GRFFile *grffile, GRFStringID grfstringid, uint8_t num_entries);
|
||||||
bool UsingNewGRFTextStack();
|
|
||||||
struct TextRefStack *CreateTextRefStackBackup();
|
|
||||||
void RestoreTextRefStackBackup(struct TextRefStack *backup);
|
|
||||||
|
|
||||||
#endif /* NEWGRF_TEXT_H */
|
#endif /* NEWGRF_TEXT_H */
|
||||||
|
|
|
@ -245,11 +245,9 @@ public:
|
||||||
if (callback_res > 0x400) {
|
if (callback_res > 0x400) {
|
||||||
ErrorUnknownCallbackResult(spec->grf_prop.grfid, CBID_OBJECT_FUND_MORE_TEXT, callback_res);
|
ErrorUnknownCallbackResult(spec->grf_prop.grfid, CBID_OBJECT_FUND_MORE_TEXT, callback_res);
|
||||||
} else {
|
} else {
|
||||||
StringID message = GetGRFStringID(spec->grf_prop.grfid, GRFSTR_MISC_GRF_TEXT + callback_res);
|
std::string str = GetGRFStringWithTextStack(spec->grf_prop.grffile, GRFSTR_MISC_GRF_TEXT + callback_res, 6);
|
||||||
if (message != STR_NULL && message != STR_UNDEFINED) {
|
if (!str.empty()) {
|
||||||
StartTextRefStackUsage(spec->grf_prop.grffile, 6);
|
tr.top = DrawStringMultiLine(tr, str, TC_ORANGE);
|
||||||
tr.top = DrawStringMultiLine(tr, message, TC_ORANGE);
|
|
||||||
StopTextRefStackUsage();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1143,19 +1143,7 @@ static void FormatString(StringBuilder &builder, const char *str_arg, StringPara
|
||||||
*/
|
*/
|
||||||
std::string buffer;
|
std::string buffer;
|
||||||
StringBuilder dry_run_builder(buffer);
|
StringBuilder dry_run_builder(buffer);
|
||||||
if (UsingNewGRFTextStack()) {
|
FormatString(dry_run_builder, str_arg, args, case_index, game_script, true);
|
||||||
/* Values from the NewGRF text stack are only copied to the normal
|
|
||||||
* argv array at the time they are encountered. That means that if
|
|
||||||
* another string command references a value later in the string it
|
|
||||||
* would fail. We solve that by running FormatString twice. The first
|
|
||||||
* pass makes sure the argv array is correctly filled and the second
|
|
||||||
* pass can reference later values without problems. */
|
|
||||||
struct TextRefStack *backup = CreateTextRefStackBackup();
|
|
||||||
FormatString(dry_run_builder, str_arg, args, case_index, game_script, true);
|
|
||||||
RestoreTextRefStackBackup(backup);
|
|
||||||
} else {
|
|
||||||
FormatString(dry_run_builder, str_arg, args, case_index, game_script, true);
|
|
||||||
}
|
|
||||||
/* We have to restore the original offset here to to read the correct values. */
|
/* We have to restore the original offset here to to read the correct values. */
|
||||||
args.SetOffset(orig_offset);
|
args.SetOffset(orig_offset);
|
||||||
}
|
}
|
||||||
|
@ -1174,8 +1162,7 @@ static void FormatString(StringBuilder &builder, const char *str_arg, StringPara
|
||||||
|
|
||||||
if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
|
if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
|
||||||
/* We need to pass some stuff as it might be modified. */
|
/* We need to pass some stuff as it might be modified. */
|
||||||
StringParameters remaining = args.GetRemainingParameters();
|
b = RemapNewGRFStringControlCode(b, &str);
|
||||||
b = RemapNewGRFStringControlCode(b, &str, remaining, dry_run);
|
|
||||||
if (b == 0) continue;
|
if (b == 0) continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1677,11 +1664,7 @@ static void FormatString(StringBuilder &builder, const char *str_arg, StringPara
|
||||||
const GRFFile *grffile = e->GetGRF();
|
const GRFFile *grffile = e->GetGRF();
|
||||||
assert(grffile != nullptr);
|
assert(grffile != nullptr);
|
||||||
|
|
||||||
StartTextRefStackUsage(grffile, 6);
|
builder += GetGRFStringWithTextStack(grffile, GRFSTR_MISC_GRF_TEXT + callback, 6);
|
||||||
ArrayStringParameters<6> tmp_params;
|
|
||||||
GetStringWithArgs(builder, GetGRFStringID(grffile->grfid, GRFSTR_MISC_GRF_TEXT + callback), tmp_params);
|
|
||||||
StopTextRefStackUsage();
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -338,6 +338,6 @@ void GenerateTownNameString(StringBuilder &builder, size_t lang, uint32_t seed);
|
||||||
void GetTownName(StringBuilder &builder, const struct Town *t);
|
void GetTownName(StringBuilder &builder, const struct Town *t);
|
||||||
void GRFTownNameGenerate(StringBuilder &builder, uint32_t grfid, uint16_t gen, uint32_t seed);
|
void GRFTownNameGenerate(StringBuilder &builder, uint32_t grfid, uint16_t gen, uint32_t seed);
|
||||||
|
|
||||||
char32_t RemapNewGRFStringControlCode(char32_t scc, const char **str, StringParameters ¶meters, bool modify_parameters);
|
char32_t RemapNewGRFStringControlCode(char32_t scc, const char **str);
|
||||||
|
|
||||||
#endif /* STRINGS_INTERNAL_H */
|
#endif /* STRINGS_INTERNAL_H */
|
||||||
|
|
Loading…
Reference in New Issue