Example #1
0
static int ParseStandardProperty(FScanner &scanner, UMapEntry *mape)
{
	// find the next line with content.
	// this line is no property.
	
	scanner.MustGetToken(TK_Identifier);
	FString pname = scanner.String;
	scanner.MustGetToken('=');

	if (!pname.CompareNoCase("levelname"))
	{
		scanner.MustGetToken(TK_StringConst);
		mape->LevelName = scanner.String;
	}
	else if (!pname.CompareNoCase("next"))
	{
		ParseLumpName(scanner, mape->nextmap);
	}
	else if (!pname.CompareNoCase("nextsecret"))
	{
		ParseLumpName(scanner, mape->nextsecret);
	}
	else if (!pname.CompareNoCase("levelpic"))
	{
		ParseLumpName(scanner, mape->levelpic);
	}
	else if (!pname.CompareNoCase("skytexture"))
	{
		ParseLumpName(scanner, mape->skytexture);
	}
	else if (!pname.CompareNoCase("music"))
	{
		ParseLumpName(scanner, mape->music);
	}
	else if (!pname.CompareNoCase("endpic"))
	{
		ParseLumpName(scanner, mape->endpic);
	}
	else if (!pname.CompareNoCase("endcast"))
	{
		scanner.MustGetBoolToken();
		if (scanner.Number) strcpy(mape->endpic, "$CAST");
		else strcpy(mape->endpic, "-");
	}
	else if (!pname.CompareNoCase("endbunny"))
	{
		scanner.MustGetBoolToken();
		if (scanner.Number) strcpy(mape->endpic, "$BUNNY");
		else strcpy(mape->endpic, "-");
	}
	else if (!pname.CompareNoCase("endgame"))
	{
		scanner.MustGetBoolToken();
		if (scanner.Number) strcpy(mape->endpic, "!");
		else strcpy(mape->endpic, "-");
	}
	else if (!pname.CompareNoCase("exitpic"))
	{
		ParseLumpName(scanner, mape->exitpic);
	}
	else if (!pname.CompareNoCase("enterpic"))
	{
		ParseLumpName(scanner, mape->enterpic);
	}
	else if (!pname.CompareNoCase("nointermission"))
	{
		scanner.MustGetBoolToken();
		mape->nointermission = scanner.Number;
	}
	else if (!pname.CompareNoCase("partime"))
	{
		scanner.MustGetValue(false);
		mape->partime = TICRATE * scanner.Number;
	}
	else if (!pname.CompareNoCase("intertext"))
	{
		mape->InterText = ParseMultiString(scanner, 1);
		if (mape->InterText.IsEmpty()) return 0;
	}
	else if (!pname.CompareNoCase("intertextsecret"))
	{
		mape->InterTextSecret = ParseMultiString(scanner, 1);
		if (mape->InterTextSecret.IsEmpty()) return 0;
	}
	else if (!pname.CompareNoCase("interbackdrop"))
	{
		ParseLumpName(scanner, mape->interbackdrop);
	}
	else if (!pname.CompareNoCase("intermusic"))
	{
		ParseLumpName(scanner, mape->intermusic);
	}
	else if (!pname.CompareNoCase("episode"))
	{
		FString Episode = ParseMultiString(scanner, 1);
		if (Episode.IsEmpty()) return 0;
		if (Episode.Compare("-") == 0)
		{
			// clear the given episode
			for (unsigned i = 0; i < AllEpisodes.Size(); i++)
			{
				if (AllEpisodes[i].mEpisodeMap.CompareNoCase(mape->MapName) == 0)
				{
					AllEpisodes.Delete(i);
					break;
				}
			}
		}
		else
		{
			auto split = Episode.Split("\n");
			// add the given episode
			FEpisode epi;

			epi.mEpisodeName = strbin1(split[1]);
			epi.mEpisodeMap = mape->MapName;
			epi.mPicName = split[0];
			epi.mShortcut = split[2][0];

			unsigned i;
			for (i = 0; i < AllEpisodes.Size(); i++)
			{
				if (AllEpisodes[i].mEpisodeMap.CompareNoCase(mape->MapName) == 0)
				{
					AllEpisodes[i] = std::move(epi);
					break;
				}
			}
			if (i == AllEpisodes.Size())
			{
				AllEpisodes.Push(epi);
			}
		}
	}
	else if (!pname.CompareNoCase("bossaction"))
	{
		scanner.MustGetToken(TK_Identifier);
		int classnum, special, tag;
		if (!stricmp(scanner.String, "clear"))
		{
			// mark level free of boss actions
			classnum = special = tag = -1;
			mape->BossActions.Clear();
		}
		else
		{
			FName type = scanner.String;
			scanner.MustGetToken(',');
			scanner.MustGetValue(false);
			int special = scanner.Number;
			scanner.MustGetToken(',');
			scanner.MustGetValue(false);
			int tag = scanner.Number;
			// allow no 0-tag specials here, unless a level exit.
			if (tag != 0 || special == 11 || special == 51 || special == 52 || special == 124)
			{
				// This cannot be evaluated here because this needs to be done in the context of the level being used.
				FSpecialAction & bossact = mape->BossActions[mape->BossActions.Reserve(1)];
				bossact = { type, special | 0x40000000, {tag} };
			};
		}
	}
	else
	{
		// Skip over all unknown properties.
		do
		{
			if (!scanner.CheckValue(true))
			{
				scanner.MustGetAnyToken();
				if (scanner.TokenType != TK_Identifier && scanner.TokenType != TK_StringConst && scanner.TokenType != TK_True && scanner.TokenType != TK_False)
				{
					scanner.ScriptError("Identifier or value expected");
				}
			}
			
		} while (scanner.CheckToken(','));
	}
	return 1;
}
Example #2
0
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();
}