From 8f1d3db15b147087685f13389798b0908bbf90f0 Mon Sep 17 00:00:00 2001 From: Glenn Maynard Date: Sun, 5 Jan 2003 07:55:31 +0000 Subject: [PATCH] handle default font, import fonts, default glyph --- stepmania/src/Font.cpp | 31 ++++++++- stepmania/src/Font.h | 7 ++ stepmania/src/FontManager.cpp | 46 +++++++++++++ stepmania/src/FontManager.h | 3 +- stepmania/src/ThemeManager.cpp | 120 ++++++++++++++++++++++++++++++++- stepmania/src/ThemeManager.h | 1 + 6 files changed, 203 insertions(+), 5 deletions(-) diff --git a/stepmania/src/Font.cpp b/stepmania/src/Font.cpp index d7eef12194..f1877e9aff 100644 --- a/stepmania/src/Font.cpp +++ b/stepmania/src/Font.cpp @@ -18,6 +18,9 @@ #include "RageUtil.h" #include "RageLog.h" #include "RageException.h" +#include "FontManager.h" + +const wchar_t Font::DEFAULT_GLYPH = 0xFFFF; FontPage::FontPage() { @@ -188,6 +191,10 @@ Font::~Font() { for(unsigned i = 0; i < pages.size(); ++i) delete pages[i]; + + /* Free any fonts that we merged into us. */ + for(unsigned i = 0; i < merged_fonts.size(); ++i) + FONT->UnloadFont(merged_fonts[i]); } void Font::AddPage(FontPage *fp) @@ -201,12 +208,28 @@ void Font::AddPage(FontPage *fp) } } +void Font::MergeFont(Font *f) +{ + for(map::iterator it = f->m_iCharToGlyph.begin(); + it != f->m_iCharToGlyph.end(); ++it) + { + m_iCharToGlyph[it->first] = it->second; + } + + /* We now have ownership of f. Mark it to be freed. */ + merged_fonts.push_back(f); +} + const glyph &Font::GetGlyph( wchar_t c ) const { map::const_iterator it = m_iCharToGlyph.find(c); if(it == m_iCharToGlyph.end()) - RageException::Throw( "The font '%s' does not implement the character '%c'", path.GetString(), c ); + { + if(c == Font::DEFAULT_GLYPH) + RageException::Throw( "The default glyph is missing from the font '%s'", path.GetString() ); + return GetGlyph(DEFAULT_GLYPH); + } return *it->second; } @@ -216,7 +239,11 @@ RageTexture *Font::GetGlyphTexture( wchar_t c ) map::iterator it = m_iCharToGlyph.find(c); if(it == m_iCharToGlyph.end()) - RageException::Throw( "The font '%s' does not implement the character '%c'", path.GetString(), c ); + { + if(c == Font::DEFAULT_GLYPH) + RageException::Throw( "The default glyph is missing from the font '%s'", path.GetString() ); + return GetGlyphTexture(DEFAULT_GLYPH); + } return it->second->Texture; } diff --git a/stepmania/src/Font.h b/stepmania/src/Font.h index e0536ac397..efe6df6ad3 100644 --- a/stepmania/src/Font.h +++ b/stepmania/src/Font.h @@ -92,11 +92,18 @@ public: /* Add a FontPage to this font. */ void AddPage(FontPage *fp); + /* Steal all of a font's pages. */ + void MergeFont(Font *f); + /* Load font-wide settings. */ void CapsOnly(); + static const wchar_t DEFAULT_GLYPH; + private: + /* List of pages and fonts that we're responsible for freeing. */ vector pages; + vector merged_fonts; }; #endif diff --git a/stepmania/src/FontManager.cpp b/stepmania/src/FontManager.cpp index 188d435e00..e67938b857 100644 --- a/stepmania/src/FontManager.cpp +++ b/stepmania/src/FontManager.cpp @@ -18,6 +18,7 @@ #include "RageLog.h" #include "RageException.h" #include "RageTimer.h" +#include "ThemeManager.h" static map CharAliases; @@ -206,9 +207,24 @@ void FontManager::GetFontPaths(const CString &sFontOrTextureFilePath, ASSERT(!TexturePaths.empty()); /* ThemeManager should have checked this */ } +static CStringArray LoadStack; + /* Load the named font and return it. */ Font *FontManager::LoadFontInt(const CString &sFontOrTextureFilePath, CString sChars) { + /* Check for recursion (recursive imports). */ + { + for(unsigned i = 0; i < LoadStack.size(); ++i) + { + if(LoadStack[i] == sFontOrTextureFilePath) + { + CString str = join("\n", LoadStack); + str += "\n" + sFontOrTextureFilePath; + RageException::Throw("Font import recursion detected\n%s", str.GetString()); + } + } + LoadStack.push_back(sFontOrTextureFilePath); + } /* The font is not already loaded. Figure out what we have. */ // LOG->Trace( "FontManager::LoadFont(%s).", sFontFilePath.GetString() ); @@ -226,9 +242,36 @@ Font *FontManager::LoadFontInt(const CString &sFontOrTextureFilePath, CString sC { ini.SetPath( IniPath ); ini.ReadFile(); + ini.RenameKey("Char Widths", "main"); ini.GetValueB( "main", "CapitalsOnly", CapitalsOnly ); } + { + /* Check to see if we need to import any other fonts. Do this + * before loading this font, so any characters in this font + * override those imported. */ + CString imports; + ini.GetValue( "main", "import", imports ); + CStringArray ImportList; + split(imports, ",", ImportList, true); + + /* Load the default font first, unless we're currently loading it. */ + static bool LoadingDefaultFont = false; + if(!LoadingDefaultFont) + { + LoadingDefaultFont = true; + CString path = THEME->GetPathTo("Fonts", "default font"); + pFont->MergeFont(FONT->LoadFont(path)); + LoadingDefaultFont = false; + } + + for(unsigned i = 0; i < ImportList.size(); ++i) + { + CString path = THEME->GetPathTo("Fonts", ImportList[i]); + pFont->MergeFont(FONT->LoadFont(path)); + } + } + /* Load each font page. */ int expect = -1; for(unsigned i = 0; i < TexturePaths.size(); ++i) @@ -274,6 +317,8 @@ Font *FontManager::LoadFontInt(const CString &sFontOrTextureFilePath, CString sC if(pFont->m_iCharToGlyph.empty()) LOG->Warn("Font %s has no characters", sFontOrTextureFilePath.GetString()); + LoadStack.pop_back(); + return pFont; } @@ -310,6 +355,7 @@ Font* FontManager::LoadFont( const CString &sFontOrTextureFilePath, CString sCha { /* XXX; these should be in a data file somewhere (theme metrics?) * (The comments here are UTF-8; they won't show up in VC6.) */ + CharAliases["default"] = Font::DEFAULT_GLYPH; /* ? */ CharAliases["kakumei1"] = 0x547D; /* 革 */ CharAliases["kakumei2"] = 0x9769; /* 命 */ CharAliases["matsuri"] = 0x796D; /* 祭 */ diff --git a/stepmania/src/FontManager.h b/stepmania/src/FontManager.h index 1d2b81bebd..ca1fb6e9be 100644 --- a/stepmania/src/FontManager.h +++ b/stepmania/src/FontManager.h @@ -27,11 +27,12 @@ public: Font* LoadFont( const CString &sFontOrTextureFilePath, CString sChars = "" ); void UnloadFont( Font *fp ); + static bool MatchesFont(CString FontName, CString FileName); + protected: // map from file name to a texture holder map m_mapPathToFont; static Font *LoadFontInt(const CString &sFontOrTextureFilePath, CString sChars); - static bool MatchesFont(CString FontName, CString FileName); static CString GetPageNameFromFileName(const CString &fn); static void LoadFontPageSettings(FontPageSettings &cfg, IniFile &ini, const CString &PageName, int NumFrames); static void GetFontPaths(const CString &sFontOrTextureFilePath, diff --git a/stepmania/src/ThemeManager.cpp b/stepmania/src/ThemeManager.cpp index 495c4b3dcb..17fe0eb4aa 100644 --- a/stepmania/src/ThemeManager.cpp +++ b/stepmania/src/ThemeManager.cpp @@ -19,6 +19,7 @@ #include "GameDef.h" #include "IniFile.h" #include "RageTimer.h" +#include "FontManager.h" using namespace std; @@ -114,8 +115,123 @@ CString ThemeManager::GetThemeDirFromName( CString sThemeName ) return THEMES_DIR + sThemeName + "\\"; } +CString ThemeManager::GetPathToFont( CString sAssetCategory, CString sFileName ) +{ +try_element_again: + sAssetCategory.MakeLower(); + sFileName.MakeLower(); + + const CString sCurrentThemeDir = GetThemeDirFromName( m_sCurThemeName ); + const CString sDefaultThemeDir = GetThemeDirFromName( BASE_THEME_NAME ); + + CStringArray asPossibleElementFilePaths; + + /////////////////////////////////////// + // Search both the current theme and the default theme dirs for this element + /////////////////////////////////////// + static const char *font_masks[] = { "*.png", ".redir", NULL }; + static const char *blank_mask[] = { "", NULL }; + const char **asset_masks = font_masks; + + /* If the theme asset name has an extension, don't add + * a mask. This should only happen with redirs. */ + if(sFileName.find_last_of('.') != sFileName.npos) + asset_masks = blank_mask; + + int i; + CString path; + + for(i = 0; asset_masks[i]; ++i) + { + path = sCurrentThemeDir; + GetDirListing( path + "Fonts\\"+sFileName + asset_masks[i], + asPossibleElementFilePaths, false, true ); + } + + /* Weed out false matches. (For example, this gets rid of "normal2" when + * we're really looking for "normal".) */ + for(i = 0; i < int(asPossibleElementFilePaths.size()); ) { + if(!FontManager::MatchesFont(path + "Fonts\\"+ sFileName, asPossibleElementFilePaths[i])) + asPossibleElementFilePaths.erase(asPossibleElementFilePaths.begin()+i); + else i++; + } + + if(asPossibleElementFilePaths.empty()) + for(i = 0; asset_masks[i]; ++i) + { + path = sDefaultThemeDir; + + GetDirListing( path + "Fonts\\"+ sFileName + asset_masks[i], + asPossibleElementFilePaths, false, true ); + } + + for(i = 0; i < int(asPossibleElementFilePaths.size()); ) { + if(!FontManager::MatchesFont(path + "Fonts\\"+ sFileName, asPossibleElementFilePaths[i])) + asPossibleElementFilePaths.erase(asPossibleElementFilePaths.begin()+i); + else i++; + } + + /* If it's empty, we found nothing. */ + if( asPossibleElementFilePaths.empty() ) + { +#ifdef _DEBUG + CString sMessage = ssprintf("The theme element %s/%s is missing.",sAssetCategory.GetString(),sFileName.GetString()); + switch( MessageBox(NULL, sMessage, "ThemeManager", MB_ABORTRETRYIGNORE ) ) + { + case IDRETRY: + goto try_element_again; + break; + case IDABORT: +#endif + RageException::Throw( "Theme element '%s/%s' could not be found in '%s' or '%s'.", + sAssetCategory.GetString(), + sFileName.GetString(), + GetThemeDirFromName(m_sCurThemeName).GetString(), + GetThemeDirFromName(BASE_THEME_NAME).GetString() ); +#ifdef _DEBUG + case IDIGNORE: + LOG->Warn( + "Theme element '%s/%s' could not be found in '%s' or '%s'.", + sAssetCategory.GetString(), + sFileName.GetString(), + GetThemeDirFromName(m_sCurThemeName), + GetThemeDirFromName(BASE_THEME_NAME) ); + return GetPathTo( sAssetCategory, "_missing" ); + break; + } +#endif + } + + if( asPossibleElementFilePaths[0].GetLength() > 5 && asPossibleElementFilePaths[0].Right(5) == "redir" ) // this is a redirect file + { + CString sNewFilePath = DerefRedir(asPossibleElementFilePaths[0]); + + /* backwards-compatibility hack */ + if( sAssetCategory == "fonts" ) + sNewFilePath.Replace(" 16x16.png", ""); + + /* Search again. For example, themes/default/Fonts/foo might redir + * to "Hello"; but "Hello" might be overridden in themes/hot pink/Fonts/Hello. */ + + /* Strip off the path. */ + unsigned pos = sNewFilePath.find_last_of("/\\"); + if(pos != sNewFilePath.npos) sNewFilePath = sNewFilePath.substr(pos+1); + sFileName = sNewFilePath; + + /* XXX check for loops */ + goto try_element_again; + } + + /* If we're searching for a font, the return value should be the file + * prefix we searched for; don't resolve it to the complete filename. */ + return path + sAssetCategory+"\\"+sFileName; +} + CString ThemeManager::GetPathTo( CString sAssetCategory, CString sFileName ) { + if(sAssetCategory == "Fonts") + return GetPathToFont(sAssetCategory, sFileName); + try_element_again: sAssetCategory.MakeLower(); sFileName.MakeLower(); @@ -133,14 +249,14 @@ try_element_again: "*.avi", "*.mpg", "*.mpeg", NULL }; static const char *sound_masks[] = { ".set", ".mp3", ".ogg", ".wav", ".redir", NULL }; - static const char *font_masks[] = { " 16x16.png", ".redir", NULL }; + static const char *font_masks[] = { "*.png", ".redir", NULL }; static const char *numbers_masks[] = { " 5x3.png", ".redir", NULL }; static const char *bganimations_masks[] = { ".", ".redir", NULL }; static const char *blank_mask[] = { "", NULL }; const char **asset_masks = NULL; if( sAssetCategory == "graphics" ) asset_masks = graphic_masks; else if( sAssetCategory == "sounds" ) asset_masks = sound_masks; - else if( sAssetCategory == "fonts" ) asset_masks = font_masks; +// else if( sAssetCategory == "fonts" ) asset_masks = font_masks; else if( sAssetCategory == "numbers" ) asset_masks = numbers_masks; else if( sAssetCategory == "bganimations" ) asset_masks = bganimations_masks; else ASSERT(0); // Unknown theme asset category diff --git a/stepmania/src/ThemeManager.h b/stepmania/src/ThemeManager.h index 84cb2f1b64..2538b409da 100644 --- a/stepmania/src/ThemeManager.h +++ b/stepmania/src/ThemeManager.h @@ -42,6 +42,7 @@ protected: static CString GetThemeDirFromName( CString sThemeName ); CString GetElementDir( CString sThemeName ); static CString GetMetricsPathFromName( CString sThemeName ); + CString GetPathToFont( CString sAssetCategory, CString sFileName ); CString m_sCurThemeName;