static CString GetDirOfExecutable( CString argv0 ) { #ifdef _XBOX return "D:\\"; #else /* argv[0] can be wrong in most OS's; try to avoid using it. */ CString sPath; #if defined(_WIN32) char buf[MAX_PATH]; GetModuleFileName( NULL, buf, sizeof(buf) ); sPath = buf; #else sPath = argv0; #endif sPath.Replace( "\\", "/" ); bool IsAbsolutePath = false; if( sPath.size() == 0 || sPath[0] == '/' ) IsAbsolutePath = true; #if defined(_WIN32) if( sPath.size() > 2 && sPath[1] == ':' && sPath[2] == '/' ) IsAbsolutePath = true; #endif // strip off executable name size_t n = sPath.find_last_of("/"); if( n != sPath.npos ) sPath.erase(n); else sPath.erase(); if( !IsAbsolutePath ) { sPath = GetCwd() + "/" + sPath; sPath.Replace( "\\", "/" ); } return sPath; #endif }
CString Font::GetFontName( CString sFileName ) { CString sOrig = sFileName; CString sDir, sFName, sExt; splitpath( sFileName, sDir, sFName, sExt ); sFileName = sFName; /* If it ends in an extension, remove it. */ static Regex drop_ext( "\\....$" ); if( drop_ext.Compare(sFileName) ) sFileName.erase( sFileName.size()-4 ); /* If it ends in a resolution spec, remove it. */ CStringArray asMatch; static Regex ResSpec( "( \\(res [0-9]+x[0-9]+\\))$" ); if( ResSpec.Compare(sFileName, asMatch) ) sFileName.erase(sFileName.size()-asMatch[0].size()); /* If it ends in a dimension spec, remove it. */ static Regex DimSpec( "( [0-9]+x[0-9]+)$" ); if( DimSpec.Compare(sFileName, asMatch) ) sFileName.erase( sFileName.size()-asMatch[0].size() ); /* If it ends in texture hints, remove them. */ static Regex Hints( "( \\([^\\)]+\\))$" ); if( Hints.Compare(sFileName, asMatch) ) sFileName.erase( sFileName.size()-asMatch[0].size() ); /* If it ends in a page name, remove it. */ static Regex PageName("( \\[.+\\])$"); if( PageName.Compare( sFileName, asMatch ) ) sFileName.erase( sFileName.size()-asMatch[0].size() ); TrimRight( sFileName ); if( sFileName.empty() ) RageException::Throw( "Can't parse font filename \"%s\"", sOrig.c_str() ); sFileName.MakeLower(); return sFileName; }
CString CMSWindowsClipboardUTF16Converter::doToIClipboard(const CString& data) const { // convert and strip nul terminator CString dst = CUnicode::UTF16ToUTF8(data); CString::size_type n = dst.find('\0'); if (n != CString::npos) { dst.erase(n); } return dst; }
CString SongUtil::MakeSortString( CString s ) { s.MakeUpper(); // Make sure that non-alphanumeric strings are placed at the very end. if( s.size() > 0 ) { if( s[0] == '.' ) // ".59" s.erase(s.begin()); if( (s[0] < 'A' || s[0] > 'Z') && (s[0] < '0' || s[0] > '9') ) s = char(126) + s; } return s; }
void CDCCSock::ReadData(const char* data, size_t len) { if (!m_pFile) { DEBUG("File not open! closing get."); if (m_bSend) { m_pModule->PutModule(t_f("Sending [{1}] to [{2}]: File not open!")( m_sFileName, m_sRemoteNick)); } else { m_pModule->PutModule( t_f("Receiving [{1}] from [{2}]: File not open!")( m_sFileName, m_sRemoteNick)); } Close(); return; } // DCC specs says the receiving end sends the number of bytes it // received so far as a 4 byte integer in network byte order, so we need // uint32_t to do the job portably. This also means that the maximum // file that we can transfer is 4 GiB big (see OpenFile()). if (m_bSend) { m_sSendBuf.append(data, len); while (m_sSendBuf.size() >= 4) { uint32_t iRemoteSoFar; memcpy(&iRemoteSoFar, m_sSendBuf.data(), sizeof(iRemoteSoFar)); iRemoteSoFar = ntohl(iRemoteSoFar); if ((iRemoteSoFar + 65536) >= m_uBytesSoFar) { SendPacket(); } m_sSendBuf.erase(0, 4); } } else { m_pFile->Write(data, len); m_uBytesSoFar += len; uint32_t uSoFar = htonl((uint32_t)m_uBytesSoFar); Write((char*)&uSoFar, sizeof(uSoFar)); if (m_uBytesSoFar >= m_uFileSize) { Close(); } } }
SongCreditDisplay::SongCreditDisplay() { if( GAMESTATE->IsCourseMode() ) return; this->LoadFromFont( THEME->GetPathF("SongCreditDisplay","text") ); Song* pSong = GAMESTATE->m_pCurSong; ASSERT( pSong ); CString s = pSong->GetFullDisplayTitle() + "\n" + pSong->GetDisplayArtist() + "\n"; if( !pSong->m_sCredit.empty() ) s += pSong->m_sCredit + "\n"; // use a vector and not a set so that ordering is maintained vector<Steps*> vpStepsToShow; FOREACH_PlayerNumber( p ) { if( !GAMESTATE->IsHumanPlayer(p) ) continue; // skip Steps* pSteps = GAMESTATE->m_pCurSteps[p]; bool bAlreadyAdded = find( vpStepsToShow.begin(), vpStepsToShow.end(), pSteps ) != vpStepsToShow.end(); if( !bAlreadyAdded ) vpStepsToShow.push_back( pSteps ); } for( unsigned i=0; i<vpStepsToShow.size(); i++ ) { Steps* pSteps = vpStepsToShow[i]; CString sDifficulty = DifficultyToThemedString( pSteps->GetDifficulty() ); // HACK: reset capitalization sDifficulty.MakeLower(); sDifficulty = Capitalize( sDifficulty ); s += sDifficulty + " steps: " + pSteps->GetDescription() + "\n"; } // erase the last newline s.erase( s.end()-1 ); this->SetText( s ); }
CString CDataStream::GetLine() { // keep looping while not hitting delimiter int nReadCount; char szTmpBuf[MAX_PATH]; CString szRet; while ( (nReadCount = Read(szTmpBuf, MAX_PATH - 1)) != 0) { // Terminate string szTmpBuf[nReadCount] = '\0'; char *p = strchr(szTmpBuf, '\n'); if (p != 0) { // reposition backwards Skip((long)(p + 1 - szTmpBuf - nReadCount)); *p = '\0'; } szRet = szTmpBuf; if (p != 0) { // trim off trailing cr if this was a cr/lf entry if (szRet.length() && szRet[szRet.length() - 1] == '\r') { szRet.erase(szRet.length() - 1, 1); } // found terminator, break out break; } } CStringUtil::Trim(szRet); return szRet; }
CWebSock::EPageReqResult CWebSock::OnPageRequestInternal(const CString& sURI, CString& sPageRet) { // Check that their session really belongs to their IP address. IP-based // authentication is bad, but here it's just an extra layer that makes // stealing cookies harder to pull off. // // When their IP is wrong, we give them an invalid cookie. This makes // sure that they will get a new cookie on their next request. if (CZNC::Get().GetProtectWebSessions() && GetSession()->GetIP() != GetRemoteIP()) { DEBUG("Expected IP: " << GetSession()->GetIP()); DEBUG("Remote IP: " << GetRemoteIP()); SendCookie("SessionId", "WRONG_IP_FOR_SESSION"); PrintErrorPage(403, "Access denied", "This session does not belong to your IP."); return PAGE_DONE; } // Check that they really POSTed from one our forms by checking if they // know the "secret" CSRF check value. Don't do this for login since // CSRF against the login form makes no sense and the login form does a // cookies-enabled check which would break otherwise. if (IsPost() && GetParam("_CSRF_Check") != GetCSRFCheck() && sURI != "/login") { DEBUG("Expected _CSRF_Check: " << GetCSRFCheck()); DEBUG("Actual _CSRF_Check: " << GetParam("_CSRF_Check")); PrintErrorPage(403, "Access denied", "POST requests need to send " "a secret token to prevent cross-site request forgery attacks."); return PAGE_DONE; } SendCookie("SessionId", GetSession()->GetId()); if (GetSession()->IsLoggedIn()) { m_sUser = GetSession()->GetUser()->GetUserName(); m_bLoggedIn = true; } // Handle the static pages that don't require a login if (sURI == "/") { if(!m_bLoggedIn && GetParam("cookie_check", false).ToBool() && GetRequestCookie("SessionId").empty()) { GetSession()->AddError("Your browser does not have cookies enabled for this site!"); } return PrintTemplate("index", sPageRet); } else if (sURI == "/favicon.ico") { return PrintStaticFile("/pub/favicon.ico", sPageRet); } else if (sURI == "/robots.txt") { return PrintStaticFile("/pub/robots.txt", sPageRet); } else if (sURI == "/logout") { GetSession()->SetUser(NULL); SetLoggedIn(false); Redirect("/"); // We already sent a reply return PAGE_DONE; } else if (sURI == "/login") { if (GetParam("submitted").ToBool()) { m_sUser = GetParam("user"); m_sPass = GetParam("pass"); m_bLoggedIn = OnLogin(m_sUser, m_sPass); // AcceptedLogin()/RefusedLogin() will call Redirect() return PAGE_DEFERRED; } Redirect("/"); // the login form is here return PAGE_DONE; } else if (sURI.Left(5) == "/pub/") { return PrintStaticFile(sURI, sPageRet); } else if (sURI.Left(11) == "/skinfiles/") { CString sSkinName = sURI.substr(11); CString::size_type uPathStart = sSkinName.find("/"); if (uPathStart != CString::npos) { CString sFilePath = sSkinName.substr(uPathStart + 1); sSkinName.erase(uPathStart); m_Template.ClearPaths(); m_Template.AppendPath(GetSkinPath(sSkinName) + "pub"); if (PrintFile(m_Template.ExpandFile(sFilePath))) { return PAGE_DONE; } else { return PAGE_NOTFOUND; } } return PAGE_NOTFOUND; } else if (sURI.Left(6) == "/mods/" || sURI.Left(10) == "/modfiles/") { // Make sure modules are treated as directories if (sURI.Right(1) != "/" && sURI.find(".") == CString::npos && sURI.TrimLeft_n("/mods/").TrimLeft_n("/").find("/") == CString::npos) { Redirect(sURI + "/"); return PAGE_DONE; } // The URI looks like: // /mods/[type]/([network]/)?[module][/page][?arg1=val1&arg2=val2...] m_sPath = GetPath().TrimLeft_n("/"); m_sPath.TrimPrefix("mods/"); m_sPath.TrimPrefix("modfiles/"); CString sType = m_sPath.Token(0, false, "/"); m_sPath = m_sPath.Token(1, true, "/"); CModInfo::EModuleType eModType; if (sType.Equals("global")) { eModType = CModInfo::GlobalModule; } else if (sType.Equals("user")) { eModType = CModInfo::UserModule; } else if (sType.Equals("network")) { eModType = CModInfo::NetworkModule; } else { PrintErrorPage(403, "Forbidden", "Unknown module type [" + sType + "]"); return PAGE_DONE; } if ((eModType != CModInfo::GlobalModule) && !ForceLogin()) { // Make sure we have a valid user return PAGE_DONE; } CIRCNetwork *pNetwork = NULL; if (eModType == CModInfo::NetworkModule) { CString sNetwork = m_sPath.Token(0, false, "/"); m_sPath = m_sPath.Token(1, true, "/"); pNetwork = GetSession()->GetUser()->FindNetwork(sNetwork); if (!pNetwork) { PrintErrorPage(404, "Not Found", "Network [" + sNetwork + "] not found."); return PAGE_DONE; } } m_sModName = m_sPath.Token(0, false, "/"); m_sPage = m_sPath.Token(1, true, "/"); if (m_sPage.empty()) { m_sPage = "index"; } DEBUG("Path [" + m_sPath + "], Module [" + m_sModName + "], Page [" + m_sPage + "]"); CModule *pModule = NULL; switch (eModType) { case CModInfo::GlobalModule: pModule = CZNC::Get().GetModules().FindModule(m_sModName); break; case CModInfo::UserModule: pModule = GetSession()->GetUser()->GetModules().FindModule(m_sModName); break; case CModInfo::NetworkModule: pModule = pNetwork->GetModules().FindModule(m_sModName); break; } if (!pModule) return PAGE_NOTFOUND; m_Template["ModPath"] = pModule->GetWebPath(); m_Template["ModFilesPath"] = pModule->GetWebFilesPath(); if (pModule->WebRequiresLogin() && !ForceLogin()) { return PAGE_PRINT; } else if (pModule->WebRequiresAdmin() && !GetSession()->IsAdmin()) { PrintErrorPage(403, "Forbidden", "You need to be an admin to access this module"); return PAGE_DONE; } else if (pModule->GetType() != CModInfo::GlobalModule && pModule->GetUser() != GetSession()->GetUser()) { PrintErrorPage(403, "Forbidden", "You must login as " + pModule->GetUser()->GetUserName() + " in order to view this page"); return PAGE_DONE; } else if (pModule->OnWebPreRequest(*this, m_sPage)) { return PAGE_DEFERRED; } VWebSubPages& vSubPages = pModule->GetSubPages(); for (unsigned int a = 0; a < vSubPages.size(); a++) { TWebSubPage& SubPage = vSubPages[a]; bool bActive = (m_sModName == pModule->GetModName() && m_sPage == SubPage->GetName()); if (bActive && SubPage->RequiresAdmin() && !GetSession()->IsAdmin()) { PrintErrorPage(403, "Forbidden", "You need to be an admin to access this page"); return PAGE_DONE; } } if (pModule && pModule->GetType() != CModInfo::GlobalModule && (!IsLoggedIn() || pModule->GetUser() != GetSession()->GetUser())) { AddModLoop("UserModLoop", *pModule); } if (sURI.Left(10) == "/modfiles/") { m_Template.AppendPath(GetSkinPath(GetSkinName()) + "/mods/" + m_sModName + "/files/"); m_Template.AppendPath(pModule->GetModDataDir() + "/files/"); if (PrintFile(m_Template.ExpandFile(m_sPage.TrimLeft_n("/")))) { return PAGE_PRINT; } else { return PAGE_NOTFOUND; } } else { SetPaths(pModule, true); /* if a module returns false from OnWebRequest, it does not want the template to be printed, usually because it did a redirect. */ if (pModule->OnWebRequest(*this, m_sPage, m_Template)) { // If they already sent a reply, let's assume // they did what they wanted to do. if (SentHeader()) { return PAGE_DONE; } return PrintTemplate(m_sPage, sPageRet, pModule); } if (!SentHeader()) { PrintErrorPage(404, "Not Implemented", "The requested module does not acknowledge web requests"); } return PAGE_DONE; } } else { CString sPage(sURI.Trim_n("/")); if (sPage.length() < 32) { for (unsigned int a = 0; a < sPage.length(); a++) { unsigned char c = sPage[a]; if ((c < '0' || c > '9') && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && c != '_') { return PAGE_NOTFOUND; } } return PrintTemplate(sPage, sPageRet); } } return PAGE_NOTFOUND; }
CWebSock::EPageReqResult CWebSock::OnPageRequestInternal(const CString& sURI, CString& sPageRet) { if (CZNC::Get().GetProtectWebSessions() && GetSession()->GetIP() != GetRemoteIP()) { DEBUG("Expected IP: " << GetSession()->GetIP()); DEBUG("Remote IP: " << GetRemoteIP()); PrintErrorPage(403, "Access denied", "This session does not belong to your IP."); return PAGE_DONE; } // Check that they really POSTed from one our forms by checking if they // know the "secret" CSRF check value. Don't do this for login since // CSRF against the login form makes no sense and the login form does a // cookies-enabled check which would break otherwise. if (IsPost() && GetParam("_CSRF_Check") != GetCSRFCheck() && sURI != "/login") { DEBUG("Expected _CSRF_Check: " << GetCSRFCheck()); DEBUG("Actual _CSRF_Check: " << GetParam("_CSRF_Check")); PrintErrorPage(403, "Access denied", "POST requests need to send " "a secret token to prevent cross-site request forgery attacks."); return PAGE_DONE; } SendCookie("SessionId", GetSession()->GetId()); if (GetSession()->IsLoggedIn()) { m_sUser = GetSession()->GetUser()->GetUserName(); m_bLoggedIn = true; } // Handle the static pages that don't require a login if (sURI == "/") { if(!m_bLoggedIn && GetParam("cookie_check", false).ToBool() && GetRequestCookie("SessionId").empty()) { GetSession()->AddError("Your browser does not have cookies enabled for this site!"); } return PrintTemplate("index", sPageRet); } else if (sURI == "/favicon.ico") { return PrintStaticFile("/pub/favicon.ico", sPageRet); } else if (sURI == "/robots.txt") { return PrintStaticFile("/pub/robots.txt", sPageRet); } else if (sURI == "/logout") { GetSession()->SetUser(NULL); SetLoggedIn(false); Redirect("/"); // We already sent a reply return PAGE_DONE; } else if (sURI == "/login") { if (GetParam("submitted").ToBool()) { m_sUser = GetParam("user"); m_sPass = GetParam("pass"); m_bLoggedIn = OnLogin(m_sUser, m_sPass); // AcceptedLogin()/RefusedLogin() will call Redirect() return PAGE_DEFERRED; } Redirect("/"); // the login form is here return PAGE_DONE; } else if (sURI.Left(5) == "/pub/") { return PrintStaticFile(sURI, sPageRet); } else if (sURI.Left(11) == "/skinfiles/") { CString sSkinName = sURI.substr(11); CString::size_type uPathStart = sSkinName.find("/"); if (uPathStart != CString::npos) { CString sFilePath = sSkinName.substr(uPathStart + 1); sSkinName.erase(uPathStart); m_Template.ClearPaths(); m_Template.AppendPath(GetSkinPath(sSkinName) + "pub"); if (PrintFile(m_Template.ExpandFile(sFilePath))) { return PAGE_DONE; } else { return PAGE_NOTFOUND; } } return PAGE_NOTFOUND; } else if (sURI.Left(6) == "/mods/" || sURI.Left(10) == "/modfiles/") { ParsePath(); // Make sure modules are treated as directories if (sURI.Right(1) != "/" && sURI.find(".") == CString::npos && sURI.TrimLeft_n("/mods/").TrimLeft_n("/").find("/") == CString::npos) { Redirect(sURI + "/"); return PAGE_DONE; } CModule *pModule = CZNC::Get().GetModules().FindModule(m_sModName); if (!pModule) { // Check if GetSession()->GetUser() is NULL and display // an error in that case if (!ForceLogin()) return PAGE_DONE; pModule = GetSession()->GetUser()->GetModules().FindModule(m_sModName); } if (!pModule) { return PAGE_NOTFOUND; } else if (pModule->WebRequiresLogin() && !ForceLogin()) { return PAGE_PRINT; } else if (pModule->WebRequiresAdmin() && !GetSession()->IsAdmin()) { PrintErrorPage(403, "Forbidden", "You need to be an admin to access this module"); return PAGE_DONE; } else if (!pModule->IsGlobal() && pModule->GetUser() != GetSession()->GetUser()) { PrintErrorPage(403, "Forbidden", "You must login as " + pModule->GetUser()->GetUserName() + " in order to view this page"); return PAGE_DONE; } else if (pModule->OnWebPreRequest(*this, m_sPage)) { return PAGE_DEFERRED; } VWebSubPages& vSubPages = pModule->GetSubPages(); for (unsigned int a = 0; a < vSubPages.size(); a++) { TWebSubPage& SubPage = vSubPages[a]; bool bActive = (m_sModName == pModule->GetModName() && m_sPage == SubPage->GetName()); if (bActive && SubPage->RequiresAdmin() && !GetSession()->IsAdmin()) { PrintErrorPage(403, "Forbidden", "You need to be an admin to access this page"); return PAGE_DONE; } } if (pModule && !pModule->IsGlobal() && (!IsLoggedIn() || pModule->GetUser() != GetSession()->GetUser())) { AddModLoop("UserModLoop", *pModule); } if (sURI.Left(10) == "/modfiles/") { m_Template.AppendPath(GetSkinPath(GetSkinName()) + "/mods/" + m_sModName + "/files/"); m_Template.AppendPath(pModule->GetModDataDir() + "/files/"); if (PrintFile(m_Template.ExpandFile(m_sPage.TrimLeft_n("/")))) { return PAGE_PRINT; } else { return PAGE_NOTFOUND; } } else { SetPaths(pModule, true); /* if a module returns false from OnWebRequest, it does not want the template to be printed, usually because it did a redirect. */ if (pModule->OnWebRequest(*this, m_sPage, m_Template)) { // If they already sent a reply, let's assume // they did what they wanted to do. if (SentHeader()) { return PAGE_DONE; } return PrintTemplate(m_sPage, sPageRet, pModule); } if (!SentHeader()) { PrintErrorPage(404, "Not Implemented", "The requested module does not acknowledge web requests"); } return PAGE_DONE; } } else { CString sPage(sURI.Trim_n("/")); if (sPage.length() < 32) { for (unsigned int a = 0; a < sPage.length(); a++) { unsigned char c = sPage[a]; if ((c < '0' || c > '9') && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && c != '_') { return PAGE_NOTFOUND; } } return PrintTemplate(sPage, sPageRet); } } return PAGE_NOTFOUND; }
void TitleSubst::Load(const CString &filename, const CString §ion) { RageFile f; if( !f.Open(filename) ) { LOG->Trace("Error opening %s: %s", filename.c_str(), f.GetError().c_str() ); return; } CString CurrentSection; TitleTrans tr; while (!f.AtEOF()) { CString line; int ret = f.GetLine( line ); if( ret == 0 ) break; if( ret < 0 ) { LOG->Trace("Error reading %s: %s", filename.c_str(), f.GetError().c_str() ); break; } if(line.size() > 0 && utf8_get_char(line.c_str()) == 0xFEFF) { /* Annoying header that Windows puts on UTF-8 plaintext * files; remove it. */ line.erase(0, utf8_get_char_len(line[0])); } TrimLeft(line); TrimRight(line); if(line.size() == 0) continue; /* blank */ if(line[0] == '#') continue; /* comment */ if(!line.CompareNoCase("DontTransliterate")) { tr.translit = false; continue; } size_t pos = line.find_first_of(':'); if(pos != string::npos) { /* x: y */ CString id = line.substr(0, pos); CString txt = line.substr(pos+1); TrimLeft(txt); /* Surround each regex with ^(...)$, to force all comparisons to default * to being a full-line match. (Add ".*" manually if this isn't wanted.) */ if(!id.CompareNoCase("TitleFrom")) tr.TitleFrom = "^(" + txt + ")$"; else if(!id.CompareNoCase("ArtistFrom")) tr.ArtistFrom = "^(" + txt + ")$"; else if(!id.CompareNoCase("SubtitleFrom")) tr.SubFrom = "^(" + txt + ")$"; else if(!id.CompareNoCase("TitleTo")) tr.Dest.Title = txt; else if(!id.CompareNoCase("ArtistTo")) tr.Dest.Artist = txt; else if(!id.CompareNoCase("SubtitleTo")) tr.Dest.Subtitle = txt; else if(!id.CompareNoCase("TitleTransTo")) tr.Dest.TitleTranslit = txt; else if(!id.CompareNoCase("ArtistTransTo")) tr.Dest.ArtistTranslit = txt; else if(!id.CompareNoCase("SubtitleTransTo")) tr.Dest.SubtitleTranslit = txt; else LOG->Warn( "Unknown TitleSubst tag: \"%s\"", id.c_str() ); } /* Add the translation if this is a terminator (*) or section * marker ([foo]). */ if(line[0] == '*' || line[0] == '[') { if(!CurrentSection.CompareNoCase(section)) AddTrans(tr); /* Reset. */ tr = TitleTrans(); } if(line[0] == '[' && line[line.size()-1] == ']') { CurrentSection = line.substr(1, line.size()-2); } } }
static CString StripHTML(const CString& sFrom) { CString sResult = sFrom; // remove tags: CString::size_type pos = sResult.find('<'); while(pos != CString::npos) { CString::size_type endPos = sResult.find('>', pos); if(endPos != CString::npos) { sResult.erase(pos, endPos - pos + 1); pos = sResult.find('<', pos); } else { sResult.erase(pos); break; } } // remove stupid legay HTML entities: pos = sResult.find('&'); while(pos != CString::npos) { CString::size_type endPos = sResult.find(';', pos); if(endPos != CString::npos) { bool found = false; for(unsigned int c = 0; c < 256; c++) { if(g_szHTMLescapes[c] && !strncasecmp(g_szHTMLescapes[c], (char*)(sResult.c_str() + pos), endPos - pos + 1)) { sResult.erase(pos, endPos - pos + 1); sResult.insert(pos, 1, (char)c); found = true; break; } } if(!found && sResult[pos + 1] != '#') sResult.erase(pos, endPos - pos + 1); pos = sResult.find('&', pos + 1); } else break; } // remove numerical and XML entities: sResult = sResult.Escape_n(CString::EHTML, CString::EASCII); // because entitiy decoding is being done in two steps, // this screws up in certain situations, e.g. &gt; // produces '>' instead of '>' ... but whatever. // normalize whitespace: RE("\\s+").GlobalReplace(" ", &sResult); sResult.Trim(); return sResult; }
/* * Each dwMaxSize, dwTextureColorDepth and iAlphaBits are maximums; we may * use less. iAlphaBits must be 0, 1 or 4. * * XXX: change iAlphaBits == 4 to iAlphaBits == 8 to indicate "as much alpha * as needed", since that's what it really is; still only use 4 in 16-bit textures. * * Dither forces dithering when loading 16-bit textures. * Stretch forces the loaded image to fill the texture completely. */ void RageBitmapTexture::Create() { RageTextureID actualID = GetID(); ASSERT( actualID.filename != "" ); /* Create (and return) a surface ready to be loaded to OpenGL */ /* Load the image into a RageSurface. */ CString error; RageSurface *img = RageSurfaceUtils::LoadFile( actualID.filename, error ); /* Tolerate corrupt/unknown images. */ if( img == NULL ) { CString sWarning = ssprintf( "RageBitmapTexture: Couldn't load %s: %s", actualID.filename.c_str(), error.c_str() ); Dialog::OK( sWarning ); img = RageSurfaceUtils::MakeDummySurface( 64, 64 ); ASSERT( img != NULL ); } if( actualID.bHotPinkColorKey ) RageSurfaceUtils::ApplyHotPinkColorKey( img ); { /* Do this after setting the color key for paletted images; it'll also return * TRAIT_NO_TRANSPARENCY if the color key is never used. */ int traits = RageSurfaceUtils::FindSurfaceTraits(img); if( traits & RageSurfaceUtils::TRAIT_NO_TRANSPARENCY ) actualID.iAlphaBits = 0; else if( traits & RageSurfaceUtils::TRAIT_BOOL_TRANSPARENCY ) actualID.iAlphaBits = 1; } // look in the file name for a format hints CString HintString = GetID().filename + actualID.AdditionalTextureHints; HintString.MakeLower(); if( HintString.find("32bpp") != CString::npos ) actualID.iColorDepth = 32; else if( HintString.find("16bpp") != CString::npos ) actualID.iColorDepth = 16; if( HintString.find("dither") != CString::npos ) actualID.bDither = true; if( HintString.find("stretch") != CString::npos ) actualID.bStretch = true; if( HintString.find("mipmaps") != CString::npos ) actualID.bMipMaps = true; if( HintString.find("nomipmaps") != CString::npos ) actualID.bMipMaps = false; // check for "nomipmaps" after "mipmaps" /* If the image is marked grayscale, then use all bits not used for alpha * for the intensity. This way, if an image has no alpha, you get an 8-bit * grayscale; if it only has boolean transparency, you get a 7-bit grayscale. */ if( HintString.find("grayscale") != CString::npos ) actualID.iGrayscaleBits = 8-actualID.iAlphaBits; /* This indicates that the only component in the texture is alpha; assume all * color is white. */ if( HintString.find("alphamap") != CString::npos ) actualID.iGrayscaleBits = 0; /* No iGrayscaleBits for images that are already paletted. We don't support * that; and that hint is intended for use on images that are already grayscale, * it's not intended to change a color image into a grayscale image. */ if( actualID.iGrayscaleBits != -1 && img->format->BitsPerPixel == 8 ) actualID.iGrayscaleBits = -1; /* Cap the max texture size to the hardware max. */ actualID.iMaxSize = min( actualID.iMaxSize, DISPLAY->GetMaxTextureSize() ); /* Save information about the source. */ m_iSourceWidth = img->w; m_iSourceHeight = img->h; /* image size cannot exceed max size */ m_iImageWidth = min( m_iSourceWidth, actualID.iMaxSize ); m_iImageHeight = min( m_iSourceHeight, actualID.iMaxSize ); /* Texture dimensions need to be a power of two; jump to the next. */ m_iTextureWidth = power_of_two(m_iImageWidth); m_iTextureHeight = power_of_two(m_iImageHeight); /* If we're under 8x8, increase it, to avoid filtering problems on odd hardware. */ if(m_iTextureWidth < 8 || m_iTextureHeight < 8) { actualID.bStretch = true; m_iTextureWidth = max(8, m_iTextureWidth); m_iTextureHeight = max(8, m_iTextureHeight); } ASSERT( m_iTextureWidth <= actualID.iMaxSize ); ASSERT( m_iTextureHeight <= actualID.iMaxSize ); if(actualID.bStretch) { /* The hints asked for the image to be stretched to the texture size, * probably for tiling. */ m_iImageWidth = m_iTextureWidth; m_iImageHeight = m_iTextureHeight; } if( img->w != m_iImageWidth || img->h != m_iImageHeight ) RageSurfaceUtils::Zoom( img, m_iImageWidth, m_iImageHeight ); // Format of the image that we will pass to OpenGL and that we want OpenGL to use RageDisplay::PixelFormat pixfmt; if( actualID.iGrayscaleBits != -1 && DISPLAY->SupportsTextureFormat(RageDisplay::FMT_PAL) ) { RageSurface *dst = RageSurfaceUtils::PalettizeToGrayscale( img, actualID.iGrayscaleBits, actualID.iAlphaBits ); delete img; img = dst; } /* Figure out which texture format to use. */ // if the source is palleted, load palleted no matter what the prefs if(img->format->BitsPerPixel == 8 && DISPLAY->SupportsTextureFormat(RageDisplay::FMT_PAL)) { pixfmt = RageDisplay::FMT_PAL; } else { // not paletted switch( actualID.iColorDepth ) { case 16: { /* Bits of alpha in the source: */ int src_alpha_bits = 8 - img->format->Loss[3]; /* Don't use more than we were hinted to. */ src_alpha_bits = min( actualID.iAlphaBits, src_alpha_bits ); switch( src_alpha_bits ) { case 0: case 1: pixfmt = RageDisplay::FMT_RGB5A1; break; default: pixfmt = RageDisplay::FMT_RGBA4; break; } } break; case 32: pixfmt = RageDisplay::FMT_RGBA8; break; default: RageException::Throw( "Invalid color depth: %d bits", actualID.iColorDepth ); } } /* Make we're using a supported format. Every card supports either RGBA8 or RGBA4. */ if( !DISPLAY->SupportsTextureFormat(pixfmt) ) { pixfmt = RageDisplay::FMT_RGBA8; if( !DISPLAY->SupportsTextureFormat(pixfmt) ) pixfmt = RageDisplay::FMT_RGBA4; } /* Dither if appropriate. XXX: This is a special case: don't bother dithering to * RGBA8888. We actually want to dither only if the destination has greater color * depth on at least one color channel than the source. For example, it doesn't * make sense to do this when pixfmt is RGBA5551 if the image is only RGBA555. */ if( actualID.bDither && (pixfmt==RageDisplay::FMT_RGBA4 || pixfmt==RageDisplay::FMT_RGB5A1) ) { /* Dither down to the destination format. */ const RageDisplay::PixelFormatDesc *pfd = DISPLAY->GetPixelFormatDesc(pixfmt); RageSurface *dst = CreateSurface( img->w, img->h, pfd->bpp, pfd->masks[0], pfd->masks[1], pfd->masks[2], pfd->masks[3] ); RageSurfaceUtils::ErrorDiffusionDither( img, dst ); delete img; img = dst; } /* This needs to be done *after* the final resize, since that resize * may introduce new alpha bits that need to be set. It needs to be * done *before* we set up the palette, since it might change it. */ RageSurfaceUtils::FixHiddenAlpha(img); /* Convert the data to the destination format and dimensions * required by OpenGL if it's not in it already. */ /* We no longer need to do this; pixfmt and the format of img no longer have * to match. This means that if we have a paletted image, but the hardware * doesn't support it, we can leave the data in the smaller paletted format * and let OpenGL dereference it, as long as nothing else (such as dithering) * ends up depalettizing it first. We only have to scale it up to the texture * size, which we won't have to do either if the image size is already a power * of two. */ // const RageDisplay::PixelFormatDesc *pfd = DISPLAY->GetPixelFormatDesc(pixfmt); RageSurfaceUtils::ConvertSurface( img, m_iTextureWidth, m_iTextureHeight, img->fmt.BitsPerPixel, img->fmt.Mask[0], img->fmt.Mask[1], img->fmt.Mask[2], img->fmt.Mask[3] ); // pfd->bpp, pfd->masks[0], pfd->masks[1], pfd->masks[2], pfd->masks[3] ); m_uTexHandle = DISPLAY->CreateTexture( pixfmt, img, actualID.bMipMaps ); CreateFrameRects(); // // Enforce frames in the image have even dimensions. Otherwise, // pixel/texel alignment will be off. // bool bRunCheck = true; // Don't check if the artist intentionally blanked the image by making it very tiny. if( this->GetSourceWidth()<=2 || this->GetSourceHeight()<=2 ) bRunCheck = false; // HACK: Don't check song graphics. Many of them are weird dimensions. if( !TEXTUREMAN->GetOddDimensionWarning() ) bRunCheck = false; if( bRunCheck ) { float fFrameWidth = this->GetSourceWidth() / (float)this->GetFramesWide(); float fFrameHeight = this->GetSourceHeight() / (float)this->GetFramesHigh(); float fBetterFrameWidth = roundf((fFrameWidth+0.99f)/2)*2; float fBetterFrameHeight = roundf((fFrameHeight+0.99f)/2)*2; float fBetterSourceWidth = this->GetFramesWide() * fBetterFrameWidth; float fBetterSourceHeight = this->GetFramesHigh() * fBetterFrameHeight; if( fFrameWidth!=fBetterFrameWidth || fFrameHeight!=fBetterFrameHeight ) { CString sWarning = ssprintf( "The graphic '%s' has frame dimensions that aren't even numbers.\n\n" "The entire image is %dx%d and frame size is %.1fx%.1f.\n\n" "Image quality will be much improved if you resize the graphic to %.0fx%.0f, which is a frame size of %.0fx%.0f.", actualID.filename.c_str(), this->GetSourceWidth(), this->GetSourceHeight(), fFrameWidth, fFrameHeight, fBetterSourceWidth, fBetterSourceHeight, fBetterFrameWidth, fBetterFrameHeight ); LOG->Warn( sWarning ); Dialog::OK( sWarning, "FRAME_DIMENSIONS_WARNING" ); } } delete img; /* See if the apparent "size" is being overridden. */ GetResolutionFromFileName(actualID.filename, m_iSourceWidth, m_iSourceHeight); CString props; props += RageDisplay::PixelFormatToString( pixfmt ) + " "; if(actualID.iAlphaBits == 0) props += "opaque "; if(actualID.iAlphaBits == 1) props += "matte "; if(actualID.bStretch) props += "stretch "; if(actualID.bDither) props += "dither "; props.erase(props.size()-1); LOG->Trace( "RageBitmapTexture: Loaded '%s' (%ux%u); %s, source %d,%d; image %d,%d.", actualID.filename.c_str(), GetTextureWidth(), GetTextureHeight(), props.c_str(), m_iSourceWidth, m_iSourceHeight, m_iImageWidth, m_iImageHeight); }