11using HtmlAgilityPack ;
2+ using Microsoft . Data . Sqlite ;
23using Newtonsoft . Json . Linq ;
4+ using System . Data ;
35using System . Diagnostics ;
6+ using System . DirectoryServices ;
47using System . IO ;
58using System . Text ;
69using System . Windows . Input ;
@@ -18,7 +21,6 @@ public partial class MiscViewModel : ObservableObject, INavigationAware
1821 private readonly ISnackbarService _snackbarService ;
1922 private TimeSpan _snackbarDuration = TimeSpan . FromSeconds ( 2 ) ;
2023 private Lazy < XboxRestAPI > _xboxRestAPI = new Lazy < XboxRestAPI > ( ( ) => new XboxRestAPI ( HomeViewModel . XAUTH ) ) ;
21- private Lazy < DBoxRestApi > _dboxRestApi = new Lazy < DBoxRestApi > ( ) ;
2224
2325
2426
@@ -197,87 +199,126 @@ public async Task Spoofing()
197199 #endregion
198200
199201 #region GameSearch
200- [ ObservableProperty ] private JObject _tSearchResponse = new JObject ( ) ;
202+ [ ObservableProperty ] private List < GameItem > _tSearchResults = new List < GameItem > ( ) ;
201203 [ ObservableProperty ] private List < string > _tSearchTitleNames = new List < string > ( ) ;
202204 [ ObservableProperty ] private string _tSearchText = "" ;
203- [ ObservableProperty ] private string _tSearchGameImage = "pack://application:,,,/Assets/cirno.png" ;
204205 [ ObservableProperty ] private string _tSearchGameName = "Name: " ;
205206 [ ObservableProperty ] private string _tSearchGameTitleID = "" ;
207+ [ ObservableProperty ] private string _tSearchGameTitleBased = "Title Based: Unknown" ;
208+
209+ private string GetDatabasePath ( )
210+ {
211+ return Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . MyDocuments ) , "XAU" , "TitleSearch" , "xbox_games.db" ) ;
212+ }
213+
206214 [ RelayCommand ]
207215 public async Task SearchGame ( )
208216 {
209217 try
210218 {
211- var response = await _dboxRestApi . Value . SearchAsync ( TSearchText ) ;
219+ if ( string . IsNullOrWhiteSpace ( TSearchText ) )
220+ {
221+ TSearchTitleNames = new List < string > ( ) ;
222+ TSearchResults = new List < GameItem > ( ) ;
223+ return ;
224+ }
225+
226+ string dbPath = GetDatabasePath ( ) ;
212227
213- var items = response [ "items" ] as JArray ;
214- if ( items == null || items . Count == 0 )
228+ if ( ! File . Exists ( dbPath ) )
215229 {
216- _snackbarService . Show ( "Error" , $ "No results were found for { TSearchText } ",
230+ _snackbarService . Show ( "Error" , "Game database not found. Please wait for it to download. ",
217231 ControlAppearance . Danger ,
218232 new SymbolIcon ( SymbolRegular . ErrorCircle24 ) , _snackbarDuration ) ;
219- TSearchTitleNames = new List < string > ( ) ;
220- TSearchResponse = response ;
221233 return ;
222234 }
223235
224- // Sort items alphabetically by "name"
225- var sortedItems = new JArray ( items
226- . OrderBy ( item => item [ "name" ] ? . ToString ( ) ?? string . Empty , StringComparer . OrdinalIgnoreCase ) ) ;
236+ var results = await Task . Run ( ( ) => SearchGamesInDatabase ( dbPath , TSearchText ) ) ;
227237
228- // Update TSearchResponse with sorted items
229- response [ "items" ] = sortedItems ;
230- TSearchResponse = response ;
238+ if ( ! results . Any ( ) )
239+ {
240+ _snackbarService . Show ( "Error" , $ "No results were found for '{ TSearchText } '",
241+ ControlAppearance . Danger ,
242+ new SymbolIcon ( SymbolRegular . ErrorCircle24 ) , _snackbarDuration ) ;
243+ TSearchTitleNames = new List < string > ( ) ;
244+ TSearchResults = new List < GameItem > ( ) ;
245+ return ;
246+ }
231247
232- // Create TSearchTitleNames list from sorted items
233- TSearchTitleNames = sortedItems
234- . Select ( item => item [ "name" ] ? . ToString ( ) ?? string . Empty )
235- . Where ( name => ! string . IsNullOrWhiteSpace ( name ) )
236- . ToList ( ) ;
248+ results = results . OrderBy ( game => game . Title , StringComparer . OrdinalIgnoreCase ) . ToList ( ) ;
249+
250+ TSearchResults = results ;
251+ TSearchTitleNames = results . Select ( game => game . Title ) . ToList ( ) ;
237252 }
238- catch
253+ catch ( Exception ex )
239254 {
240- _snackbarService . Show ( "Error" , $ "No results were found for { TSearchText } ",
255+ _snackbarService . Show ( "Error" , $ "Search failed: { ex . Message } ",
241256 ControlAppearance . Danger ,
242257 new SymbolIcon ( SymbolRegular . ErrorCircle24 ) , _snackbarDuration ) ;
243258 TSearchTitleNames = new List < string > ( ) ;
244- TSearchResponse = new JObject ( ) ;
259+ TSearchResults = new List < GameItem > ( ) ;
245260 }
246261 }
247262
263+ private List < GameItem > SearchGamesInDatabase ( string dbPath , string searchText )
264+ {
265+ var results = new List < GameItem > ( ) ;
266+
267+ using var connection = new SqliteConnection ( $ "Data Source={ dbPath } ") ;
268+ connection . Open ( ) ;
269+
270+ // Search for games that contain the search text (case-insensitive)
271+ string sql = @"
272+ SELECT title, titleId, isTitleBased
273+ FROM games
274+ WHERE title LIKE @searchText
275+ ORDER BY title COLLATE NOCASE
276+ LIMIT 100" ;
277+
278+ using var command = new SqliteCommand ( sql , connection ) ;
279+ command . Parameters . AddWithValue ( "@searchText" , $ "%{ searchText } %") ;
280+
281+ using var reader = command . ExecuteReader ( ) ;
282+ while ( reader . Read ( ) )
283+ {
284+ results . Add ( new GameItem
285+ {
286+ Title = reader . GetString ( "title" ) ,
287+ TitleId = reader . GetString ( "titleId" ) ,
288+ IsTitleBased = reader . GetInt32 ( "isTitleBased" ) == 1
289+ } ) ;
290+ }
291+
292+ return results ;
293+ }
294+
248295 public void DisplayGameInfo ( int index )
249296 {
250297 try
251298 {
252- var items = TSearchResponse [ "items" ] as JArray ;
253- if ( items == null || items . Count <= index )
299+ if ( TSearchResults == null || TSearchResults . Count <= index || index < 0 )
254300 {
255- _snackbarService . Show ( "Error" , "No game found at the selected index." , ControlAppearance . Danger , new SymbolIcon ( SymbolRegular . ErrorCircle24 ) , _snackbarDuration ) ;
301+ _snackbarService . Show ( "Error" , "No game found at the selected index." ,
302+ ControlAppearance . Danger ,
303+ new SymbolIcon ( SymbolRegular . ErrorCircle24 ) , _snackbarDuration ) ;
256304 return ;
257305 }
258306
259- var item = items [ index ] ;
260- var titleIdHex = item [ "title_id" ] ? . ToString ( ) ?? "0" ;
261- var titleIdBase10 = long . TryParse ( titleIdHex , System . Globalization . NumberStyles . HexNumber , null , out var base10Id )
262- ? base10Id . ToString ( )
263- : "-1" ;
307+ var selectedGame = TSearchResults [ index ] ;
264308
265- TSearchGameName = "Name: " + ( item [ "name" ] ? . ToString ( ) ?? "Unknown" ) ;
266- TSearchGameTitleID = titleIdBase10 ;
309+ TSearchGameName = selectedGame . Title ;
310+ TSearchGameTitleID = selectedGame . TitleId ;
311+ TSearchGameTitleBased = $ "Title Based: { ( selectedGame . IsTitleBased ? "True" : "False" ) } ";
267312
268- if ( titleIdBase10 == "-1" )
269- {
270- _snackbarService . Show ( "Error: TitleID not found" ,
271- $ "The TitleID for { TSearchGameName } was not available or invalid.",
272- ControlAppearance . Danger ,
273- new SymbolIcon ( SymbolRegular . ErrorCircle24 ) , _snackbarDuration ) ;
274- }
275313 }
276- catch
314+ catch ( Exception ex )
277315 {
278- _snackbarService . Show ( "Error" , "Failed to display game info." , ControlAppearance . Danger , new SymbolIcon ( SymbolRegular . ErrorCircle24 ) , _snackbarDuration ) ;
316+ _snackbarService . Show ( "Error" , "Failed to display game info." ,
317+ ControlAppearance . Danger ,
318+ new SymbolIcon ( SymbolRegular . ErrorCircle24 ) , _snackbarDuration ) ;
279319 }
280320 }
321+
281322 #endregion
282323
283324 #region GamertagSearch
0 commit comments