99#include " import/details.h"
1010#include " import/backup.h"
1111
12+ /* TODO: There was an efficiency drop with this since the change to libfp that uses Qx::Sql, as the results are
13+ * all returned as a list and not something iterable that pulls from the DB as needed. Benchmark this, and
14+ * if the memory usage is particularly high, then we might need to introduce an iterable wrapper to libfp
15+ * that holds Qx::SqlResult internally, so that we can go back to not loading the whole list at once.
16+ *
17+ * Alternatively, since most of this comes from the fact that we query a lot up-front to get sizes for
18+ * progress tracking, we could add methods to libfp that just return the size of the result only (i.e.
19+ * without actually making the full query), use those for the progress setup, and then run the actual
20+ * query that returns the whole list immediately before they're needed. This won't reduce things quite as much,
21+ * but is still better than nothing.
22+ */
23+
1224namespace Import
1325{
1426
@@ -49,6 +61,29 @@ Qx::ProgressGroup* Worker::initializeProgressGroup(const QString& groupName, qui
4961 return pg;
5062}
5163
64+ Fp::DbError Worker::loadGamesByPlatform (QList<PlatformQueryResult>& games, const QStringList& platforms, const InclusionOptions& inclusions, const QList<QUuid>& idWhitelist)
65+ {
66+ games.clear ();
67+ games.reserve (platforms.size ());
68+ Fp::Db* db = mFlashpointInstall ->database ();
69+
70+ // Build common filter
71+ Fp::Db::GameFilter gf{.excludedTagIds = inclusions.excludedTagIds , .includedIds = idWhitelist, .includeAnimations = inclusions.includeAnimations };
72+
73+ // Load one platform at a time
74+ for (const auto & pf : platforms)
75+ {
76+ PlatformQueryResult& res = games.emplaceBack ();
77+ res.platform = pf;
78+ gf.platforms = {pf};
79+
80+ if (auto err = db->searchGames (res.result , gf); err.isValid ())
81+ return err;
82+ }
83+
84+ return {};
85+ }
86+
5287Qx::Error Worker::preloadPlaylists (QList<Fp::Playlist>& targetPlaylists)
5388{
5489 // Reset playlists
@@ -64,7 +99,7 @@ Qx::Error Worker::preloadPlaylists(QList<Fp::Playlist>& targetPlaylists)
6499
65100 targetPlaylists.removeIf ([&plNames, inclAnim](const Fp::Playlist& pl){
66101 return !plNames.contains (pl.title ()) ||
67- (pl.library () == Fp::Db::Table_Game::ENTRY_ANIM_LIBRARY && !inclAnim);
102+ (pl.library () == Fp::Library::Animation && !inclAnim);
68103 });
69104
70105 return Qx::Error ();
@@ -81,61 +116,32 @@ QList<QUuid> Worker::getPlaylistSpecificGameIds(const QList<Fp::Playlist>& playl
81116 return playlistSpecGameIds;
82117}
83118
84- Worker::Result Worker::processPlatformGames (Qx::Error& errorReport, std::unique_ptr<Lr::IPlatformDoc>& platformDoc, Fp::Db::QueryBuffer & gameQueryResult)
119+ Worker::Result Worker::processPlatformGames (Qx::Error& errorReport, std::unique_ptr<Lr::IPlatformDoc>& platformDoc, const PlatformQueryResult & gameQueryResult)
85120{
86121 Fp::Db* db = mFlashpointInstall ->database ();
87122
88123 // Add/Update games
89- for (int j = 0 ; j < gameQueryResult.size ; j++ )
124+ for (const auto & game : gameQueryResult.result )
90125 {
91- // Advance to next record
92- gameQueryResult.result .next ();
93-
94- // Form game from record
95- Fp::Game::Builder fpGb;
96- fpGb.wId (gameQueryResult.result .value (Fp::Db::Table_Game::COL_ID).toString ());
97- fpGb.wTitle (gameQueryResult.result .value (Fp::Db::Table_Game::COL_TITLE).toString ().remove (Qx::RegularExpression::LINE_BREAKS));
98- fpGb.wSeries (gameQueryResult.result .value (Fp::Db::Table_Game::COL_SERIES).toString ().remove (Qx::RegularExpression::LINE_BREAKS));
99- fpGb.wDeveloper (gameQueryResult.result .value (Fp::Db::Table_Game::COL_DEVELOPER).toString ().remove (Qx::RegularExpression::LINE_BREAKS));
100- fpGb.wPublisher (gameQueryResult.result .value (Fp::Db::Table_Game::COL_PUBLISHER).toString ().remove (Qx::RegularExpression::LINE_BREAKS));
101- fpGb.wDateAdded (gameQueryResult.result .value (Fp::Db::Table_Game::COL_DATE_ADDED).toString ());
102- fpGb.wDateModified (gameQueryResult.result .value (Fp::Db::Table_Game::COL_DATE_MODIFIED).toString ());
103- fpGb.wBroken (gameQueryResult.result .value (Fp::Db::Table_Game::COL_BROKEN).toString ());
104- fpGb.wPlayMode (gameQueryResult.result .value (Fp::Db::Table_Game::COL_PLAY_MODE).toString ());
105- fpGb.wStatus (gameQueryResult.result .value (Fp::Db::Table_Game::COL_STATUS).toString ());
106- fpGb.wNotes (gameQueryResult.result .value (Fp::Db::Table_Game::COL_NOTES).toString ());
107- fpGb.wSource (gameQueryResult.result .value (Fp::Db::Table_Game::COL_SOURCE).toString ().remove (Qx::RegularExpression::LINE_BREAKS));
108- fpGb.wAppPath (gameQueryResult.result .value (Fp::Db::Table_Game::COL_APP_PATH).toString ());
109- fpGb.wLaunchCommand (gameQueryResult.result .value (Fp::Db::Table_Game::COL_LAUNCH_COMMAND).toString ());
110- fpGb.wReleaseDate (gameQueryResult.result .value (Fp::Db::Table_Game::COL_RELEASE_DATE).toString ());
111- fpGb.wVersion (gameQueryResult.result .value (Fp::Db::Table_Game::COL_VERSION).toString ().remove (Qx::RegularExpression::LINE_BREAKS));
112- fpGb.wOriginalDescription (gameQueryResult.result .value (Fp::Db::Table_Game::COL_ORIGINAL_DESC).toString ());
113- fpGb.wLanguage (gameQueryResult.result .value (Fp::Db::Table_Game::COL_LANGUAGE).toString ().remove (Qx::RegularExpression::LINE_BREAKS));
114- fpGb.wOrderTitle (gameQueryResult.result .value (Fp::Db::Table_Game::COL_ORDER_TITLE).toString ().remove (Qx::RegularExpression::LINE_BREAKS));
115- fpGb.wLibrary (gameQueryResult.result .value (Fp::Db::Table_Game::COL_LIBRARY).toString ());
116- fpGb.wPlatformName (gameQueryResult.result .value (Fp::Db::Table_Game::COL_PLATFORM_NAME).toString ());
117-
118- Fp::Game builtGame = fpGb.build ();
119-
120126 // Get tags
121127 Fp::GameTags gameTags;
122- if (auto dbErr = db->getGameTags (gameTags, builtGame .id ()); dbErr.isValid ())
128+ if (auto dbErr = db->getGameTags (gameTags, game .id ()); dbErr.isValid ())
123129 {
124130 errorReport = Qx::Error ();
125131 return Failed;
126132 }
127133
128134 // Construct full game set
129135 Fp::Set::Builder sb;
130- sb.wGame (builtGame ); // From above
136+ sb.wGame (game ); // From above
131137 sb.wTags (gameTags); // From above
132138 if (!mOptionSet .excludeAddApps )
133139 {
134140 // Add playable add apps
135- auto addApps = mAddAppsCache .values (builtGame .id ());
141+ auto addApps = mAddAppsCache .values (game .id ());
136142 addApps.removeIf ([](const Fp::AddApp& aa){ return !aa.isPlayable (); });
137143 sb.wAddApps (addApps);
138- mAddAppsCache .remove (builtGame .id ());
144+ mAddAppsCache .remove (game .id ());
139145 }
140146
141147 // Add set to doc
@@ -174,29 +180,13 @@ void Worker::cullUnimportedPlaylistGames(QList<Fp::Playlist>& playlists)
174180 }
175181}
176182
177- Worker::Result Worker::preloadAddApps (Qx::Error& errorReport, Fp::Db::QueryBuffer & addAppQuery)
183+ Worker::Result Worker::preloadAddApps (Qx::Error& errorReport, const QList< Fp::AddApp> & addAppQuery)
178184{
179- mAddAppsCache .reserve (addAppQuery.size );
180- for (int i = 0 ; i < addAppQuery. size ; i++ )
185+ mAddAppsCache .reserve (addAppQuery.size () );
186+ for (const auto & aa : addAppQuery)
181187 {
182- // Advance to next record
183- addAppQuery.result .next ();
184-
185- // Form additional app from record
186- Fp::AddApp::Builder fpAab;
187- fpAab.wId (addAppQuery.result .value (Fp::Db::Table_Add_App::COL_ID).toString ());
188- fpAab.wAppPath (addAppQuery.result .value (Fp::Db::Table_Add_App::COL_APP_PATH).toString ());
189- fpAab.wAutorunBefore (addAppQuery.result .value (Fp::Db::Table_Add_App::COL_AUTORUN).toString ());
190- fpAab.wLaunchCommand (addAppQuery.result .value (Fp::Db::Table_Add_App::COL_LAUNCH_COMMAND).toString ());
191- fpAab.wName (addAppQuery.result .value (Fp::Db::Table_Add_App::COL_NAME).toString ().remove (Qx::RegularExpression::LINE_BREAKS));
192- fpAab.wWaitExit (addAppQuery.result .value (Fp::Db::Table_Add_App::COL_WAIT_EXIT).toString ());
193- fpAab.wParentId (addAppQuery.result .value (Fp::Db::Table_Add_App::COL_PARENT_ID).toString ());
194-
195- // Build additional app
196- Fp::AddApp additionalApp = fpAab.build ();
197-
198188 // Add to cache
199- mAddAppsCache .insert (additionalApp. parentId (), additionalApp );
189+ mAddAppsCache .insert (aa. parentGameId (), aa );
200190
201191 // Update progress dialog value
202192 if (mCanceled )
@@ -213,7 +203,7 @@ Worker::Result Worker::preloadAddApps(Qx::Error& errorReport, Fp::Db::QueryBuffe
213203 return Successful;
214204}
215205
216- Worker::Result Worker::processGames (Qx::Error& errorReport, QList<Fp::Db::QueryBuffer >& primary, QList<Fp::Db::QueryBuffer >& playlistSpecific)
206+ Worker::Result Worker::processGames (Qx::Error& errorReport, QList<PlatformQueryResult >& primary, QList<PlatformQueryResult >& playlistSpecific)
217207{
218208 // Status tracking
219209 Result platformImportStatus;
@@ -222,16 +212,14 @@ Worker::Result Worker::processGames(Qx::Error& errorReport, QList<Fp::Db::QueryB
222212 qsizetype remainingPlatforms = primary.size () + playlistSpecific.size ();
223213
224214 // Use lambda to handle both lists due to major overlap
225- auto platformsHandler = [&remainingPlatforms, &errorReport, this ](QList<Fp::Db::QueryBuffer >& platformQueryResults, QString label) -> Result {
215+ auto platformsHandler = [&remainingPlatforms, &errorReport, this ](const QList<PlatformQueryResult >& platformQueryResults, QString label) -> Result {
226216 Result result;
227217
228- for (int i = 0 ; i < platformQueryResults. size (); i++ )
218+ for (const auto & pfQuery : platformQueryResults)
229219 {
230- Fp::Db::QueryBuffer& currentQueryResult = platformQueryResults[i];
231-
232220 // Open launcher platform doc
233221 std::unique_ptr<Lr::IPlatformDoc> currentPlatformDoc;
234- Lr::DocHandlingError platformReadError = mLauncherInstall ->checkoutPlatformDoc (currentPlatformDoc, currentQueryResult. source );
222+ Lr::DocHandlingError platformReadError = mLauncherInstall ->checkoutPlatformDoc (currentPlatformDoc, pfQuery. platform );
235223
236224 // Stop import if error occurred
237225 if (platformReadError.isValid ())
@@ -242,8 +230,8 @@ Worker::Result Worker::processGames(Qx::Error& errorReport, QList<Fp::Db::QueryB
242230 }
243231
244232 // ---Import games---------------------------------------
245- emit progressStepChanged (label.arg (currentQueryResult. source ));
246- if ((result = processPlatformGames (errorReport, currentPlatformDoc, currentQueryResult )) != Successful)
233+ emit progressStepChanged (label.arg (pfQuery. platform ));
234+ if ((result = processPlatformGames (errorReport, currentPlatformDoc, pfQuery )) != Successful)
247235 return result;
248236
249237 // ---Close out document----------------------------------
@@ -381,9 +369,9 @@ Worker::Result Worker::doImport(Qx::Error& errorReport)
381369 Fp::DbError queryError;
382370
383371 // Initial query buffers
384- QList<Fp::Db::QueryBuffer > gameQueries;
385- QList<Fp::Db::QueryBuffer > playlistSpecGameQueries;
386- Fp::Db::QueryBuffer addAppQuery;
372+ QList<PlatformQueryResult > gameQueries;
373+ QList<PlatformQueryResult > playlistSpecGameQueries;
374+ QList< Fp::AddApp> addAppQuery;
387375
388376 // Get flashpoint database
389377 Fp::Db* fpDatabase = mFlashpointInstall ->database ();
@@ -400,7 +388,7 @@ Worker::Result Worker::doImport(Qx::Error& errorReport)
400388 }
401389
402390 // Make initial game query
403- queryError = fpDatabase-> queryGamesByPlatform (gameQueries, mImportSelections .platforms , mOptionSet .inclusionOptions );
391+ queryError = loadGamesByPlatform (gameQueries, mImportSelections .platforms , mOptionSet .inclusionOptions );
404392 if (queryError.isValid ())
405393 {
406394 errorReport = queryError;
@@ -413,18 +401,21 @@ Worker::Result Worker::doImport(Qx::Error& errorReport)
413401 // Get playlist game ID list
414402 const QList<QUuid> targetPlaylistGameIds = getPlaylistSpecificGameIds (targetPlaylists);
415403
416- // Make unselected platforms list
417- QStringList availablePlatforms = fpDatabase->platformNames ();
418- QStringList unselectedPlatforms = QStringList (availablePlatforms);
419- for (const QString& selPlatform : std::as_const (mImportSelections .platforms ))
420- unselectedPlatforms.removeAll (selPlatform);
421-
422- // Make game query
423- queryError = fpDatabase->queryGamesByPlatform (playlistSpecGameQueries, unselectedPlatforms, mOptionSet .inclusionOptions , &targetPlaylistGameIds);
424- if (queryError.isValid ())
404+ if (!targetPlaylistGameIds.isEmpty ())
425405 {
426- errorReport = queryError;
427- return Failed;
406+ // Make unselected platforms list
407+ QStringList availablePlatforms = fpDatabase->platformNames ();
408+ QStringList unselectedPlatforms = QStringList (availablePlatforms);
409+ for (const QString& selPlatform : std::as_const (mImportSelections .platforms ))
410+ unselectedPlatforms.removeAll (selPlatform);
411+
412+ // Make game query
413+ queryError = loadGamesByPlatform (playlistSpecGameQueries, unselectedPlatforms, mOptionSet .inclusionOptions , targetPlaylistGameIds);
414+ if (queryError.isValid ())
415+ {
416+ errorReport = queryError;
417+ return Failed;
418+ }
428419 }
429420 }
430421
@@ -435,7 +426,7 @@ Worker::Result Worker::doImport(Qx::Error& errorReport)
435426 // Make initial add apps query
436427 if (!mOptionSet .excludeAddApps )
437428 {
438- queryError = fpDatabase->queryAllAddApps (addAppQuery);
429+ queryError = fpDatabase->getAllAddApps (addAppQuery);
439430 if (queryError.isValid ())
440431 {
441432 errorReport = queryError;
@@ -447,29 +438,29 @@ Worker::Result Worker::doImport(Qx::Error& errorReport)
447438 quint64 totalGameCount = 0 ;
448439
449440 QStringList playlistSpecPlatforms;
450- for (const Fp::Db::QueryBuffer & query : std::as_const (playlistSpecGameQueries))
451- playlistSpecPlatforms.append (query.source );
441+ for (const auto & query : std::as_const (playlistSpecGameQueries))
442+ playlistSpecPlatforms.append (query.platform );
452443 QStringList involvedPlatforms = mImportSelections .platforms + playlistSpecPlatforms;
453444
454445 // Additional App pre-load
455446 Qx::ProgressGroup* pgAddAppPreload = initializeProgressGroup (Pg::AddAppPreload, 2 );
456- pgAddAppPreload->setMaximum (addAppQuery.size );
447+ pgAddAppPreload->setMaximum (addAppQuery.size () );
457448
458449 // Initialize game query progress group since there will always be at least one game to import
459450 Qx::ProgressGroup* pgGameImport = initializeProgressGroup (Pg::GameImport, 2 );
460451
461452 // All games
462- for (const Fp::Db::QueryBuffer & query : std::as_const (gameQueries))
453+ for (const auto & query : std::as_const (gameQueries))
463454 {
464- pgGameImport->increaseMaximum (query.size );
465- totalGameCount += query.size ;
455+ pgGameImport->increaseMaximum (query.result . size () );
456+ totalGameCount += query.result . size () ;
466457 }
467458
468459 // All playlist specific games
469- for (const Fp::Db::QueryBuffer & query : std::as_const (playlistSpecGameQueries))
460+ for (const auto & query : std::as_const (playlistSpecGameQueries))
470461 {
471- pgGameImport->increaseMaximum (query.size );
472- totalGameCount += query.size ;
462+ pgGameImport->increaseMaximum (query.result . size () );
463+ totalGameCount += query.result . size () ;
473464 }
474465
475466 // TODO: Maybe move initial progress setup of image related tasks to ImageManager
0 commit comments