summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/industry_gui.cpp3
-rw-r--r--src/newgrf_spritegroup.cpp2
-rw-r--r--src/newgrf_spritegroup.h2
-rw-r--r--src/newgrf_text.cpp172
-rw-r--r--src/newgrf_text.h4
-rw-r--r--src/strings.cpp6
-rw-r--r--src/table/control_codes.h24
7 files changed, 201 insertions, 12 deletions
diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp
index 12f985457..76b9a8fea 100644
--- a/src/industry_gui.cpp
+++ b/src/industry_gui.cpp
@@ -503,7 +503,10 @@ static void IndustryViewWndProc(Window *w, WindowEvent *e)
StringID message = GetGRFStringID(ind->grf_prop.grffile->grfid, 0xD000 + callback_res);
if (message != STR_NULL && message != STR_UNDEFINED) {
y += 10;
+
+ PrepareTextRefStackUsage();
DrawString(2, y, message, 0);
+ StopTextRefStackUsage();
}
}
}
diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp
index 557c5dcfc..9bc708cd6 100644
--- a/src/newgrf_spritegroup.cpp
+++ b/src/newgrf_spritegroup.cpp
@@ -76,7 +76,7 @@ void InitializeSpriteGroupPool()
_spritegroup_count = 0;
}
-TemporaryStorageArray<uint, 0x110> _temp_store;
+TemporaryStorageArray<uint32, 0x110> _temp_store;
static inline uint32 GetVariable(const ResolverObject *object, byte variable, byte parameter, bool *available)
diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h
index 3f84b9af9..496ce5c36 100644
--- a/src/newgrf_spritegroup.h
+++ b/src/newgrf_spritegroup.h
@@ -17,7 +17,7 @@
*/
static inline uint32 GetRegister(uint i)
{
- extern TemporaryStorageArray<uint, 0x110> _temp_store;
+ extern TemporaryStorageArray<uint32, 0x110> _temp_store;
return _temp_store.Get(i);
}
diff --git a/src/newgrf_text.cpp b/src/newgrf_text.cpp
index f9ef1ec5c..067e080c5 100644
--- a/src/newgrf_text.cpp
+++ b/src/newgrf_text.cpp
@@ -21,6 +21,7 @@
#include "newgrf_text.h"
#include "table/control_codes.h"
#include "helpers.hpp"
+#include "date.h"
#define GRFTAB 28
#define TABSIZE 11
@@ -228,9 +229,9 @@ char *TranslateTTDPatchCodes(const char *str)
case 0x7B:
case 0x7C:
case 0x7D:
- case 0x7E: d += Utf8Encode(d, SCC_NUM); break;
- case 0x7F: d += Utf8Encode(d, SCC_CURRENCY); break;
- case 0x80: d += Utf8Encode(d, SCC_STRING); break;
+ case 0x7E:
+ case 0x7F:
+ case 0x80: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD + c - 0x7B); break;
case 0x81: {
StringID string;
string = *str++;
@@ -239,12 +240,12 @@ char *TranslateTTDPatchCodes(const char *str)
d += Utf8Encode(d, string);
break;
}
- case 0x82: d += Utf8Encode(d, SCC_DATE_TINY); break;
- case 0x83: d += Utf8Encode(d, SCC_DATE_SHORT); break;
- case 0x84: d += Utf8Encode(d, SCC_VELOCITY); break;
- case 0x85: d += Utf8Encode(d, SCC_SKIP); break;
- case 0x86: /* "Rotate down top 4 words on stack" */ break;
- case 0x87: d += Utf8Encode(d, SCC_VOLUME); break;
+ case 0x82:
+ case 0x83:
+ case 0x84: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_SPEED + c - 0x82); break;
+ case 0x85: d += Utf8Encode(d, SCC_NEWGRF_DISCARD_WORD); break;
+ case 0x86: d += Utf8Encode(d, SCC_NEWGRF_ROTATE_TOP_4_WORDS); break;
+ case 0x87: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_LITRES); break;
case 0x88: d += Utf8Encode(d, SCC_BLUE); break;
case 0x89: d += Utf8Encode(d, SCC_SILVER); break;
case 0x8A: d += Utf8Encode(d, SCC_GOLD); break;
@@ -262,6 +263,20 @@ char *TranslateTTDPatchCodes(const char *str)
case 0x96: d += Utf8Encode(d, SCC_GRAY); break;
case 0x97: d += Utf8Encode(d, SCC_DKBLUE); break;
case 0x98: d += Utf8Encode(d, SCC_BLACK); break;
+ case 0x9A:
+ switch (*str++) {
+ case 0: /* FALL THROUGH */
+ case 1: d += Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_CURRENCY); break;
+ case 3: {
+ uint16 tmp = *str++;
+ tmp |= (*str++) << 8;
+ d += Utf8Encode(d, SCC_NEWGRF_PUSH_WORD); d += Utf8Encode(d, tmp);
+ } break;
+ case 4: d += Utf8Encode(d, SCC_NEWGRF_UNPRINT); d += Utf8Encode(d, *str++); break;
+ default: grfmsg(1, "missing handler for extended format code"); break;
+ }
+ break;
+
case 0x9E: d += Utf8Encode(d, 0x20AC); break; // Euro
case 0x9F: d += Utf8Encode(d, 0x0178); break; // Y with diaeresis
case 0xA0: d += Utf8Encode(d, SCC_UPARROW); break;
@@ -481,3 +496,142 @@ void CleanUpStrings()
_num_grf_texts = 0;
}
+
+struct TextRefStack {
+ byte stack[0x30];
+ byte position;
+ bool used;
+
+ TextRefStack() : used(false) {}
+
+ uint8 PopUnsignedByte() { assert(this->position < lengthof(this->stack)); return this->stack[this->position++]; }
+ int8 PopSignedByte() { return (int8)this->PopUnsignedByte(); }
+
+ uint16 PopUnsignedWord() { return this->PopUnsignedByte() | (((uint16)this->PopUnsignedByte()) << 8); }
+ int16 PopSignedWord() { return (int32)this->PopUnsignedWord(); }
+
+ uint32 PopUnsignedDWord() { return this->PopUnsignedWord() | (((uint32)this->PopUnsignedWord()) << 16); }
+ int32 PopSignedDWord() { return (int32)this->PopUnsignedDWord(); }
+
+ uint64 PopUnsignedQWord() { return this->PopUnsignedDWord() | (((uint64)this->PopUnsignedDWord()) << 32); }
+ int64 PopSignedQWord() { return (int64)this->PopUnsignedQWord(); }
+
+ /** Rotate the top four words down: W1, W2, W3, W4 -> W4, W1, W2, W3 */
+ void RotateTop4Words()
+ {
+ byte tmp[2];
+ for (int i = 0; i < 2; i++) tmp[i] = this->stack[this->position + i + 6];
+ for (int i = 5; i >= 0; i--) this->stack[this->position + i + 2] = this->stack[this->position + i];
+ for (int i = 0; i < 2; i++) this->stack[this->position + i] = tmp[i];
+ }
+
+ void PushWord(uint16 word)
+ {
+ if (this->position >= 2) {
+ this->position -= 2;
+ } else {
+ for (uint i = lengthof(stack) - 3; i >= this->position; i--) {
+ this->stack[this->position + 2] = this->stack[this->position];
+ }
+ }
+ this->stack[this->position] = GB(word, 0, 8);
+ this->stack[this->position + 1] = GB(word, 8, 8);
+ }
+
+ void ResetStack() { this->used = true; this->position = 0; }
+};
+
+/** The stack that is used for TTDP compatible string code parsing */
+static TextRefStack _newgrf_textrefstack;
+
+/** Prepare the TTDP compatible string code parsing */
+void PrepareTextRefStackUsage()
+{
+ extern TemporaryStorageArray<uint32, 0x110> _temp_store;
+
+ _newgrf_textrefstack.ResetStack();
+
+ byte *p = _newgrf_textrefstack.stack;
+ for (uint i = 0; i < 6; i++) {
+ for (uint j = 0; j < 32; j += 8) {
+ *p = GB(_temp_store.Get(0x100 + i), 32 - j, 8);
+ p++;
+ }
+ }
+}
+
+/** 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 stack the current "stack"
+ * @return the string control code to "execute" now
+ */
+uint RemapNewGRFStringControlCode(uint scc, char **buff, const char **str, int64 *argv)
+{
+ if (_newgrf_textrefstack.used) {
+ switch (scc) {
+ default: NOT_REACHED();
+ case SCC_NEWGRF_PRINT_SIGNED_BYTE: *argv = _newgrf_textrefstack.PopSignedByte(); break;
+ case SCC_NEWGRF_PRINT_SIGNED_WORD: *argv = _newgrf_textrefstack.PopSignedWord(); break;
+ case SCC_NEWGRF_PRINT_QWORD_CURRENCY: *argv = _newgrf_textrefstack.PopUnsignedQWord(); break;
+
+ case SCC_NEWGRF_PRINT_DWORD_CURRENCY:
+ case SCC_NEWGRF_PRINT_DWORD: *argv = _newgrf_textrefstack.PopSignedDWord(); break;
+
+ case SCC_NEWGRF_PRINT_WORD_SPEED:
+ case SCC_NEWGRF_PRINT_WORD_LITRES:
+ case SCC_NEWGRF_PRINT_UNSIGNED_WORD: *argv = _newgrf_textrefstack.PopUnsignedWord(); break;
+
+ case SCC_NEWGRF_PRINT_DATE:
+ case SCC_NEWGRF_PRINT_MONTH_YEAR: *argv = _newgrf_textrefstack.PopSignedWord() + DAYS_TILL_ORIGINAL_BASE_YEAR; 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_UNPRINT: *buff -= Utf8Consume(str); break;
+
+ case SCC_NEWGRF_PRINT_STRING_ID:
+ *argv = _newgrf_textrefstack.PopUnsignedWord();
+ if (*argv == STR_NULL) *argv = STR_EMPTY;
+ break;
+ }
+ }
+
+ switch (scc) {
+ default: NOT_REACHED();
+ case SCC_NEWGRF_PRINT_DWORD:
+ case SCC_NEWGRF_PRINT_SIGNED_WORD:
+ case SCC_NEWGRF_PRINT_SIGNED_BYTE:
+ case SCC_NEWGRF_PRINT_UNSIGNED_WORD:
+ return SCC_NUM;
+
+ case SCC_NEWGRF_PRINT_DWORD_CURRENCY:
+ case SCC_NEWGRF_PRINT_QWORD_CURRENCY:
+ return SCC_CURRENCY;
+
+ case SCC_NEWGRF_PRINT_STRING_ID:
+ return SCC_STRING;
+
+ case SCC_NEWGRF_PRINT_DATE:
+ return SCC_DATE_LONG;
+
+ case SCC_NEWGRF_PRINT_MONTH_YEAR:
+ return SCC_DATE_TINY;
+
+ case SCC_NEWGRF_PRINT_WORD_SPEED:
+ return SCC_VELOCITY;
+
+ case SCC_NEWGRF_PRINT_WORD_LITRES:
+ return SCC_VOLUME;
+
+ case SCC_NEWGRF_DISCARD_WORD:
+ case SCC_NEWGRF_ROTATE_TOP_4_WORDS:
+ case SCC_NEWGRF_PUSH_WORD:
+ case SCC_NEWGRF_UNPRINT:
+ return 0;
+ }
+}
diff --git a/src/newgrf_text.h b/src/newgrf_text.h
index ec0fcdc91..6db086d0e 100644
--- a/src/newgrf_text.h
+++ b/src/newgrf_text.h
@@ -15,4 +15,8 @@ char *TranslateTTDPatchCodes(const char *str);
bool CheckGrfLangID(byte lang_id, byte grf_version);
+void PrepareTextRefStackUsage();
+void StopTextRefStackUsage();
+uint RemapNewGRFStringControlCode(uint scc, char **buff, const char **str, int64 *argv);
+
#endif /* NEWGRF_TEXT_H */
diff --git a/src/strings.cpp b/src/strings.cpp
index 6916d2aaa..a82bdd15a 100644
--- a/src/strings.cpp
+++ b/src/strings.cpp
@@ -531,6 +531,12 @@ static char* FormatString(char* buff, const char* str, const int64* argv, uint c
uint modifier = 0;
while ((b = Utf8Consume(&str)) != '\0') {
+ if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
+ /* We need to pass some stuff as it might be modified; oh boy. */
+ b = RemapNewGRFStringControlCode(b, &buff, &str, (int64*)argv);
+ if (b == 0) continue;
+ }
+
switch (b) {
case SCC_SETX: // {SETX}
if (buff + Utf8CharLen(SCC_SETX) + 1 < last) {
diff --git a/src/table/control_codes.h b/src/table/control_codes.h
index e853ca898..5c1ae73e7 100644
--- a/src/table/control_codes.h
+++ b/src/table/control_codes.h
@@ -6,7 +6,7 @@
/* List of string control codes used for string formatting, displaying, and
* by strgen to generate the language files. */
-enum {
+enum StringControlCode {
SCC_CONTROL_START = 0xE000,
SCC_CONTROL_END = 0xE1FF,
@@ -90,6 +90,28 @@ enum {
SCC_BLACK,
SCC_PREVIOUS_COLOUR,
+ /**
+ * The next variables are part of a NewGRF subsystem for creating text strings.
+ * It uses a "stack" of bytes and reads from there.
+ */
+ SCC_NEWGRF_FIRST,
+ SCC_NEWGRF_PRINT_DWORD = SCC_NEWGRF_FIRST, ///< Read 4 bytes from the stack
+ SCC_NEWGRF_PRINT_SIGNED_WORD, ///< Read 2 bytes from the stack as signed value
+ SCC_NEWGRF_PRINT_SIGNED_BYTE, ///< Read 1 bytes from the stack as signed value
+ SCC_NEWGRF_PRINT_UNSIGNED_WORD, ///< Read 2 bytes from the stack as unsigned value
+ SCC_NEWGRF_PRINT_DWORD_CURRENCY, ///< Read 4 bytes from the stack as currency
+ SCC_NEWGRF_PRINT_STRING_ID, ///< Read 2 bytes from the stack as String ID
+ SCC_NEWGRF_PRINT_DATE, ///< Read 2 bytes from the stack as base 1920 date
+ SCC_NEWGRF_PRINT_MONTH_YEAR, ///< Read 2 bytes from the stack as base 1920 date
+ SCC_NEWGRF_PRINT_WORD_SPEED, ///< Read 2 bytes from the stack as signed speed
+ SCC_NEWGRF_PRINT_WORD_LITRES, ///< Read 2 bytes from the stack as signed litres
+ SCC_NEWGRF_PRINT_QWORD_CURRENCY, ///< Read 8 bytes from the stack as currency
+ SCC_NEWGRF_PUSH_WORD, ///< Pushes 2 bytes onto the stack
+ SCC_NEWGRF_UNPRINT, ///< "Unprints" the given number of bytes from the string
+ SCC_NEWGRF_DISCARD_WORD, ///< Discard the next two bytes
+ SCC_NEWGRF_ROTATE_TOP_4_WORDS, ///< Rotate the top 4 words of the stack (W4 W1 W2 W3)
+ SCC_NEWGRF_LAST = SCC_NEWGRF_ROTATE_TOP_4_WORDS,
+
/* Special printable symbols.
* These are mapped to the original glyphs */
SCC_LESSTHAN = SCC_SPRITE_START + 0x3C,