From 021d494157a62a31d4cfb569ec497fc3cc08c2b1 Mon Sep 17 00:00:00 2001 From: electromuis Date: Sat, 19 Jan 2019 17:07:13 +0100 Subject: [PATCH] Padmiss support (#1771) * Add support for folder based MemoryCard usage * Add support for the padmiss.com network by saving scores to individual files. --- src/CMakeData-arch.cmake | 2 + src/NotesWriterSM.cpp | 33 ++- src/NotesWriterSM.h | 8 +- src/StatsManager.cpp | 278 +++++++++++++++--- src/StatsManager.h | 52 ++-- src/arch/MemoryCard/MemoryCardDriver.cpp | 30 +- .../MemoryCardDriverThreaded_Folder.cpp | 131 +++++++++ .../MemoryCardDriverThreaded_Folder.h | 51 ++++ src/arch/arch_default.h | 1 + 9 files changed, 496 insertions(+), 90 deletions(-) create mode 100644 src/arch/MemoryCard/MemoryCardDriverThreaded_Folder.cpp create mode 100644 src/arch/MemoryCard/MemoryCardDriverThreaded_Folder.h diff --git a/src/CMakeData-arch.cmake b/src/CMakeData-arch.cmake index 35fb59313e..a97cd474c3 100644 --- a/src/CMakeData-arch.cmake +++ b/src/CMakeData-arch.cmake @@ -155,10 +155,12 @@ source_group("Arch Specific\\\\Movie Texture" FILES ${SMDATA_ARCH_MOVIE_TEXTURE_ list(APPEND SMDATA_ARCH_MEMORY_SRC "arch/MemoryCard/MemoryCardDriver.cpp" + "arch/MemoryCard/MemoryCardDriverThreaded_Folder.cpp" ) list(APPEND SMDATA_ARCH_MEMORY_HPP "arch/MemoryCard/MemoryCardDriver.h" "arch/MemoryCard/MemoryCardDriver_Null.h" + "arch/MemoryCard/MemoryCardDriverThreaded_Folder.h" ) if(WIN32) diff --git a/src/NotesWriterSM.cpp b/src/NotesWriterSM.cpp index f03affbb22..60515927d9 100644 --- a/src/NotesWriterSM.cpp +++ b/src/NotesWriterSM.cpp @@ -23,7 +23,7 @@ ThemeMetric USE_CREDIT ( "NotesWriterSM", "DescriptionUsesCreditField" ); * @brief Write out the common tags for .SM files. * @param f the file in question. * @param out the Song in question. */ -static void WriteGlobalTags( RageFile &f, Song &out ) +static void WriteGlobalTags( RageFileBasic &f, Song &out ) { TimingData &timing = out.m_SongTiming; f.PutLine( ssprintf( "#TITLE:%s;", SmEscape(out.m_sMainTitle).c_str() ) ); @@ -52,7 +52,7 @@ static void WriteGlobalTags( RageFile &f, Song &out ) default: FAIL_M(ssprintf("Invalid selection display: %i", out.m_SelectionDisplay)); case Song::SHOW_ALWAYS: f.Write( "YES" ); break; - //case Song::SHOW_NONSTOP: f.Write( "NONSTOP" ); break; + //case Song::SHOW_NONSTOP: f.Write( "NONSTOP" ); break; case Song::SHOW_NEVER: f.Write( "NO" ); break; } f.PutLine( ";" ); @@ -66,8 +66,8 @@ static void WriteGlobalTags( RageFile &f, Song &out ) if( out.m_fSpecifiedBPMMin == out.m_fSpecifiedBPMMax ) f.PutLine( ssprintf( "#DISPLAYBPM:%.6f;", out.m_fSpecifiedBPMMin ) ); else - f.PutLine( ssprintf( "#DISPLAYBPM:%.6f:%.6f;", - out.m_fSpecifiedBPMMin, out.m_fSpecifiedBPMMax ) ); + f.PutLine( ssprintf( "#DISPLAYBPM:%.6f:%.6f;", + out.m_fSpecifiedBPMMin, out.m_fSpecifiedBPMMax ) ); break; case DISPLAY_BPM_RANDOM: f.PutLine( ssprintf( "#DISPLAYBPM:*;" ) ); @@ -220,14 +220,14 @@ static RString GetSMNotesTag( const Song &song, const Steps &in ) lines.push_back( "" ); // Escape to prevent some clown from making a comment of "\r\n;" lines.push_back( ssprintf("//---------------%s - %s----------------", - in.m_StepsTypeStr.c_str(), SmEscape(in.GetDescription()).c_str()) ); + in.m_StepsTypeStr.c_str(), SmEscape(in.GetDescription()).c_str()) ); lines.push_back( song.m_vsKeysoundFile.empty() ? "#NOTES:" : "#NOTES2:" ); lines.push_back( ssprintf( " %s:", in.m_StepsTypeStr.c_str() ) ); RString desc = (USE_CREDIT ? in.GetCredit() : in.GetChartName()); lines.push_back( ssprintf( " %s:", SmEscape(desc).c_str() ) ); lines.push_back( ssprintf( " %s:", DifficultyToString(in.GetDifficulty()).c_str() ) ); lines.push_back( ssprintf( " %d:", in.GetMeter() ) ); - + vector asRadarValues; // OpenITG simfiles use 11 radar categories. int categories = 11; @@ -235,8 +235,8 @@ static RString GetSMNotesTag( const Song &song, const Steps &in ) { const RadarValues &rv = in.GetRadarValues( pn ); // Can't use the foreach anymore due to flexible radar lines. - for( RadarCategory rc = (RadarCategory)0; rc < categories; - enum_add( rc, 1 ) ) + for( RadarCategory rc = (RadarCategory)0; rc < categories; + enum_add( rc, 1 ) ) { asRadarValues.push_back( ssprintf("%.6f", rv[rc]) ); } @@ -265,9 +265,14 @@ bool NotesWriterSM::Write( RString sPath, Song &out, const vector& vpSte return false; } + Write( f, out, vpStepsToSave ); +} + +bool NotesWriterSM::Write( RageFileBasic &f, Song &out, const vector& vpStepsToSave ) +{ WriteGlobalTags( f, out ); - FOREACH_CONST( Steps*, vpStepsToSave, s ) + FOREACH_CONST( Steps*, vpStepsToSave, s ) { const Steps* pSteps = *s; RString sTag = GetSMNotesTag( out, *pSteps ); @@ -319,9 +324,9 @@ bool NotesWriterSM::WriteEditFileToMachine( const Song *pSong, Steps *pSteps, RS RString sPath = sDir + GetEditFileName(pSong,pSteps); // Check to make sure that we're not clobering an existing file before opening. - bool bFileNameChanging = - pSteps->GetSavedToDisk() && - pSteps->GetFilename() != sPath; + bool bFileNameChanging = + pSteps->GetSavedToDisk() && + pSteps->GetFilename() != sPath; if( bFileNameChanging && DoesFileExist(sPath) ) { sErrorOut = ssprintf( DESTINATION_ALREADY_EXISTS.GetValue(), sPath.c_str() ); @@ -356,7 +361,7 @@ bool NotesWriterSM::WriteEditFileToMachine( const Song *pSong, Steps *pSteps, RS /* * (c) 2001-2004 Chris Danford, Glenn Maynard * 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 @@ -366,7 +371,7 @@ bool NotesWriterSM::WriteEditFileToMachine( const Song *pSong, Steps *pSteps, RS * 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 diff --git a/src/NotesWriterSM.h b/src/NotesWriterSM.h index 580953cdac..705a884c93 100644 --- a/src/NotesWriterSM.h +++ b/src/NotesWriterSM.h @@ -3,6 +3,7 @@ class Song; class Steps; +class RageFileBasic; /** @brief Writes a Song to an .SM file. */ namespace NotesWriterSM { @@ -11,6 +12,7 @@ namespace NotesWriterSM * @param sPath the path to write the file. * @param out the Song to be written out. * @return its success or failure. */ + bool Write( RageFileBasic &file, Song &out, const vector& vpStepsToSave ); bool Write( RString sPath, Song &out, const vector& vpStepsToSave ); /** * @brief Get some contents about the edit file first. @@ -38,10 +40,10 @@ namespace NotesWriterSM /** * @file - * @author Chris Danford, Glenn Maynard (c) 2001-2004 + * @author Chris Danford, Glenn Maynard (c) 2001-2004 * @section LICENSE * 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 @@ -51,7 +53,7 @@ namespace NotesWriterSM * 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 diff --git a/src/StatsManager.cpp b/src/StatsManager.cpp index 3cf5163c8f..68eaf3c0f5 100644 --- a/src/StatsManager.cpp +++ b/src/StatsManager.cpp @@ -1,4 +1,4 @@ -#include "global.h" +#include "global.h" #include "StatsManager.h" #include "RageFileManager.h" #include "GameState.h" @@ -13,11 +13,18 @@ #include "XmlFile.h" #include "CryptManager.h" #include "XmlFileUtil.h" +#include "Song.h" +#include "RageFileDriverMemory.h" +#include "NotesWriterSM.h" +#include "PlayerOptions.h" +#include "PlayerState.h" +#include "Player.h" StatsManager* STATSMAN = NULL; // global object accessible from anywhere in the program void AddPlayerStatsToProfile( Profile *pProfile, const StageStats &ss, PlayerNumber pn ); XNode* MakeRecentScoreNode( const StageStats &ss, Trail *pTrail, const PlayerStageStats &pss, MultiPlayer mp ); +Preference g_PadmissEnabled("MemoryCardPadmissEnabled", false); StatsManager::StatsManager() { @@ -42,7 +49,7 @@ void StatsManager::Reset() m_CurStageStats.Init(); m_vPlayedStageStats.clear(); m_AccumPlayedStageStats.Init(); - + CalcAccumPlayedStageStats(); } @@ -65,7 +72,7 @@ static StageStats AccumPlayedStageStats( const vector& vss ) /* Scale radar percentages back down to roughly 0..1. Don't scale RadarCategory_TapsAndHolds * and the rest, which are counters. */ - // FIXME: Weight each song by the number of stages it took to account for + // FIXME: Weight each song by the number of stages it took to account for // long, marathon. FOREACH_EnabledPlayer( p ) { @@ -123,7 +130,7 @@ void AddPlayerStatsToProfile( Profile *pProfile, const StageStats &ss, PlayerNum int iMeter = clamp( pSteps->GetMeter(), 0, MAX_METER ); pProfile->m_iNumSongsPlayedByMeter[iMeter] ++; } - + pProfile->m_iTotalDancePoints += ss.m_player[pn].m_iActualDancePoints; if( ss.m_Stage == Stage_Extra1 || ss.m_Stage == Stage_Extra2 ) @@ -227,6 +234,10 @@ void StatsManager::CommitStatsToProfiles( const StageStats *pSS ) LOG->Trace("Adding stats to player profile..."); AddPlayerStatsToProfile( pPlayerProfile, *pSS, pn ); } + + // No marathons etc for now... + if ( g_PadmissEnabled.Get() && pSS->m_playMode == PLAY_MODE_REGULAR ) + SavePadmissScore( pSS, pn ); } } @@ -234,53 +245,226 @@ void StatsManager::CommitStatsToProfiles( const StageStats *pSS ) // in it just accumulate uselessly, wasting several seconds when finishing // a song. So this pref disables it. -Kyz if(!PREFSMAN->m_DisableUploadDir) + SaveUploadFile( pSS ); + + //FileCopy( "Data/TempTestGroups.xml", "Save/Upload/data.xml" ); +} + +void StatsManager::SaveUploadFile( const StageStats *pSS ) +{ + // Save recent scores + auto_ptr xml( new XNode("Stats") ); + xml->AppendChild( "MachineGuid", PROFILEMAN->GetMachineProfile()->m_sGuid ); + + XNode *recent = NULL; + if( GAMESTATE->IsCourseMode() ) + recent = xml->AppendChild( new XNode("RecentCourseScores") ); + else + recent = xml->AppendChild( new XNode("RecentSongScores") ); + + if(!GAMESTATE->m_bMultiplayer) { - // Save recent scores - auto_ptr xml( new XNode("Stats") ); - xml->AppendChild( "MachineGuid", PROFILEMAN->GetMachineProfile()->m_sGuid ); - - XNode *recent = NULL; - if( GAMESTATE->IsCourseMode() ) - recent = xml->AppendChild( new XNode("RecentCourseScores") ); - else - recent = xml->AppendChild( new XNode("RecentSongScores") ); - - if(!GAMESTATE->m_bMultiplayer) + FOREACH_HumanPlayer( p ) { - FOREACH_HumanPlayer( p ) - { - if( pSS->m_player[p].m_HighScore.IsEmpty() ) - continue; - recent->AppendChild( MakeRecentScoreNode( *pSS, GAMESTATE->m_pCurTrail[p], pSS->m_player[p], MultiPlayer_Invalid ) ); - } + if( pSS->m_player[p].m_HighScore.IsEmpty() ) + continue; + recent->AppendChild( MakeRecentScoreNode( *pSS, GAMESTATE->m_pCurTrail[p], pSS->m_player[p], MultiPlayer_Invalid ) ); } - else + } + else + { + FOREACH_EnabledMultiPlayer( mp ) { - FOREACH_EnabledMultiPlayer( mp ) - { - if( pSS->m_multiPlayer[mp].m_HighScore.IsEmpty() ) - continue; - recent->AppendChild( MakeRecentScoreNode( *pSS, GAMESTATE->m_pCurTrail[GAMESTATE->GetMasterPlayerNumber()], pSS->m_multiPlayer[mp], mp ) ); - } - } - - RString sDate = DateTime::GetNowDate().GetString(); - sDate.Replace(":","-"); - - const RString UPLOAD_DIR = "/Save/Upload/"; - RString sFileNameNoExtension = Profile::MakeUniqueFileNameNoExtension(UPLOAD_DIR, sDate + " " ); - RString fn = UPLOAD_DIR + sFileNameNoExtension + ".xml"; - - bool bSaved = XmlFileUtil::SaveToFile( xml.get(), fn, "", false ); - - if( bSaved ) - { - RString sStatsXmlSigFile = fn + SIGNATURE_APPEND; - CryptManager::SignFileToFile(fn, sStatsXmlSigFile); + if( pSS->m_multiPlayer[mp].m_HighScore.IsEmpty() ) + continue; + recent->AppendChild( MakeRecentScoreNode( *pSS, GAMESTATE->m_pCurTrail[GAMESTATE->GetMasterPlayerNumber()], pSS->m_multiPlayer[mp], mp ) ); } } - //FileCopy( "Data/TempTestGroups.xml", "Save/Upload/data.xml" ); + RString sDate = DateTime::GetNowDate().GetString(); + sDate.Replace(":","-"); + + const RString UPLOAD_DIR = "/Save/Upload/"; + RString sFileNameNoExtension = Profile::MakeUniqueFileNameNoExtension(UPLOAD_DIR, sDate + " " ); + RString fn = UPLOAD_DIR + sFileNameNoExtension + ".xml"; + + bool bSaved = XmlFileUtil::SaveToFile( xml.get(), fn, "", false ); + + if( bSaved ) + { + RString sStatsXmlSigFile = fn + SIGNATURE_APPEND; + CryptManager::SignFileToFile(fn, sStatsXmlSigFile); + } +} + +void StatsManager::SavePadmissScore( const StageStats *pSS, PlayerNumber pn ) +{ + const PlayerStageStats *playerStats = &pSS->m_player[ pn ]; + + auto_ptr xml( new XNode("SongScore") ); + + RString sDate = DateTime::GetNowDate().GetString(); + sDate.Replace(":","-"); + + XNode *taps = xml->AppendChild( "TapNoteScores" ); + FOREACH_ENUM( TapNoteScore, tns ) + if ( tns != TNS_None ) + taps->AppendChild( TapNoteScoreToString( tns ), playerStats->m_iTapNoteScores[ tns ] ); + + XNode *radar_actual = xml->AppendChild( "RadarActual" ); + radar_actual->AppendChild( playerStats->m_radarActual.CreateNode( true, false) ); + + XNode *radar_possible = xml->AppendChild( "RadarPossible" ); + radar_possible->AppendChild( playerStats->m_radarPossible.CreateNode( true, false ) ); + + Profile *pp = PROFILEMAN->GetProfile( playerStats->m_player_number ); + xml->AppendChild( "ScoreValue", playerStats->GetPercentDancePoints() ); + xml->AppendChild( "PlayerNumber", playerStats->m_player_number ); + xml->AppendChild( "PlayerName", pp->m_sDisplayName ); + xml->AppendChild( "PlayerGuid", pp->m_sGuid ); + + Steps *steps = playerStats->m_vpPossibleSteps[0]; // XXX Courses and such + Song *song = steps->m_pSong; + steps->Decompress(); // Hashing won't work unless the steps are decompressed + XNode *stepdata = xml->AppendChild( "Steps" ); + stepdata->AppendChild( "Hash", steps->GetHash() ); + stepdata->AppendChild( "Meter", steps->GetMeter() ); + stepdata->AppendChild( "StepArtist", steps->GetCredit() ); + stepdata->AppendChild( "StepsType", steps->m_StepsTypeStr ); + RageFileObjMem f; + vector stepv; + stepv.push_back(steps); + NotesWriterSM::Write( f, *song, stepv ); + stepdata->AppendChild( "StepData", f.GetString() ); + + XNode *songdata = xml->AppendChild( "SongData" ); + songdata->AppendChild( "Title", song->m_sMainTitle ); + songdata->AppendChild( "TitleTranslit", song->m_sMainTitleTranslit ); + songdata->AppendChild( "SubTitle", song->m_sSubTitle ); + songdata->AppendChild( "SubTitleTranslit", song->m_sSubTitleTranslit ); + songdata->AppendChild( "Artist", song->m_sArtist ); + songdata->AppendChild( "ArtistTranslit", song->m_sArtistTranslit ); + songdata->AppendChild( "Duration", song->m_fMusicLengthSeconds ); + + const PlayerOptions &opts = GAMESTATE->m_pPlayerState[ pn ]->m_PlayerOptions.Get( ModsLevel_Preferred ); + XNode *mods = xml->AppendChild( "Mods" ); + mods->AppendChild( "MusicRate", pSS->m_fMusicRate ); +#define ADD_BOOLEAN_OPTION( parent, name, opts ) \ + if ( opts[ PlayerOptions::name ] ) \ + parent->AppendChild( #name ) + + XNode *turns = mods->AppendChild( "Turns" ); + ADD_BOOLEAN_OPTION( turns, TURN_MIRROR, opts.m_bTurns ); + ADD_BOOLEAN_OPTION( turns, TURN_BACKWARDS, opts.m_bTurns ); + ADD_BOOLEAN_OPTION( turns, TURN_LEFT, opts.m_bTurns ); + ADD_BOOLEAN_OPTION( turns, TURN_RIGHT, opts.m_bTurns ); + ADD_BOOLEAN_OPTION( turns, TURN_SHUFFLE, opts.m_bTurns ); + ADD_BOOLEAN_OPTION( turns, TURN_SOFT_SHUFFLE, opts.m_bTurns ); + ADD_BOOLEAN_OPTION( turns, TURN_SUPER_SHUFFLE, opts.m_bTurns ); + + XNode *transforms = mods->AppendChild( "Transforms" ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_NOHOLDS, opts.m_bTransforms ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_NOROLLS, opts.m_bTransforms ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_NOMINES, opts.m_bTransforms ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_LITTLE, opts.m_bTransforms ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_WIDE, opts.m_bTransforms ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_BIG, opts.m_bTransforms ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_QUICK, opts.m_bTransforms ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_BMRIZE, opts.m_bTransforms ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_SKIPPY, opts.m_bTransforms ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_MINES, opts.m_bTransforms ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_ATTACKMINES, opts.m_bTransforms ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_ECHO, opts.m_bTransforms ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_STOMP, opts.m_bTransforms ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_PLANTED, opts.m_bTransforms ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_FLOORED, opts.m_bTransforms ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_TWISTER, opts.m_bTransforms ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_HOLDROLLS, opts.m_bTransforms ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_NOJUMPS, opts.m_bTransforms ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_NOHANDS, opts.m_bTransforms ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_NOLIFTS, opts.m_bTransforms ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_NOFAKES, opts.m_bTransforms ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_NOQUADS, opts.m_bTransforms ); + ADD_BOOLEAN_OPTION( transforms, TRANSFORM_NOSTRETCH, opts.m_bTransforms ); + +#define ADD_FLOAT_OPTION( parent, name, opts ) \ + do { \ + float val = opts[ PlayerOptions::name ]; \ + if ( val != 0.0f ) \ + parent->AppendChild( #name, val ); \ + } while (0) + + XNode *accels = mods->AppendChild( "Accels" ); + ADD_FLOAT_OPTION( accels, ACCEL_BOOST, opts.m_fAccels ); + ADD_FLOAT_OPTION( accels, ACCEL_BRAKE, opts.m_fAccels ); + ADD_FLOAT_OPTION( accels, ACCEL_WAVE, opts.m_fAccels ); + ADD_FLOAT_OPTION( accels, ACCEL_EXPAND, opts.m_fAccels ); + ADD_FLOAT_OPTION( accels, ACCEL_BOOMERANG, opts.m_fAccels ); + + XNode *effects = mods->AppendChild( "Effects" ); + ADD_FLOAT_OPTION( effects, EFFECT_DRUNK, opts.m_fEffects ); + ADD_FLOAT_OPTION( effects, EFFECT_DIZZY, opts.m_fEffects ); + ADD_FLOAT_OPTION( effects, EFFECT_CONFUSION, opts.m_fEffects ); + ADD_FLOAT_OPTION( effects, EFFECT_MINI, opts.m_fEffects ); + ADD_FLOAT_OPTION( effects, EFFECT_TINY, opts.m_fEffects ); + ADD_FLOAT_OPTION( effects, EFFECT_FLIP, opts.m_fEffects ); + ADD_FLOAT_OPTION( effects, EFFECT_INVERT, opts.m_fEffects ); + ADD_FLOAT_OPTION( effects, EFFECT_TORNADO, opts.m_fEffects ); + ADD_FLOAT_OPTION( effects, EFFECT_TIPSY, opts.m_fEffects ); + ADD_FLOAT_OPTION( effects, EFFECT_BUMPY, opts.m_fEffects ); + ADD_FLOAT_OPTION( effects, EFFECT_BEAT, opts.m_fEffects ); + ADD_FLOAT_OPTION( effects, EFFECT_XMODE, opts.m_fEffects ); + ADD_FLOAT_OPTION( effects, EFFECT_TWIRL, opts.m_fEffects ); + ADD_FLOAT_OPTION( effects, EFFECT_ROLL, opts.m_fEffects ); + + XNode *appearances = mods->AppendChild( "Appearances" ); + ADD_FLOAT_OPTION( appearances, APPEARANCE_HIDDEN, opts.m_fAppearances ); + ADD_FLOAT_OPTION( appearances, APPEARANCE_HIDDEN_OFFSET, opts.m_fAppearances ); + ADD_FLOAT_OPTION( appearances, APPEARANCE_SUDDEN, opts.m_fAppearances ); + ADD_FLOAT_OPTION( appearances, APPEARANCE_SUDDEN_OFFSET, opts.m_fAppearances ); + ADD_FLOAT_OPTION( appearances, APPEARANCE_STEALTH, opts.m_fAppearances ); + ADD_FLOAT_OPTION( appearances, APPEARANCE_BLINK, opts.m_fAppearances ); + ADD_FLOAT_OPTION( appearances, APPEARANCE_RANDOMVANISH, opts.m_fAppearances ); + + XNode *scrolls = mods->AppendChild( "Scrolls" ); + ADD_FLOAT_OPTION( scrolls, SCROLL_REVERSE, opts.m_fScrolls ); + ADD_FLOAT_OPTION( scrolls, SCROLL_SPLIT, opts.m_fScrolls ); + ADD_FLOAT_OPTION( scrolls, SCROLL_ALTERNATE, opts.m_fScrolls ); + ADD_FLOAT_OPTION( scrolls, SCROLL_CROSS, opts.m_fScrolls ); + ADD_FLOAT_OPTION( scrolls, SCROLL_CENTERED, opts.m_fScrolls ); + + mods->AppendChild( "NoteSkin", opts.m_sNoteSkin ); + + XNode *perspectives = mods->AppendChild( "Perspectives" ); + perspectives->AppendChild( "Tilt", opts.m_fPerspectiveTilt ); + perspectives->AppendChild( "Skew", opts.m_fSkew ); + + RString speedModType; + float speedModValue; + if ( opts.m_fTimeSpacing ) + { + speedModType = "ConstantBPM"; + speedModValue = opts.m_fScrollBPM; + } + else if ( opts.m_fMaxScrollBPM ) + { + speedModType = "MaxBPM"; + speedModValue = opts.m_fMaxScrollBPM; + } + else + { + speedModType = "Multiplier"; + speedModValue = opts.m_fScrollSpeed; + } + mods->AppendChild( "ScrollSpeed", speedModValue )->AppendAttr( "Type", speedModType ); + + XNode *timingWindows = xml->AppendChild( "TimingWindows" ); + FOREACH_ENUM( TimingWindow, tw ) + timingWindows->AppendChild( TimingWindowToString( tw ), Player::GetWindowSeconds( tw ) ); + + RString dir = "/Save/Padmiss/"; + RString fn = dir + Profile::MakeUniqueFileNameNoExtension( dir, sDate + " " ) + ".xml"; + XmlFileUtil::SaveToFile( xml.get(), fn, "", true ); } void StatsManager::UnjoinPlayer( PlayerNumber pn ) @@ -330,7 +514,7 @@ void StatsManager::GetStepsInUse( set &apInUseOut ) const // lua start #include "LuaBinding.h" -/** @brief Allow Lua to have access to the StatsManager. */ +/** @brief Allow Lua to have access to the StatsManager. */ class LunaStatsManager: public Luna { public: @@ -438,7 +622,7 @@ LUA_REGISTER_CLASS( StatsManager ) /* * (c) 2001-2004 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 @@ -448,7 +632,7 @@ LUA_REGISTER_CLASS( StatsManager ) * 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 diff --git a/src/StatsManager.h b/src/StatsManager.h index 70bd664efc..2c9fd7468e 100644 --- a/src/StatsManager.h +++ b/src/StatsManager.h @@ -7,36 +7,40 @@ class StatsManager { public: - StatsManager(); - ~StatsManager(); + StatsManager(); + ~StatsManager(); - void Reset(); + void Reset(); - /** - * @brief The current Stage stats. - * - * This is not necessarily passed stage stats if this is an Extra Stage. */ - StageStats m_CurStageStats; - vector m_vPlayedStageStats; + /** + * @brief The current Stage stats. + * + * This is not necessarily passed stage stats if this is an Extra Stage. */ + StageStats m_CurStageStats; + vector m_vPlayedStageStats; - // Only the latest 3 normal songs + passed extra stages. - void GetFinalEvalStageStats( StageStats& statsOut ) const; + // Only the latest 3 normal songs + passed extra stages. + void GetFinalEvalStageStats( StageStats& statsOut ) const; - // All stages played. Returns a ref to the private member so that - // the object will remain alive while Lua is operating on it. - void CalcAccumPlayedStageStats(); - StageStats& GetAccumPlayedStageStats() { return m_AccumPlayedStageStats; } + // All stages played. Returns a ref to the private member so that + // the object will remain alive while Lua is operating on it. + void CalcAccumPlayedStageStats(); + StageStats& GetAccumPlayedStageStats() { return m_AccumPlayedStageStats; } - static void CommitStatsToProfiles( const StageStats *pSS ); + static void CommitStatsToProfiles( const StageStats *pSS ); - void UnjoinPlayer( PlayerNumber pn ); - void GetStepsInUse( set &apInUseOut ) const; + void UnjoinPlayer( PlayerNumber pn ); + void GetStepsInUse( set &apInUseOut ) const; - // Lua - void PushSelf( lua_State *L ); + // Lua + void PushSelf( lua_State *L ); + +protected: + static void SaveUploadFile( const StageStats *pSS ); + static void SavePadmissScore( const StageStats *pSS, PlayerNumber pn ); private: - StageStats m_AccumPlayedStageStats; + StageStats m_AccumPlayedStageStats; }; extern StatsManager* STATSMAN; // global and accessible from anywhere in our program @@ -48,7 +52,7 @@ extern StatsManager* STATSMAN; // global and accessible from anywhere in our pro * @author Chris Danford (c) 2001-2004 * @section LICENSE * 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 @@ -58,7 +62,7 @@ extern StatsManager* STATSMAN; // global and accessible from anywhere in our pro * 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 @@ -68,4 +72,4 @@ extern StatsManager* STATSMAN; // global and accessible from anywhere in our pro * 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. - */ + */ \ No newline at end of file diff --git a/src/arch/MemoryCard/MemoryCardDriver.cpp b/src/arch/MemoryCard/MemoryCardDriver.cpp index 7600370f72..0b3f51351b 100644 --- a/src/arch/MemoryCard/MemoryCardDriver.cpp +++ b/src/arch/MemoryCard/MemoryCardDriver.cpp @@ -7,6 +7,23 @@ static const RString TEMP_MOUNT_POINT = "/@mctemptimeout/"; +enum MemoryCardDriverType { + MemoryCardDriverType_Usb, + MemoryCardDriverType_Directory, + NUM_MemoryCardDriverType, + MemoryCardDriverType_Invalid +}; + +static const char *MemoryCardDriverTypeNames[] = { + "USB", + "Directory" +}; +XToString(MemoryCardDriverType); +StringToX(MemoryCardDriverType); +LuaXType(MemoryCardDriverType); + +Preference g_MemoryCardDriver("MemoryCardDriver", MemoryCardDriverType_Usb); + bool UsbStorageDevice::operator==(const UsbStorageDevice& other) const { // LOG->Trace( "Comparing %d %d %d %s %s to %d %d %d %s %s", @@ -126,16 +143,25 @@ MemoryCardDriver *MemoryCardDriver::Create() { MemoryCardDriver *ret = NULL; + switch( g_MemoryCardDriver ) + { + case MemoryCardDriverType_Directory: + ret = new MemoryCardDriverThreaded_Folder; + break; + case MemoryCardDriverType_Usb: #ifdef ARCH_MEMORY_CARD_DRIVER - ret = new ARCH_MEMORY_CARD_DRIVER; + ret = new ARCH_MEMORY_CARD_DRIVER; #endif + break; + } if( !ret ) ret = new MemoryCardDriver_Null; - + return ret; } + /* * (c) 2002-2004 Glenn Maynard * All rights reserved. diff --git a/src/arch/MemoryCard/MemoryCardDriverThreaded_Folder.cpp b/src/arch/MemoryCard/MemoryCardDriverThreaded_Folder.cpp new file mode 100644 index 0000000000..b491647ba0 --- /dev/null +++ b/src/arch/MemoryCard/MemoryCardDriverThreaded_Folder.cpp @@ -0,0 +1,131 @@ +#include "global.h" +#include "MemoryCardDriverThreaded_Folder.h" +#include "RageLog.h" +#include "RageUtil.h" +#include "Foreach.h" +#include "PlayerNumber.h" +#include "MemoryCardManager.h" + +#include +#include +#include +#include +#include + +static int g_currentSerial = 0; + +MemoryCardDriverThreaded_Folder::MemoryCardDriverThreaded_Folder() +{ + m_LastDevices = 0; +} + +MemoryCardDriverThreaded_Folder::~MemoryCardDriverThreaded_Folder() +{ +} + +bool MemoryCardDriverThreaded_Folder::FolderExists(RString path) +{ + if (path.empty()) { + return false; + } + + const char *pathname = path.c_str(); + struct stat info; + + int statRC = stat( pathname, &info ); + if( statRC != 0 ) + { + if (errno == ENOENT) { return false; } // something along the path does not exist + if (errno == ENOTDIR) { return false; } // something in path prefix is not a dir + return false; + } + + if( info.st_mode & S_IFDIR ) { + return true; + } + + return false; +} + +bool MemoryCardDriverThreaded_Folder::TestWrite( UsbStorageDevice* pDevice ) +{ + //TODO + + return true; +} + +int MemoryCardDriverThreaded_Folder::GetActivePlayerMask() +{ + int ret = 0; + + FOREACH_PlayerNumber( p ) + { + const RString folder = MEMCARDMAN->m_sMemoryCardOsMountPoint[p]; + + if(FolderExists(folder)) { + ret |= 1 << p; + } + } + + return ret; +} + +bool MemoryCardDriverThreaded_Folder::USBStorageDevicesChanged() +{ + return GetActivePlayerMask() != m_LastDevices; +} + +void MemoryCardDriverThreaded_Folder::GetUSBStorageDevices( vector& vDevicesOut ) +{ + LOG->Trace( "GetUSBStorageDevices" ); + + vDevicesOut.clear(); + m_LastDevices = GetActivePlayerMask(); + + FOREACH_PlayerNumber( p ) + { + if((m_LastDevices & (1 << p)) > 0){ + UsbStorageDevice usbd; + usbd.sSerial = StringConversion::ToString(g_currentSerial++); + usbd.sSysPath = MEMCARDMAN->m_sMemoryCardOsMountPoint[p]; + usbd.sOsMountDir = MEMCARDMAN->m_sMemoryCardOsMountPoint[p]; + + vDevicesOut.push_back( usbd ); + } + } +} + +bool MemoryCardDriverThreaded_Folder::Mount( UsbStorageDevice* pDevice ) +{ + return true; +} + +void MemoryCardDriverThreaded_Folder::Unmount( UsbStorageDevice* pDevice ) +{ + return; +} + +/* + * (c) 2018-2019 Electromuis + * 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/src/arch/MemoryCard/MemoryCardDriverThreaded_Folder.h b/src/arch/MemoryCard/MemoryCardDriverThreaded_Folder.h new file mode 100644 index 0000000000..e07630390d --- /dev/null +++ b/src/arch/MemoryCard/MemoryCardDriverThreaded_Folder.h @@ -0,0 +1,51 @@ +#ifndef MemoryCardDriverThreaded_Folder_H +#define MemoryCardDriverThreaded_Folder_H 1 + +#include "MemoryCardDriver.h" + +class MemoryCardDriverThreaded_Folder : public MemoryCardDriver +{ +public: + MemoryCardDriverThreaded_Folder(); + virtual ~MemoryCardDriverThreaded_Folder(); + + virtual bool Mount( UsbStorageDevice* pDevice ); + virtual void Unmount( UsbStorageDevice* pDevice ); + +protected: + void GetUSBStorageDevices( vector& vDevicesOut ); + bool USBStorageDevicesChanged(); + bool TestWrite( UsbStorageDevice* pDevice ); + bool FolderExists(RString path); + + int GetActivePlayerMask(); + int m_LastDevices; +}; + +#endif + + +/* + * (c) 2018-2019 Electromuis + * 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/src/arch/arch_default.h b/src/arch/arch_default.h index 48fe11a4d9..3c422e157b 100644 --- a/src/arch/arch_default.h +++ b/src/arch/arch_default.h @@ -54,6 +54,7 @@ /* All use these. */ #include "LoadingWindow/LoadingWindow_Null.h" #include "MemoryCard/MemoryCardDriver_Null.h" +#include "MemoryCard/MemoryCardDriverThreaded_Folder.h" #endif