static TArray<FString> PSR_ReadBaseInstalls(FScanner &sc) { TArray<FString> result; // Get a list of possible install directories. while(sc.GetToken()) { if(sc.TokenType == '}') break; sc.TokenMustBe(TK_StringConst); FString key(sc.String); if(key.Left(18).CompareNoCase("BaseInstallFolder_") == 0) { sc.MustGetToken(TK_StringConst); result.Push(FString(sc.String) + "/steamapps/common"); } else { if(sc.CheckToken('{')) PSR_FindEndBlock(sc); else sc.MustGetToken(TK_StringConst); } } return result; }
static bool PSR_FindAndEnterBlock(FScanner &sc, const char* keyword) { // Finds a block with a given keyword and then enter it (opening brace) // Should be closed with PSR_FindEndBlock while(sc.GetToken()) { if(sc.TokenType == '}') { sc.UnGet(); return false; } sc.TokenMustBe(TK_StringConst); if(!sc.Compare(keyword)) { if(!sc.CheckToken(TK_StringConst)) PSR_SkipBlock(sc); } else { sc.MustGetToken('{'); return true; } } return false; }
FFont::FFont (const char *name, const char *nametemplate, const char *filetemplate, int lfirst, int lcount, int start, int fdlump, int spacewidth, bool notranslate, bool iwadonly) { int i; FTextureID lump; char buffer[12]; int maxyoffs; bool doomtemplate = (nametemplate && (gameinfo.gametype & GAME_DoomChex)) ? strncmp (nametemplate, "STCFN", 5) == 0 : false; DVector2 Scale = { 1, 1 }; noTranslate = notranslate; Lump = fdlump; GlobalKerning = false; FontName = name; Next = FirstFont; FirstFont = this; Cursor = '_'; ActiveColors = 0; SpaceWidth = 0; FontHeight = 0; uint8_t pp = 0; for (auto &p : PatchRemap) p = pp++; translateUntranslated = false; int FixedWidth = 0; maxyoffs = 0; TMap<int, FTexture*> charMap; int minchar = INT_MAX; int maxchar = INT_MIN; // Read the font's configuration. // This will not be done for the default fonts, because they are not atomic and the default content does not need it. TArray<FolderEntry> folderdata; if (filetemplate != nullptr) { FStringf path("fonts/%s/", filetemplate); // If a name template is given, collect data from all resource files. // For anything else, each folder is being treated as an atomic, self-contained unit and mixing from different glyph sets is blocked. Wads.GetLumpsInFolder(path, folderdata, nametemplate == nullptr); //if (nametemplate == nullptr) { FStringf infpath("fonts/%s/font.inf", filetemplate); unsigned index = folderdata.FindEx([=](const FolderEntry &entry) { return infpath.CompareNoCase(entry.name) == 0; }); if (index < folderdata.Size()) { FScanner sc; sc.OpenLumpNum(folderdata[index].lumpnum); while (sc.GetToken()) { sc.TokenMustBe(TK_Identifier); if (sc.Compare("Kerning")) { sc.MustGetValue(false); GlobalKerning = sc.Number; } else if (sc.Compare("Scale")) { sc.MustGetValue(true); Scale.Y = Scale.X = sc.Float; if (sc.CheckToken(',')) { sc.MustGetValue(true); Scale.Y = sc.Float; } } else if (sc.Compare("SpaceWidth")) { sc.MustGetValue(false); SpaceWidth = sc.Number; } else if (sc.Compare("FontHeight")) { sc.MustGetValue(false); FontHeight = sc.Number; } else if (sc.Compare("CellSize")) { sc.MustGetValue(false); FixedWidth = sc.Number; sc.MustGetToken(','); sc.MustGetValue(false); FontHeight = sc.Number; } else if (sc.Compare("Translationtype")) { sc.MustGetToken(TK_Identifier); if (sc.Compare("console")) { TranslationType = 1; } else if (sc.Compare("standard")) { TranslationType = 0; } else { sc.ScriptError("Unknown translation type %s", sc.String); } } } } } } if (FixedWidth > 0) { ReadSheetFont(folderdata, FixedWidth, FontHeight, Scale); Type = Folder; } else { if (nametemplate != nullptr) { if (!iwadonly) { for (i = 0; i < lcount; i++) { int position = lfirst + i; mysnprintf(buffer, countof(buffer), nametemplate, i + start); lump = TexMan.CheckForTexture(buffer, ETextureType::MiscPatch); if (doomtemplate && lump.isValid() && i + start == 121) { // HACKHACK: Don't load STCFN121 in doom(2), because // it's not really a lower-case 'y' but a '|'. // Because a lot of wads with their own font seem to foolishly // copy STCFN121 and make it a '|' themselves, wads must // provide STCFN120 (x) and STCFN122 (z) for STCFN121 to load as a 'y'. if (!TexMan.CheckForTexture("STCFN120", ETextureType::MiscPatch).isValid() || !TexMan.CheckForTexture("STCFN122", ETextureType::MiscPatch).isValid()) { // insert the incorrectly named '|' graphic in its correct position. position = 124; } } if (lump.isValid()) { Type = Multilump; if (position < minchar) minchar = position; if (position > maxchar) maxchar = position; charMap.Insert(position, TexMan.GetTexture(lump)); } } } else { FTexture *texs[256] = {}; if (lcount > 256 - start) lcount = 256 - start; for (i = 0; i < lcount; i++) { TArray<FTextureID> array; mysnprintf(buffer, countof(buffer), nametemplate, i + start); TexMan.ListTextures(buffer, array, true); for (auto entry : array) { FTexture *tex = TexMan.GetTexture(entry, false); if (tex && tex->SourceLump >= 0 && Wads.GetLumpFile(tex->SourceLump) <= Wads.GetIwadNum() && tex->UseType == ETextureType::MiscPatch) { texs[i] = tex; } } } if (doomtemplate) { // Handle the misplaced '|'. if (texs[121 - '!'] && !texs[120 - '!'] && !texs[122 - '!'] && !texs[124 - '!']) { texs[124 - '!'] = texs[121 - '!']; texs[121 - '!'] = nullptr; } } for (i = 0; i < lcount; i++) { if (texs[i]) { int position = lfirst + i; Type = Multilump; if (position < minchar) minchar = position; if (position > maxchar) maxchar = position; charMap.Insert(position, texs[i]); } } } } if (folderdata.Size() > 0) { // all valid lumps must be named with a hex number that represents its Unicode character index. for (auto &entry : folderdata) { char *endp; auto base = ExtractFileBase(entry.name); auto position = strtoll(base.GetChars(), &endp, 16); if ((*endp == 0 || (*endp == '.' && position >= '!' && position < 0xffff))) { auto lump = TexMan.CheckForTexture(entry.name, ETextureType::MiscPatch); if (lump.isValid()) { if ((int)position < minchar) minchar = (int)position; if ((int)position > maxchar) maxchar = (int)position; auto tex = TexMan.GetTexture(lump); tex->SetScale(Scale); charMap.Insert((int)position, tex); Type = Folder; } } } } FirstChar = minchar; LastChar = maxchar; auto count = maxchar - minchar + 1; Chars.Resize(count); int fontheight = 0; for (i = 0; i < count; i++) { auto lump = charMap.CheckKey(FirstChar + i); if (lump != nullptr) { FTexture *pic = *lump; if (pic != nullptr) { int height = pic->GetDisplayHeight(); int yoffs = pic->GetDisplayTopOffset(); if (yoffs > maxyoffs) { maxyoffs = yoffs; } height += abs(yoffs); if (height > fontheight) { fontheight = height; } } pic->SetUseType(ETextureType::FontChar); if (!noTranslate) { Chars[i].OriginalPic = pic; Chars[i].TranslatedPic = new FImageTexture(new FFontChar1(pic->GetImage()), ""); Chars[i].TranslatedPic->CopySize(pic); Chars[i].TranslatedPic->SetUseType(ETextureType::FontChar); TexMan.AddTexture(Chars[i].TranslatedPic); } else { Chars[i].TranslatedPic = pic; } Chars[i].XMove = Chars[i].TranslatedPic->GetDisplayWidth(); } else { Chars[i].TranslatedPic = nullptr; Chars[i].XMove = INT_MIN; } } if (SpaceWidth == 0) // An explicit override from the .inf file must always take precedence { if (spacewidth != -1) { SpaceWidth = spacewidth; } else if ('N' - FirstChar >= 0 && 'N' - FirstChar < count && Chars['N' - FirstChar].TranslatedPic != nullptr) { SpaceWidth = (Chars['N' - FirstChar].XMove + 1) / 2; } else { SpaceWidth = 4; } } if (FontHeight == 0) FontHeight = fontheight; FixXMoves(); } if (!noTranslate) LoadTranslations(); }
void FRemapTable::AddToTranslation(const char *range) { int start,end; bool desaturated = false; FScanner sc; sc.OpenMem("translation", range, int(strlen(range))); sc.SetCMode(true); try { sc.MustGetToken(TK_IntConst); start = sc.Number; sc.MustGetToken(':'); sc.MustGetToken(TK_IntConst); end = sc.Number; sc.MustGetToken('='); if (start < 0 || start > 255 || end < 0 || end > 255) { sc.ScriptError("Palette index out of range"); return; } sc.MustGetAnyToken(); if (sc.TokenType != '[' && sc.TokenType != '%') { int pal1,pal2; sc.TokenMustBe(TK_IntConst); pal1 = sc.Number; sc.MustGetToken(':'); sc.MustGetToken(TK_IntConst); pal2 = sc.Number; AddIndexRange(start, end, pal1, pal2); } else if (sc.TokenType == '[') { // translation using RGB values int r1,g1,b1,r2,g2,b2; sc.MustGetToken(TK_IntConst); r1 = sc.Number; sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); g1 = sc.Number; sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); b1 = sc.Number; sc.MustGetToken(']'); sc.MustGetToken(':'); sc.MustGetToken('['); sc.MustGetToken(TK_IntConst); r2 = sc.Number; sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); g2 = sc.Number; sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); b2 = sc.Number; sc.MustGetToken(']'); AddColorRange(start, end, r1, g1, b1, r2, g2, b2); } else if (sc.TokenType == '%') { // translation using RGB values double r1,g1,b1,r2,g2,b2; sc.MustGetToken('['); sc.MustGetAnyToken(); if (sc.TokenType != TK_IntConst) sc.TokenMustBe(TK_FloatConst); r1 = sc.Float; sc.MustGetToken(','); sc.MustGetAnyToken(); if (sc.TokenType != TK_IntConst) sc.TokenMustBe(TK_FloatConst); g1 = sc.Float; sc.MustGetToken(','); sc.MustGetAnyToken(); if (sc.TokenType != TK_IntConst) sc.TokenMustBe(TK_FloatConst); b1 = sc.Float; sc.MustGetToken(']'); sc.MustGetToken(':'); sc.MustGetToken('['); sc.MustGetAnyToken(); if (sc.TokenType != TK_IntConst) sc.TokenMustBe(TK_FloatConst); r2 = sc.Float; sc.MustGetToken(','); sc.MustGetAnyToken(); if (sc.TokenType != TK_IntConst) sc.TokenMustBe(TK_FloatConst); g2 = sc.Float; sc.MustGetToken(','); sc.MustGetAnyToken(); if (sc.TokenType != TK_IntConst) sc.TokenMustBe(TK_FloatConst); b2 = sc.Float; sc.MustGetToken(']'); AddDesaturation(start, end, r1, g1, b1, r2, g2, b2); } } catch (CRecoverableError &err) { Printf("Error in translation '%s':\n%s\n", range, err.GetMessage()); } }