- allow localization of Hexen's original ACS strings.

This way of looking up strings is intentionally limited to content from Hexen.wad and Hexdd.wad.
This commit is contained in:
Christoph Oelckers 2019-02-05 11:51:19 +01:00
parent dc86138219
commit 546d3d1bf5
4 changed files with 183 additions and 21 deletions

View file

@ -2863,7 +2863,7 @@ void MapLoader::LoadBehavior(MapData * map)
{ {
if (map->Size(ML_BEHAVIOR) > 0) if (map->Size(ML_BEHAVIOR) > 0)
{ {
Level->Behaviors.LoadModule(-1, &map->Reader(ML_BEHAVIOR), map->Size(ML_BEHAVIOR)); Level->Behaviors.LoadModule(-1, &map->Reader(ML_BEHAVIOR), map->Size(ML_BEHAVIOR), map->lumpnum);
} }
if (!Level->Behaviors.CheckAllGood()) if (!Level->Behaviors.CheckAllGood())
{ {

View file

@ -1942,7 +1942,7 @@ void FBehaviorContainer::LoadDefaultModules ()
} }
} }
FBehavior *FBehaviorContainer::LoadModule (int lumpnum, FileReader *fr, int len) FBehavior *FBehaviorContainer::LoadModule (int lumpnum, FileReader *fr, int len, int reallumpnum)
{ {
if (lumpnum == -1 && fr == NULL) return NULL; if (lumpnum == -1 && fr == NULL) return NULL;
@ -1955,7 +1955,7 @@ FBehavior *FBehaviorContainer::LoadModule (int lumpnum, FileReader *fr, int len)
} }
FBehavior * behavior = new FBehavior (); FBehavior * behavior = new FBehavior ();
if (behavior->Init(Level, lumpnum, fr, len)) if (behavior->Init(Level, lumpnum, fr, len, reallumpnum))
{ {
return behavior; return behavior;
} }
@ -2209,7 +2209,7 @@ FBehavior::FBehavior()
} }
bool FBehavior::Init(FLevelLocals *Level, int lumpnum, FileReader * fr, int len) bool FBehavior::Init(FLevelLocals *Level, int lumpnum, FileReader * fr, int len, int reallumpnum)
{ {
uint8_t *object; uint8_t *object;
int i; int i;
@ -2303,6 +2303,8 @@ bool FBehavior::Init(FLevelLocals *Level, int lumpnum, FileReader * fr, int len)
// Forget about the compatibility cruft at the end of the lump // Forget about the compatibility cruft at the end of the lump
DataSize = LittleLong(((uint32_t *)object)[1]) - 8; DataSize = LittleLong(((uint32_t *)object)[1]) - 8;
} }
ShouldLocalize = false;
} }
else else
{ {
@ -2316,6 +2318,17 @@ bool FBehavior::Init(FLevelLocals *Level, int lumpnum, FileReader * fr, int len)
StringTable = LittleLong(((uint32_t *)Data)[1]); StringTable = LittleLong(((uint32_t *)Data)[1]);
StringTable += LittleLong(((uint32_t *)(Data + StringTable))[0]) * 12 + 4; StringTable += LittleLong(((uint32_t *)(Data + StringTable))[0]) * 12 + 4;
UnescapeStringTable(Data + StringTable, Data, false); UnescapeStringTable(Data + StringTable, Data, false);
// If this is an original Hexen BEHAVIOR, set up some localization info for it. Original Hexen BEHAVIORs are always in the old format.
if ((Level->flags2 & LEVEL2_HEXENHACK) && gameinfo.gametype == GAME_Hexen && lumpnum == -1 && reallumpnum > 0)
{
int fileno = Wads.GetLumpFile(reallumpnum);
const char * filename = Wads.GetWadName(fileno);
if (!stricmp(filename, "HEXEN.WAD") || !stricmp(filename, "HEXDD.WAD"))
{
ShouldLocalize = true;
}
}
} }
else else
{ {
@ -3193,7 +3206,7 @@ uint8_t *FBehavior::NextChunk (uint8_t *chunk) const
return NULL; return NULL;
} }
const char *FBehaviorContainer::LookupString (uint32_t index) const char *FBehaviorContainer::LookupString (uint32_t index, bool forprint)
{ {
uint32_t lib = index >> LIBRARYID_SHIFT; uint32_t lib = index >> LIBRARYID_SHIFT;
@ -3205,10 +3218,10 @@ const char *FBehaviorContainer::LookupString (uint32_t index)
{ {
return NULL; return NULL;
} }
return StaticModules[lib]->LookupString (index & 0xffff); return StaticModules[lib]->LookupString (index & 0xffff, forprint);
} }
const char *FBehavior::LookupString (uint32_t index) const const char *FBehavior::LookupString (uint32_t index, bool forprint) const
{ {
if (StringTable == 0) if (StringTable == 0)
{ {
@ -3220,7 +3233,26 @@ const char *FBehavior::LookupString (uint32_t index) const
if (index >= list[0]) if (index >= list[0])
return NULL; // Out of range for this list; return NULL; // Out of range for this list;
return (const char *)(Data + list[1+index]);
const char *s = (const char *)(Data + list[1 + index]);
// Allow translations for Hexen's original strings.
// This synthesizes a string label and looks it up.
// It will only do this for original Hexen maps and PCD_PRINTSTRING operations.
// For localizing user content better solutions exist so this hack won't be available as an editing feature.
if (ShouldLocalize && forprint)
{
FString token = s;
token.ToUpper();
token.ReplaceChars(".,-+!?", ' ');
token.Substitute(" ", "");
token.Truncate(5);
FStringf label("TXT_ACS_%s_%d_%.5s", Level->MapName.GetChars(), index, token);
auto p = GStrings[label];
if (p) return p;
}
return s;
} }
else else
{ {
@ -8362,7 +8394,7 @@ scriptwait:
case PCD_PRINTSTRING: case PCD_PRINTSTRING:
case PCD_PRINTLOCALIZED: case PCD_PRINTLOCALIZED:
lookup = Level->Behaviors.LookupString (STACK(1)); lookup = Level->Behaviors.LookupString (STACK(1), true);
if (pcd == PCD_PRINTLOCALIZED) if (pcd == PCD_PRINTLOCALIZED)
{ {
lookup = GStrings(lookup); lookup = GStrings(lookup);

View file

@ -350,7 +350,7 @@ class FBehavior
public: public:
FBehavior (); FBehavior ();
~FBehavior (); ~FBehavior ();
bool Init(FLevelLocals *l, int lumpnum, FileReader * fr = NULL, int len = 0); bool Init(FLevelLocals *l, int lumpnum, FileReader * fr = NULL, int len = 0, int reallumpnum = -1);
bool IsGood (); bool IsGood ();
uint8_t *FindChunk (uint32_t id) const; uint8_t *FindChunk (uint32_t id) const;
@ -378,7 +378,7 @@ public:
const char *GetModuleName() const { return ModuleName; } const char *GetModuleName() const { return ModuleName; }
ACSProfileInfo *GetFunctionProfileData(int index) { return index >= 0 && index < NumFunctions ? &FunctionProfileData[index] : NULL; } ACSProfileInfo *GetFunctionProfileData(int index) { return index >= 0 && index < NumFunctions ? &FunctionProfileData[index] : NULL; }
ACSProfileInfo *GetFunctionProfileData(ScriptFunction *func) { return GetFunctionProfileData((int)(func - (ScriptFunction *)Functions)); } ACSProfileInfo *GetFunctionProfileData(ScriptFunction *func) { return GetFunctionProfileData((int)(func - (ScriptFunction *)Functions)); }
const char *LookupString (uint32_t index) const; const char *LookupString (uint32_t index, bool forprint = false) const;
BoundsCheckingArray<int32_t *, NUM_MAPVARS> MapVars; BoundsCheckingArray<int32_t *, NUM_MAPVARS> MapVars;
@ -386,26 +386,28 @@ public:
private: private:
struct ArrayInfo; struct ArrayInfo;
ACSFormat Format;
FLevelLocals *Level; FLevelLocals *Level;
int LumpNum;
uint8_t *Data; uint8_t *Data;
int DataSize;
uint8_t *Chunks; uint8_t *Chunks;
ScriptPtr *Scripts; ScriptPtr *Scripts;
int NumScripts;
ScriptFunction *Functions; ScriptFunction *Functions;
ACSProfileInfo *FunctionProfileData; ACSProfileInfo *FunctionProfileData;
int NumFunctions;
ArrayInfo *ArrayStore; ArrayInfo *ArrayStore;
int NumArrays;
ArrayInfo **Arrays; ArrayInfo **Arrays;
ACSFormat Format;
int LumpNum;
int DataSize;
int NumScripts;
int NumFunctions;
int NumArrays;
int NumTotalArrays; int NumTotalArrays;
uint32_t StringTable; uint32_t StringTable;
uint32_t LibraryID;
bool ShouldLocalize;
int32_t MapVarStore[NUM_MAPVARS]; int32_t MapVarStore[NUM_MAPVARS];
TArray<FBehavior *> Imports; TArray<FBehavior *> Imports;
uint32_t LibraryID;
char ModuleName[9]; char ModuleName[9];
TArray<int> JumpPoints; TArray<int> JumpPoints;
@ -432,7 +434,7 @@ struct FBehaviorContainer
FBehaviorContainer(FLevelLocals *l) : Level(l) {} FBehaviorContainer(FLevelLocals *l) : Level(l) {}
FBehavior *LoadModule(int lumpnum, FileReader *fr = nullptr, int len = 0); FBehavior *LoadModule(int lumpnum, FileReader *fr = nullptr, int len = 0, int reallumpnum = -1);
void LoadDefaultModules(); void LoadDefaultModules();
void UnloadModules(); void UnloadModules();
bool CheckAllGood(); bool CheckAllGood();
@ -443,7 +445,7 @@ struct FBehaviorContainer
void UnlockLevelVarStrings(int levelnum); void UnlockLevelVarStrings(int levelnum);
const ScriptPtr *FindScript(int script, FBehavior *&module); const ScriptPtr *FindScript(int script, FBehavior *&module);
const char *LookupString(uint32_t index); const char *LookupString(uint32_t index, bool forprint = false);
void StartTypedScripts(uint16_t type, AActor *activator, bool always, int arg1 = 0, bool runNow = false); void StartTypedScripts(uint16_t type, AActor *activator, bool always, int arg1 = 0, bool runNow = false);
void StopMyScripts(AActor *actor); void StopMyScripts(AActor *actor);
void ArrangeScriptProfiles(TArray<ProfileCollector> &profiles); void ArrangeScriptProfiles(TArray<ProfileCollector> &profiles);

View file

@ -2943,3 +2943,131 @@ MSG_TALISMANRED = "You have a feeling that it wasn't to be touched...";
MSG_TALISMANGREEN = "Whatever it is, it doesn't belong in this world..."; MSG_TALISMANGREEN = "Whatever it is, it doesn't belong in this world...";
MSG_TALISMANBLUE = "It must do something..."; MSG_TALISMANBLUE = "It must do something...";
MSG_TALISMANPOWER = "You have super strength!"; MSG_TALISMANPOWER = "You have super strength!";
// Strings from Hexen's IWAD scripts. Technically they are not needed here for English, they are mainly meant to be documentation for translating.
TXT_ACS_map01_5_THEDO = "THE DOOR IS LOCKED";
TXT_ACS_map02_9_GREET = "GREETINGS, MORTAL";
TXT_ACS_map02_11_AREYO = "ARE YOU READY TO DIE?";
TXT_ACS_map02_20_ADOOR = "A DOOR OPENED ON THE GUARDIAN OF ICE";
TXT_ACS_map03_12_THISP = "THIS PATH IS BARRED";
TXT_ACS_map04_9_ONEHA = "ONE HALF OF THE PUZZLE HAS BEEN SOLVED";
TXT_ACS_map04_10_ONTHE = "ON THE SEVEN PORTALS";
TXT_ACS_map04_11_ONETH = "ONE THIRD OF THE PUZZLE HAS BEEN SOLVED";
TXT_ACS_map04_12_STAIR = "STAIRS HAVE RISEN ON THE SEVEN PORTALS";
TXT_ACS_map05_6_ONETH = "ONE THIRD OF THE PUZZLE HAS BEEN SOLVED";
TXT_ACS_map05_7_ONTHE = "ON THE SEVEN PORTALS";
TXT_ACS_map05_8_STAIR = "STAIRS HAVE RISEN ON THE SEVEN PORTALS";
TXT_ACS_map05_9_YOUHA = "YOU HAVE TO FIND ANOTHER SWITCH...";
TXT_ACS_map05_10_STONE = "STONES GRIND ON THE SEVEN PORTALS";
TXT_ACS_map08_6_ONESI = "ONE SIXTH OF THE PUZZLE HAS BEEN SOLVED";
TXT_ACS_map08_7_ONTHE = "ON THE SHADOW WOOD";
TXT_ACS_map08_10_THEDO = "THE DOOR IS BARRED FROM THE INSIDE";
TXT_ACS_map08_11_YOUHE = "YOU HEAR A DOOR OPEN IN THE DISTANCE";
TXT_ACS_map09_6_ONESI = "ONE SIXTH OF THE PUZZLE HAS BEEN SOLVED";
TXT_ACS_map09_7_ONTHE = "ON THE SHADOW WOOD";
TXT_ACS_map10_6_ONESI = "ONE SIXTH OF THE PUZZLE HAS BEEN SOLVED";
TXT_ACS_map10_7_ONTHE = "ON THE SHADOW WOOD";
TXT_ACS_map11_0_ETTIN = " ETTINS LEFT";
TXT_ACS_map11_1_YOUWA = "YOU WAITED TOO LONG, NOW YOU DIE!";
TXT_ACS_map11_7_ADOOR = "A DOOR OPENED ON THE FORSAKEN OUTPOST";
TXT_ACS_map12_9_THISD = "THIS DOOR WON'T OPEN YET";
TXT_ACS_map13_11_MYSER = "MY SERVANTS CAN SMELL YOUR BLOOD, HUMAN";
TXT_ACS_map21_0_ADOOR = "A DOOR OPENED IN THE GIBBET";
TXT_ACS_map21_2_THEDO = "THE DOOR IS BARRED FROM THE INSIDE";
TXT_ACS_map22_3_APLAT = "A PLATFORM HAS LOWERED IN THE TOWER";
TXT_ACS_map22_27_YOUHA = "YOU HAVE PLAYED THIS GAME TOO LONG, MORTAL...";
TXT_ACS_map22_29_ITHIN = "I THINK I SHALL REMOVE YOU FROM THE BOARD";
TXT_ACS_map23_10_YOUHE = "YOU HEAR A DOOR OPEN UPSTAIRS";
TXT_ACS_map27_8_WORSH = "WORSHIP ME, AND I MAY YET BE MERCIFUL";
TXT_ACS_map27_10_THENA = "THEN AGAIN, MAYBE NOT";
TXT_ACS_map28_6_ONENI = "ONE NINTH OF THE PUZZLE HAS BEEN SOLVED";
TXT_ACS_map28_7_ONTHE = "ON THE MONASTERY";
TXT_ACS_map30_6_ONENI = "ONE NINTH OF THE PUZZLE HAS BEEN SOLVED";
TXT_ACS_map30_7_ONTHE = "ON THE MONASTERY";
TXT_ACS_map34_1_ONENI = "ONE NINTH OF THE PUZZLE HAS BEEN SOLVED";
TXT_ACS_map34_2_ONTHE = "ON THE MONASTERY";
TXT_ACS_map35_0_THEPO = "THE PORTAL HAS BEEN SEALED";
TXT_ACS_map35_1_CHOOS = "CHOOSE YOUR FATE";
TXT_ACS_map35_3_THEDO = "THE DOOR IS BARRED FROM THE INSIDE";
TXT_ACS_map35_12_AREYO = "ARE YOU STRONG ENOUGH";
TXT_ACS_map35_14_TOFAC = "TO FACE YOUR OWN MASTERS?";
// Deathkings texts
TXT_ACS_map33_6_YOUDA = "YOU DARE BATTLE IN THE READY ROOM?";
TXT_ACS_map33_7_FORTH = "FOR THAT, YOU SHALL DIE!";
TXT_ACS_map41_6_THEWA = "THE WATERFALL IS OPEN";
TXT_ACS_map41_7_THEWA = "THE WATERFALL IS BLOCKED";
TXT_ACS_map41_8_ADOOR = "A DOOR HAS OPENED IN THE CHAPEL";
TXT_ACS_map42_4_NOWTH = "NOW THAT'S ODD...";
TXT_ACS_map44_1_THREE = "THREE MORE PARTS OF THE PUZZLE REMAIN";
TXT_ACS_map44_2_TWOMO = "TWO MORE PARTS OF THE PUZZLE REMAIN";
TXT_ACS_map44_3_ONEMO = "ONE MORE PART OF THE PUZZLE REMAINS";
TXT_ACS_map44_4_THEPU = "THE PUZZLE IS COMPLETE";
TXT_ACS_map44_6_YOUHA = "YOU HAVE NOT COMPLETED THE PUZZLE";
TXT_ACS_map44_8_THEFL = "THE FLOOR IS NOT SAFE!";
TXT_ACS_map44_10_ONETH = "ONE THIRD OF THE PUZZLE IS SOLVED";
TXT_ACS_map44_11_TWOTH = "TWO THIRDS OF THE PUZZLE IS SOLVED";
TXT_ACS_map45_1_YOUHE = "YOU HEAR A PLATFORM MOVING IN THE DISTANCE";
TXT_ACS_map46_0_ITISD = "IT IS DONE...";
TXT_ACS_map46_1_YOUHA = "YOU HAVE NOT COMPLETED THE PUZZLE";
TXT_ACS_map46_2_I'MWA = "I'M WARNING YOU...";
TXT_ACS_map46_3_STUBB = "STUBBORN, AREN'T YOU?";
TXT_ACS_map46_4_ANDST = "AND STUPID, TOO";
TXT_ACS_map46_8_ONEFO = "ONE FOURTH OF THIS PUZZLE IS COMPLETE";
TXT_ACS_map46_9_BADCH = "BAD CHOICE...";
TXT_ACS_map47_2_THESY = "THE SYMBOLS ARE NOT ALIGNED";
TXT_ACS_map48_2_THEDO = "THE DOOR WON'T OPEN FROM THIS SIDE";
TXT_ACS_map50_1_THEDO = "THE DOOR IS BARRED FROM THE OUTSIDE";
TXT_ACS_map51_5_SACRI = "SACRILEGE !";
TXT_ACS_map51_6_YOUHA = "YOU HAVE DEFILED ERIC'S TOMB !!";
TXT_ACS_map51_7_ANDNO = "AND NOW YOU DIE !!!";
TXT_ACS_map51_8_ONETH = "ONE THIRD OF THE PUZZLE IS SOLVED";
TXT_ACS_map51_9_TWOTH = "TWO THIRDS OF THE PUZZLE IS SOLVED";
TXT_ACS_map51_10_THECR = "THE CRYPT IS OPEN";
TXT_ACS_map51_11_BEWAR = "BEWARE THE SPIDER'S TOMB";
TXT_ACS_map51_13_YOUHE = "YOU HEAR A PLATFORM RISE OUTSIDE";
TXT_ACS_map51_14_DOYOU = "DO YOU FEEL LUCKY?";
TXT_ACS_map51_15_YOUGU = "YOU GUESSED WRONG!";
TXT_ACS_map51_16_GOODG = "GOOD GUESS";
TXT_ACS_map51_17_CANYO = "CAN YOU DO ALL THE SCRIPTING FOR MY LEVEL?";
TXT_ACS_map51_18_DON'T = "DON'T TOUCH MY GLOPPY";
TXT_ACS_map51_19_VORPA = "VORPAL ?!?!?!";
TXT_ACS_map51_20_GIMME = "GIMME SOME SUGAR, BABY";
TXT_ACS_map51_21_DUHUH = "DUH-UHHH...";
TXT_ACS_map51_22_FILMI = "FILM IN AN HOUR?";
TXT_ACS_map51_23_IDON' = "I DON'T EVEN GET MY OWN TOMBSTONE - CF";
TXT_ACS_map51_24_LETNO = "LET NO BLOOD BE SPILT";
TXT_ACS_map51_25_LETNO = "LET NO HAND BE RAISED IN ANGER";
TXT_ACS_map52_9_WHODA = "WHO DARES DISTURB OUR SLUMBER?";
TXT_ACS_map52_10_THEWA = "THE WAY IS OPEN";
TXT_ACS_map53_2_YOUHA = "YOU HAVE ";
TXT_ACS_map53_3_SWITC = " SWITCHES LEFT";
TXT_ACS_map53_4_YOUHA = "YOU HAVE ONLY ";
TXT_ACS_map53_5_SWITC = " SWITCH LEFT";
TXT_ACS_map53_6_THEEX = "THE EXIT IS OPEN";
TXT_ACS_map54_1_THEDO = "THE DOORS WON'T OPEN FROM THIS SIDE";
TXT_ACS_map54_4_THEDO = "THE DOORS ARE OPEN...";
TXT_ACS_map54_5_IFYOU = "...IF YOU ARE READY";
TXT_ACS_map54_9_ADOOR = "A DOOR HAS OPENED";
TXT_ACS_map54_10_ONTHE = "ON THE CHANTRY";
TXT_ACS_map54_11_ABRID = "A BRIDGE HAS BEEN BUILT";
TXT_ACS_map54_12_ONTHE = "ON THE ABATTOIR";
TXT_ACS_map54_13_ASTAI = "A STAIR HAS BEEN BUILT";
TXT_ACS_map54_14_ONTHE = "ON THE DARK WATCH";
TXT_ACS_map54_15_ONEGE = "ONE GEAR HAS BEEN PLACED";
TXT_ACS_map54_16_GEARS = " GEARS HAVE BEEN PLACED";
TXT_ACS_map54_17_ABARR = "A BARRICADE HAS OPENED";
TXT_ACS_map54_18_ONTHE = "ON THE CLOACA";
TXT_ACS_map54_20_THEWA = "THE WAY BACK IS OPEN";
TXT_ACS_map55_9_THEDO = "THE DOOR IS BARRED FROM THE INSIDE";
TXT_ACS_map56_0_YOUDA = "YOU DARE PLUNDER THE TOMB";
TXT_ACS_map56_1_OFTHE = "OF THE EXECUTIONER?";
TXT_ACS_map56_2_PREPA = "PREPARE TO DIE";
TXT_ACS_map59_1_YOUHA = "YOU HAVE ";
TXT_ACS_map59_2_MORES = " MORE SWITCHES TO FIND";
TXT_ACS_map59_3_YOUHA = "YOU HAVE ONLY ";
TXT_ACS_map59_4_SWITC = " SWITCH LEFT";
TXT_ACS_map59_5_THEWA = "THE WAY TO THE TOWER IS OPEN";
TXT_ACS_map60_3_THEWA = "THE WAY IS OPEN";