diff --git a/stepmania/src/BannerCache.cpp b/stepmania/src/BannerCache.cpp index f2fc5958f5..2da4ac6dbb 100644 --- a/stepmania/src/BannerCache.cpp +++ b/stepmania/src/BannerCache.cpp @@ -17,10 +17,10 @@ #include "RageSurfaceUtils_Palettize.h" #include "RageSurfaceUtils_Dither.h" #include "RageSurfaceUtils_Zoom.h" +#include "SpecialFiles.h" #include "Banner.h" -#define CACHE_DIR "Cache/" #define BANNER_CACHE_INDEX CACHE_DIR "banners.cache" diff --git a/stepmania/src/CsvFile.cpp b/stepmania/src/CsvFile.cpp new file mode 100644 index 0000000000..ac38a1fb80 --- /dev/null +++ b/stepmania/src/CsvFile.cpp @@ -0,0 +1,160 @@ +#include "global.h" +#include "CsvFile.h" +#include "RageUtil.h" +#include "RageFile.h" +#include "RageLog.h" + +CsvFile::CsvFile() +{ +} + +bool CsvFile::ReadFile( const RString &sPath ) +{ + m_sPath = sPath; + CHECKPOINT_M( ssprintf("Reading '%s'",m_sPath.c_str()) ); + + RageFile f; + if( !f.Open( m_sPath ) ) + { + LOG->Trace( "Reading '%s' failed: %s", m_sPath.c_str(), f.GetError().c_str() ); + m_sError = f.GetError(); + return 0; + } + + return ReadFile( f ); +} + +bool CsvFile::ReadFile( RageFileBasic &f ) +{ + m_vvs.clear(); + + // hi,"hi2,","""hi3""" + + while( 1 ) + { + RString line; + switch( f.GetLine(line) ) + { + case -1: + m_sError = f.GetError(); + return false; + case 0: + return true; /* eof */ + } + + utf8_remove_bom( line ); + + vector vs; + + while( !line.empty() ) + { + if( line[0] == '\"' ) // quoted value + { + line.erase( line.begin() ); // eat open quote + RString::size_type iEnd = 0; + do + { + iEnd = line.find('\"', iEnd); + if( iEnd == line.npos ) + { + iEnd = line.size()-1; // didn't find an end. Take the whole line. + break; + } + + if( line.size() > iEnd+1 && line[iEnd+1] == '\"' ) // next char is also double quote + iEnd = iEnd+2; + else + break; + } + while(true); + + RString sValue = line; + sValue = sValue.Left( iEnd ); + vs.push_back( sValue ); + + line.erase( line.begin(), line.begin()+iEnd ); + + if( !line.empty() && line[0] == '\"' ) + line.erase( line.begin() ); + } + else + { + RString::size_type iEnd = line.find(','); + if( iEnd == line.npos ) + iEnd = line.size(); // didn't find an end. Take the whole line + + RString sValue = line; + sValue = sValue.Left( iEnd ); + vs.push_back( sValue ); + + line.erase( line.begin(), line.begin()+iEnd ); + } + + if( !line.empty() && line[0] == ',' ) + line.erase( line.begin() ); + } + + m_vvs.push_back( vs ); + } +} + +bool CsvFile::WriteFile( const RString &sPath ) const +{ + RageFile f; + if( !f.Open( sPath, RageFile::WRITE ) ) + { + LOG->Trace( "Writing '%s' failed: %s", sPath.c_str(), f.GetError().c_str() ); + m_sError = f.GetError(); + return false; + } + + return CsvFile::WriteFile( f ); +} + +bool CsvFile::WriteFile( RageFileBasic &f ) const +{ + FOREACH_CONST( StringVector, m_vvs, line ) + { + CString sLine; + FOREACH_CONST( RString, *line, value ) + { + RString sVal = *value; + sVal.Replace( "\"", "\"\"" ); // escape quotes to double-quotes + sLine += "\"" + sVal + "\""; + if( value != line->end()-1 ) + sLine += ","; + } + if( f.PutLine(sLine) == -1 ) + { + m_sError = f.GetError(); + return false; + } + } + return true; +} + + +/* + * (c) 2001-2004 Adam Clauss, Chris Danford + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ diff --git a/stepmania/src/CsvFile.h b/stepmania/src/CsvFile.h new file mode 100644 index 0000000000..7bf8184896 --- /dev/null +++ b/stepmania/src/CsvFile.h @@ -0,0 +1,52 @@ +/* IniFile - Reading and writing .CSV files. */ + +#ifndef CsvFile_H +#define CsvFile_H + +class RageFileBasic; + +class CsvFile +{ +public: + CsvFile(); + + bool ReadFile( const RString &sPath ); + bool ReadFile( RageFileBasic &sFile ); + bool WriteFile( const RString &sPath ) const; + bool WriteFile( RageFileBasic &sFile ) const; + + typedef vector StringVector; + vector m_vvs; + +private: + RString m_sPath; + mutable RString m_sError; +}; + +#endif + +/* + * (c) 2001-2004 Adam Clauss, Chris Danford + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ diff --git a/stepmania/src/Song.cpp b/stepmania/src/Song.cpp index df6c463eb8..42ad0ce0af 100644 --- a/stepmania/src/Song.cpp +++ b/stepmania/src/Song.cpp @@ -23,6 +23,7 @@ #include "Foreach.h" #include "UnlockManager.h" #include "BackgroundUtil.h" +#include "SpecialFiles.h" #include "NotesLoaderSM.h" #include "NotesLoaderDWI.h" @@ -36,8 +37,6 @@ #include #include -#define CACHE_DIR "Cache/" - const int FILE_CACHE_VERSION = 144; // increment this to invalidate cache const float DEFAULT_MUSIC_SAMPLE_LENGTH = 12.f; diff --git a/stepmania/src/SongCacheIndex.cpp b/stepmania/src/SongCacheIndex.cpp index 3946166eae..b586f3c8bd 100644 --- a/stepmania/src/SongCacheIndex.cpp +++ b/stepmania/src/SongCacheIndex.cpp @@ -5,6 +5,7 @@ #include "RageUtil.h" #include "RageFileManager.h" #include "song.h" +#include "SpecialFiles.h" /* * A quick explanation of song cache hashes: Each song has two hashes; a hash of the @@ -23,8 +24,7 @@ * path; we don't have to actually look in the directory (to find out the directory hash) * in order to find the cache file. */ -#define CACHE_DIR "Cache/" -#define CACHE_INDEX CACHE_DIR "index.cache" +#define CACHE_INDEX SpecialFiles::CACHE_DIR + "index.cache" SongCacheIndex *SONGINDEX; diff --git a/stepmania/src/SpecialFiles.cpp b/stepmania/src/SpecialFiles.cpp index 4845cdafcb..82635ed5f2 100644 --- a/stepmania/src/SpecialFiles.cpp +++ b/stepmania/src/SpecialFiles.cpp @@ -7,6 +7,7 @@ const RString SpecialFiles::THEMES_DIR = "Themes/"; const RString SpecialFiles::LANGUAGES_SUBDIR = "Languages/"; const RString SpecialFiles::BASE_LANGUAGE = "en"; const RString SpecialFiles::METRICS_FILE = "metrics.ini"; +const RString SpecialFiles::CACHE_DIR = "Cache/"; /* * (c) 2003-2005 Chris Danford diff --git a/stepmania/src/SpecialFiles.h b/stepmania/src/SpecialFiles.h index f6219b4818..e1876e0ba5 100644 --- a/stepmania/src/SpecialFiles.h +++ b/stepmania/src/SpecialFiles.h @@ -11,6 +11,7 @@ namespace SpecialFiles extern const RString LANGUAGES_SUBDIR; extern const RString BASE_LANGUAGE; extern const RString METRICS_FILE; + extern const RString CACHE_DIR; } #endif diff --git a/stepmania/src/smpackage-net2003.vcproj b/stepmania/src/smpackage-net2003.vcproj index e0336d12dc..499505a90e 100644 --- a/stepmania/src/smpackage-net2003.vcproj +++ b/stepmania/src/smpackage-net2003.vcproj @@ -491,6 +491,12 @@ + + + + diff --git a/stepmania/src/smpackage/LanguagesDlg.cpp b/stepmania/src/smpackage/LanguagesDlg.cpp index 33d0d30596..d24eb637ff 100644 --- a/stepmania/src/smpackage/LanguagesDlg.cpp +++ b/stepmania/src/smpackage/LanguagesDlg.cpp @@ -15,7 +15,8 @@ #include "RageFile.h" #include "languagesdlg.h" #include "RageFileManager.h" - +#include ".\languagesdlg.h" +#include "CsvFile.h" // LanguagesDlg dialog @@ -180,6 +181,8 @@ BEGIN_MESSAGE_MAP(LanguagesDlg, CDialog) ON_LBN_SELCHANGE(IDC_LIST_LANGUAGES, OnSelchangeListLanguages) ON_BN_CLICKED(IDC_BUTTON_CREATE, OnBnClickedButtonCreate) ON_BN_CLICKED(IDC_BUTTON_DELETE, OnBnClickedButtonDelete) + ON_BN_CLICKED(IDC_BUTTON_EXPORT, OnBnClickedButtonExport) + ON_BN_CLICKED(IDC_BUTTON_IMPORT, OnBnClickedButtonImport) END_MESSAGE_MAP() @@ -225,3 +228,117 @@ void LanguagesDlg::OnBnClickedButtonDelete() OnSelchangeListThemes(); } + +struct TranslationLine +{ + RString sSection, sID, sBaseLanguage, sCurrentLanguage; +}; + +void LanguagesDlg::OnBnClickedButtonExport() +{ + // TODO: Add your control notification handler code here + RString sTheme = GetCurrentString( m_listThemes ); + ASSERT( !sTheme.empty() ); + RString sLanguage = GetCurrentString( m_listLanguages ); + ASSERT( !sLanguage.empty() ); + sLanguage = SMPackageUtil::GetLanguageCodeFromDisplayString( sLanguage ); + + RString sBaseLanguageFile = GetLanguageFile( sTheme, SpecialFiles::BASE_LANGUAGE ); + RString sLanguageFile = GetLanguageFile( sTheme, sLanguage ); + + CsvFile csv; + IniFile ini1; + ini1.ReadFile( sBaseLanguageFile ); + IniFile ini2; + ini2.ReadFile( sLanguageFile ); + FOREACH_CONST_Child( &ini1, key ) + { + FOREACH_CONST_Attr( key, value ) + { + TranslationLine tl; + tl.sSection = key->m_sName; + tl.sID = value->first; + tl.sBaseLanguage = value->second; + ini2.GetValue( tl.sSection, tl.sID, tl.sCurrentLanguage ); + + vector vs; + vs.push_back( tl.sSection ); + vs.push_back( tl.sID ); + vs.push_back( tl.sBaseLanguage ); + vs.push_back( tl.sCurrentLanguage ); + csv.m_vvs.push_back( vs ); + } + } + RString sFile = sTheme+"-"+sLanguage+".csv"; + RString sFullFile = "Desktop/"+sFile; + if( csv.WriteFile(sFullFile) ) + MessageBox( ssprintf("Exported to your Desktop as '%s'.",sFile.c_str()) ); + else + MessageBox( ssprintf("Failed to save '%s'.",sFullFile.c_str()) ); +} + +void LanguagesDlg::OnBnClickedButtonImport() +{ + // TODO: Add your control notification handler code here + CFileDialog dialog ( + TRUE, // file open? + NULL, // default file extension + NULL, // default file name + OFN_HIDEREADONLY | OFN_NOCHANGEDIR, // flags + "CSV file (*.csv)|*.csv|||" + ); + int iRet = dialog.DoModal(); + RString sCsvFile = dialog.GetPathName(); + if( iRet != IDOK ) + return; + + RString sTheme = GetCurrentString( m_listThemes ); + ASSERT( !sTheme.empty() ); + RString sLanguage = GetCurrentString( m_listLanguages ); + ASSERT( !sLanguage.empty() ); + sLanguage = SMPackageUtil::GetLanguageCodeFromDisplayString( sLanguage ); + + RageFileOsAbsolute cvsFile; + if( !cvsFile.Open(sCsvFile) ) + { + MessageBox( ssprintf("Error reading file '%s'.",sCsvFile.c_str()) ); + return; + } + CsvFile csv; + if( !csv.ReadFile(cvsFile) ) + { + MessageBox( ssprintf("Error parsing file '%s'.",sCsvFile.c_str()) ); + return; + } + + RString sLanguageFile = GetLanguageFile( sTheme, sLanguage ); + + { + int iRet = MessageBox( ssprintf("Importing these strings will override all data in '%s'. Continue?",sLanguageFile.c_str()), NULL, MB_OKCANCEL ); + if( iRet != IDOK ) + return; + } + + IniFile ini; + + FOREACH_CONST( CsvFile::StringVector, csv.m_vvs, line ) + { + TranslationLine tl; + if( line->size() != 3 && line->size() != 4 ) + { + MessageBox( ssprintf("Error reading '%s': Each line must have either 3 or 4 values. This row has %d values",sCsvFile.c_str(),(int)line->size()) ); + return; + } + tl.sSection = (*line)[0]; + tl.sID = (*line)[1]; + tl.sBaseLanguage = (*line)[2]; + tl.sCurrentLanguage = line->size() == 4 ? (*line)[3] : RString(); + + ini.SetValue( tl.sSection, tl.sID, tl.sCurrentLanguage ); + } + + if( ini.WriteFile(sLanguageFile) ) + MessageBox( ssprintf("Saved '%s'.",sLanguageFile.c_str()) ); + else + MessageBox( ssprintf("Failed to save '%s'.",sLanguageFile.c_str()) ); +} diff --git a/stepmania/src/smpackage/LanguagesDlg.h b/stepmania/src/smpackage/LanguagesDlg.h index 1412dc70a8..bb5a72eef3 100644 --- a/stepmania/src/smpackage/LanguagesDlg.h +++ b/stepmania/src/smpackage/LanguagesDlg.h @@ -27,4 +27,6 @@ public: CListBox m_listLanguages; afx_msg void OnBnClickedButtonCreate(); afx_msg void OnBnClickedButtonDelete(); + afx_msg void OnBnClickedButtonExport(); + afx_msg void OnBnClickedButtonImport(); }; diff --git a/stepmania/src/smpackage/MainMenuDlg.cpp b/stepmania/src/smpackage/MainMenuDlg.cpp index b399d1eccb..63069b8e5e 100644 --- a/stepmania/src/smpackage/MainMenuDlg.cpp +++ b/stepmania/src/smpackage/MainMenuDlg.cpp @@ -247,7 +247,7 @@ void MainMenuDlg::OnBnClickedButtonLaunchGame() void MainMenuDlg::OnBnClickedViewStatistics() { // TODO: Add your control notification handler code here - RString sPersonalDir = GetMyDocumentsDir(); + RString sPersonalDir = SpecialDirs::GetMyDocumentsDir(); RString sFile = sPersonalDir + PRODUCT_ID +"/Save/MachineProfile/Stats.xml"; if( NULL == ::ShellExecute( this->m_hWnd, "open", sFile, "", "", SW_SHOWNORMAL ) ) MessageBox( "Failed to open '" + sFile + "': " + GetLastErrorString() ); @@ -256,7 +256,19 @@ void MainMenuDlg::OnBnClickedViewStatistics() void MainMenuDlg::OnBnClickedClearCache() { // TODO: Add your control notification handler code here - ASSERT(0); + if( DoesFileExist(SpecialFiles::CACHE_DIR) ) + { + if( DeleteRecursive(SpecialFiles::CACHE_DIR) ) + MessageBox( "The cache has been cleared." ); + else + MessageBox( "There was an error clearing the cache." ); + } + else + { + MessageBox( "The cache is already cleared." ); + } + + FlushDirCache(); } void MainMenuDlg::OnBnClickedLanguages() diff --git a/stepmania/src/smpackage/SMPackageUtil.cpp b/stepmania/src/smpackage/SMPackageUtil.cpp index cd2aae3988..b235515e1d 100644 --- a/stepmania/src/smpackage/SMPackageUtil.cpp +++ b/stepmania/src/smpackage/SMPackageUtil.cpp @@ -5,6 +5,7 @@ #include "archutils/Win32/RegistryAccess.h" #include "ProductInfo.h" #include "RageUtil.h" +#include "RageFileManager.h" void SMPackageUtil::WriteStepManiaInstallDirs( const vector& asInstallDirsToWrite ) { @@ -209,6 +210,29 @@ RString SMPackageUtil::GetLanguageCodeFromDisplayString( const RString &sDisplay return s; } +static const RString TEMP_MOUNT_POINT = "/@package/"; + +RageFileOsAbsolute::~RageFileOsAbsolute() +{ + if( !m_sOsDir.empty() ) + FILEMAN->Unmount( "dir", m_sOsDir, TEMP_MOUNT_POINT ); +} + +bool RageFileOsAbsolute::Open( const RString& path, int mode ) +{ + if( !m_sOsDir.empty() ) + FILEMAN->Unmount( "dir", m_sOsDir, TEMP_MOUNT_POINT ); + + m_sOsDir = path; + size_t iStart = m_sOsDir.find_last_of( "/\\" ); + ASSERT( iStart != m_sOsDir.npos ); + m_sOsDir.erase( m_sOsDir.begin()+iStart, m_sOsDir.end() ); + + FILEMAN->Mount( "dir", m_sOsDir, TEMP_MOUNT_POINT ); + RString sFileName = path.Right( path.size()-m_sOsDir.size() ); + return RageFile::Open( TEMP_MOUNT_POINT+sFileName, mode ); +} + /* * (c) 2002-2005 Chris Danford * All rights reserved. diff --git a/stepmania/src/smpackage/SmpackageExportDlg.cpp b/stepmania/src/smpackage/SmpackageExportDlg.cpp index 24f4b6e5fc..1533cc561f 100644 --- a/stepmania/src/smpackage/SmpackageExportDlg.cpp +++ b/stepmania/src/smpackage/SmpackageExportDlg.cpp @@ -14,6 +14,7 @@ #include "EditInsallations.h" #include "IniFile.h" #include "RageFileDriverMemory.h" +#include "archutils/Win32/SpecialDirs.h" #include #include @@ -92,31 +93,6 @@ RString ReplaceInvalidFileNameChars( RString sOldFileName ) return sNewFileName; } -RString GetDesktopPath() -{ - static TCHAR strNull[2] = _T(""); - static TCHAR strPath[MAX_PATH]; - DWORD dwType; - DWORD dwSize = MAX_PATH; - HKEY hKey; - - // Open the appropriate registry key - LONG lResult = RegOpenKeyEx( HKEY_CURRENT_USER, - _T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"), - 0, KEY_READ, &hKey ); - if( ERROR_SUCCESS != lResult ) - return strNull; - - lResult = RegQueryValueEx( hKey, _T("Desktop"), NULL, - &dwType, (BYTE*)strPath, &dwSize ); - RegCloseKey( hKey ); - - if( ERROR_SUCCESS != lResult ) - return strNull; - - return strPath; -} - static bool ExportPackage( RString sPackageName, const vector& asDirectoriesToExport, RString sComment ) { CZipArchive zip; @@ -124,7 +100,7 @@ static bool ExportPackage( RString sPackageName, const vector& asDirect // // Create the package zip file // - const RString sPackagePath = GetDesktopPath() + "\\" + sPackageName; + const RString sPackagePath = "Desktop/" + sPackageName; try { zip.Open( sPackagePath, CZipArchive::zipCreate ); diff --git a/stepmania/src/smpackage/smpackage.cpp b/stepmania/src/smpackage/smpackage.cpp index 370d2256dd..95609e92ae 100644 --- a/stepmania/src/smpackage/smpackage.cpp +++ b/stepmania/src/smpackage/smpackage.cpp @@ -62,7 +62,7 @@ BOOL CSmpackageApp::InitInstance() CString sArg = arrayCommandLineBits[i]; if( sArg == "--machine-profile-stats" ) { - RString sPersonalDir = GetMyDocumentsDir(); + RString sPersonalDir = SpecialDirs::GetMyDocumentsDir(); RString sFile = sPersonalDir + PRODUCT_ID +"/Save/MachineProfile/Stats.xml"; if( NULL == ::ShellExecute( NULL, "open", sFile, "", "", SW_SHOWNORMAL ) ) AfxMessageBox( "Failed to open '" + sFile + "': " + GetLastErrorString() ); diff --git a/stepmania/src/smpackage/smpackageUtil.h b/stepmania/src/smpackage/smpackageUtil.h index ec5e5d68d7..a5c0b4c79d 100644 --- a/stepmania/src/smpackage/smpackageUtil.h +++ b/stepmania/src/smpackage/smpackageUtil.h @@ -21,8 +21,20 @@ namespace SMPackageUtil RString GetLanguageDisplayString( const RString &sIsoCode ); RString GetLanguageCodeFromDisplayString( const RString &sDisplayString ); + + bool GetFileContentsOsAbsolute( const RString &sAbsoluteOsFile, RString &sOut ); } +#include "RageFile.h" + +class RageFileOsAbsolute : public RageFile +{ +public: + bool Open( const RString& path, int mode = READ ); +private: + RString m_sOsDir; +}; + #endif /*