Compare commits

...

620 Commits

Author SHA1 Message Date
Fenopy
15efd91769 Merge pull request #300 from VRPirates/beta/RSL-3.0.1
Beta/rsl 3.0.1
2026-03-18 12:58:00 -05:00
fenopy
28fe132012 fix: better handling when no config files are available 2026-03-18 12:55:26 -05:00
Fenopy
db93c7b610 Merge pull request #292 from jp64k/RSL-3.0.5
Bunch of changes, look inside
2026-02-06 06:22:15 -06:00
jp64k
3bae614d7e Fixed another regression (release notes placeholder not being centered on launch) 2026-02-04 04:35:27 +01:00
jp64k
3e3a452ceb Updated changelog.txt 2026-02-04 04:11:48 +01:00
jp64k
4dc11191f0 Implemented async real-time progress updates for drag and drop. Removed deprecated Sideload(...) and CopyOBB(...) functions
Replaced the old sideload/OBB operations with our newer async, progress-aware flows to enable real-time progress feedback in the UI for non-blocking drag and drop operations. Also cleaned up a bit code and made minor logging adjustments. Updated "No Trailer found" message to disappear after 5 seconds
2026-02-04 03:47:28 +01:00
jp64k
3e626c6390 Refined backup messages and updated backup button labels
Changed button texts for clarity: 'BACKUP WITH ADB' to 'BACKUP GAMESAVE WITH ADB', 'BACKUP GAMESAVES' to 'BACKUP ALL GAMESAVES'. Updated dialog and message box titles to match. Added explanatory notes to the bulk backup summary, guiding users to use ADB backup for games requiring special permissions
2026-02-02 04:04:13 +01:00
jp64k
5b44295418 Improved Rookie connection status feedback and fixed typo
Updated the sideloading status label and download button to provide clearer feedback when no device is connected or sideloading is disabled. The download button now shows "DOWNLOAD AND INSTALL" only when sideloading is enabled and a device is connected, otherwise it shows "DOWNLOAD". Meanwhile the sideloading status label now shows "Sideloading: No Device Connected" in orange while no device is connected. Also added missing </Setting> tag in Settings.settings
2026-02-02 03:51:39 +01:00
jp64k
95b9059bed ListView: Show uninstall button on hover
Changed uninstall button logic to display on mouse hover over a ListView item instead of on selection (click) to match gallery view behavior
2026-02-02 02:48:21 +01:00
jp64k
163a0d9fce Bumped version to 3.0.1 and updated changelogs
Increased the version to 3.0.1. New changelog reflects fixes for popularity ranking, favorites updating, improved YouTube trailer matching, automatic public config creation, and new grouped tile logic in Gallery View
2026-01-30 04:49:56 +01:00
jp64k
af84f2cf8c Improvd base game name derivation for grouped tiles
Refactored the logic to strip parentheses from all version names before selecting the shortest, instead of selecting the shortest name first and then stripping
2026-01-30 04:40:59 +01:00
jp64k
9f2e824df2 Gallery View: Added logic to remove unfavorited subitems of grouped tiles in favorites-view
In gallery view, when a game version of a grouped tile is unfavorited in favorites-view, it is now
automatically removed from the display. Added RemoveVersionFromDisplay
method to handle item removal, tile updates and layout
recalculation. The grouped-view is automatically closed
when only one version remains after unfavoriting
2026-01-30 04:33:51 +01:00
jp64k
f21efcd476 Fixed group titles to show actual name when only one version is favorited
Updated the version name logic in gallery view to display the actual game name instead of the simplified base name when only one version of a grouped game is present in favorites
2026-01-30 04:17:38 +01:00
jp64k
50b52d963b Fixed favorites not refreshing when removing entries
Fixed favorites not refreshing when removing entries while in favorites view. Previously, removed items would only disappear after switching to regular view and back. The favorites list now updates immediately upon removal
2026-01-30 04:07:30 +01:00
jp64k
61e6c143fa Fixed regression from earlier commit (f91ee27)
Corrected the index used to parse the size in MB from index 6 to 5
2026-01-30 03:55:35 +01:00
jp64k
60d68cbc8c Improved YouTube trailer matching accuracy
Improved the trailer selection algorithm by requiring all game name words (normalized by removing apostrophes) to be present in the video title. Increased the number of processed matches from 5 to 10 and refined scoring bonuses and penalties, enhancing the accuracy for selecting the most relevant clip
2026-01-30 03:50:24 +01:00
jp64k
93ed359ed1 Auto-create public config file without prompt
Removed the user prompt for creating the public config file when missing. The file is now created automatically when AutoUpdateConfig is enabled, streamlining the process similar to how Rookie handles other required files
2026-01-30 03:13:13 +01:00
jp64k
8bd7efc22e Gallery View: Added grouped tile logic for packages with multiple versions
Implemented grouping of gallery tiles by package with a cleaned base name and an overlay UI for selection among multiple versions. Added new rendering logic, sorting and interaction for grouped tiles, including badges, a scrollable overlay, favorite context-menu support and release-notes updates on hover. Refactored a bit of code in GalleryView.
2026-01-29 18:47:08 +01:00
fenopy
07957d93fe Merge branch 'master' of https://github.com/VRPirates/rookie 2026-01-28 06:21:45 -06:00
jp64k
f91ee27c35 Fixed culture-specific decimal separator problem
Replaced all direct double.TryParse calls with new StringUtilities.TryParseDouble helper method to ensure consistent, culture-invariant parsing of numeric values
2026-01-24 16:57:34 +01:00
Fenopy
d8cc5dc539 Merge pull request #283 from jp64k/RSL-3.0.4
Improved YouTube trailer search accuracy, added TryDeleteDirectory utility for all directory deletions, moved WebView2 cache from hardcoded path to app directory, replaced dated folder dialogs with modern FolderSelectDialog, improved backup/restore logic
2026-01-08 06:06:21 -06:00
jp64k
ed17322983 Refactored and fixed backup directory handling, refactored and fixed backup/restore logic and improved UX
Centralized backup directory logic via SettingsManager.GetEffectiveBackupDir(), replacing repeated path construction throughout the codebase. Fixed backup and restore logic aborting on error, streamlined backup/restore workflows for both individual apps and bulk operations, improved UI text for clarity, improved error handling and improved progress feedback. Removed redundant code and ensured consistent backup directory usage in MainForm, Sideloader, and SettingsForm
2026-01-08 01:46:11 +01:00
jp64k
0970b73fe7 Replaced dated download/backup directory set dialogs with modern FolderSelectDialog
Updated download and backup directory selection to use modern FolderSelectDialog for better folder browsing with initial directories based on user settings, improving UX
2026-01-07 20:32:26 +01:00
jp64k
7cc437309a Moved hardcoded WebView2 path from "C:\RSL" to the application's current directory
Changed the WebView2 cache directory from system root path "C:\RSL" to the application's current directory for improved portability and reliability. Ensured the cache directory is created before initializing the WebView2 environment
2026-01-07 20:16:44 +01:00
jp64k
a050d82c8b Refactored all directory deletion calls to use new TryDeleteDirectory utility
Replaced all direct Directory.Delete calls by our new custom FileSystemUtilities.TryDeleteDirectory class and method for safer and more robust directory deletion throughout the codebase with automatic retries (3x 150ms), recursive deletion, handling of read-only attributes, rename-then-delete fallback, and better error handling. Also moved the WebView cleanup to happen earlier on initialization, before creation of the WebView environment
2026-01-07 19:46:35 +01:00
jp64k
4c5a651b2b Removed deprecated multi-strategy approach 2026-01-07 17:39:28 +01:00
jp64k
af17f5bb12 Improved YouTube trailer search accuracy
Improved YouTube trailer search algorithm by cleaning game names, having 2 search strategies for higher hit accuracy, and adding a scoring system that selects the most relevant clip (trailer) based on title similarity and result position (respecting YouTube's sort order)
2026-01-07 03:55:52 +01:00
Fenopy
670ace1963 Merge pull request #282 from jp64k/RSL-3.0.4
Feat: Rookie now flashes in taskbar when FlexibleMessageBox is thrown and window is inactive, Fix: FlexibleMessageBox positioning for inactive/minimized window
2026-01-06 12:03:22 -06:00
jp64k
77cfb81545 Feat: Rookie now flashes in taskbar when FlexibleMessageBox is thrown and window is inactive, Fix: FlexibleMessageBox positioning for inactive/minimized window
Added taskbar flashing to notify users when a task is complete or requires user input (= when FlexibleMessageBox is thrown) while the application is inactive or minimized. Enhanced FlexibleMessageBox to better handle when the application is minimized by centering the dialog on the screen and repositioning it when the form is restored.
2026-01-06 17:51:36 +01:00
fenopy
3d241cc634 fix: issue with selecting the default mirror 2026-01-06 08:50:34 -06:00
Fenopy
187f1d3689 Merge pull request #279 from jp64k/RSL-3.0-2
Fixes, QoL and new features... Enhanced DNS/ADB handling, fixed gallery selection, fixed newer than list/blacklist logic, synchronized gallery/list view sorting, modernized download queue UI, added session persistence
2026-01-05 06:53:11 -06:00
jp64k
6ccaf06ab4 Fixed broken shortcuts (CTRL+P, CTRL+L, ALT+L)
Fixed broken shortcuts (CTRL+P behavior was already broken on 2.34.1), updated to use FlexibleMessageBox and changed CTRL+P logic to immediately copy the selected game's package name without confirmation, supporting both list and gallery views, with a neater notification
2026-01-03 05:41:01 +01:00
jp64k
f289026438 Added game name to disk/device space error messages
Updated disk and device space error messages to show the game name
2026-01-03 05:14:32 +01:00
jp64k
de6b0e4c70 Added download queue space checks with new error messages
Implemented disk and device space validation for the download/install queue with per-game and total queue size checks. The UI displays total queue size and updates available device space after each install. Improved error handling and user prompts for insufficient space.

When adding an entry to the queue, we check:
- Free disk space in the current download directory
- Device free space (if sideloading is enabled)
- Whether the download is already queued
- Total queue size against download directory and device free space to prevent over-queuing

When a download starts (e.g., when resuming downloads after a restart), we recheck disk and device space for that download and allow the user to skip it or clear the queue if not.

Space checks are intentionally lenient. We only block downloads when we're confident the download won't fit. If space is close, users will proceed since they'll receive an error during the actual installation when it fails anyway.
2026-01-03 05:05:43 +01:00
jp64k
debb204719 Improved YouTube search query accuracy
Wrapped the game name in quotes in the YouTube search query to yield more accurate results for VR trailers
2026-01-02 16:09:45 +01:00
jp64k
f80d5f841c Removed dated drag-and-drop label, updated notes display
Removed DragDropLbl label and its related logic, consolidating drag-and-drop tips into the notesRichTextBox with improved styling. Added UpdateReleaseNotes method to handle dynamic notes display and placeholer styling
2026-01-02 15:43:33 +01:00
jp64k
38a1968c42 Refactored gallery view tile rendering for better performance and increased hover effect animation speed
Simplified and unified tile and thumbnail drawing, reduced gallery image filter quality (interpolation) for better performance with negligible visual quality loss while maintaining the best filter quality (HighQualityBicubic) for the currently hovered tile, and adjusted hover and animation constants. Tile borders are now drawn after the image. Removed no longer used FormatSize method
2026-01-02 15:20:30 +01:00
jp64k
b92def0283 Added persistent download queue with resume support
Implemented automatic saving and loading of the download queue, allowing users to resume unfinished downloads after restarting the application. Added methods to persist the queue state, prompting users to ask if they want to resume queued downloads on startup, and updated queue management logic to support resumed downloads
2025-12-31 05:37:47 +01:00
jp64k
62343f8cf6 Implemented minor Copilot changes 2025-12-31 05:11:58 +01:00
jp64k
fdb091cee6 Persist window size, position, state and sort order between sessions
Added functionality to automatically save and restore the main window's position, size, (maximized) state, and sort order for list and gallery views between sessions. Introduced new properties in SettingsManager for window and sort state persistence. Updated MainForm to load and save these settings on startup, close, and sort changes, and added default/fallback behavior. Modified ModernListView to suppress header drawing and interaction during initialization
2025-12-31 04:54:11 +01:00
jp64k
3993e574a8 Replaced dated download queue ListBox with custom ModernQueuePanel
Introduced ModernQueuePanel as a modern control for the download queue, supporting
drag-and-drop reordering, auto-scroll, and entry removal via close buttons (replaced
double-click) in a modernized look. Removed legacy ListBox event handlers and updated
MainForm to use the new panel, synchronizing it with the queue's binding list and
refactored UI logic for queue management and download cancellation
2025-12-31 03:44:46 +01:00
jp64k
fb36826091 Fixed incorrect NEWER THAN LIST logic/behavior for blacklisted apps
Moved increment of newerThanListCount and setting of item.ForeColor outside the blacklist check. Now, only the prompt for upload is skipped for blacklisted packages, while the UI still reflects that a newer version is installed
2025-12-30 22:24:14 +01:00
jp64k
84ddce1423 Synchronized sorting states between list view and gallery view
Introduced shared sorting state logic to maintain consistent sorting order when switching between gallery and list views (game name, last updated, size, popularity, asc and desc)
2025-12-30 22:12:29 +01:00
jp64k
f363550351 Fixed incorrect item selection in gallery view for entries with duplicate package names
Updated GalleryView selection logic to consistently use unique release name (SubItems[ReleaseNameIndex]) instead of non-unique package name, which fixes incorrect item selections in gallery view for entries with duplicate package names
2025-12-30 21:50:47 +01:00
jp64k
02ab0f0e2a Improved timeout handling for ADB connect command and suppressed devices command log spam
Enhanced RunAdbCommandToString to use a 5-second timeout for connect commands, preventing prolonged blocking during startup/initialization when an ADB wireless config exists but fails to connect to the device. Also added a suppressLogging parameter to control 'devices' command output logging, which is used for our quick 'devices' check command that runs every second while no devices are connected
2025-12-30 17:23:00 +01:00
jp64k
5819bc8083 Refactored DnsHelper and added DNS testing for the public config hostname instead of a hardcoded URL
Refactored DnsHelper for maintainability and added automatic DNS testing for the public config hostname after creation/update of vrp-public.json to automatically enable fallback DNS and proxy if system DNS fails for that
2025-12-30 16:54:00 +01:00
fenopy
311f0cfb7e feat: save selected mirror 2025-12-23 09:26:35 -06:00
Fenopy
58cb75c38c Merge pull request #276 from jp64k/RSL-3.0-2
Several changes and fixes, see notes below
2025-12-23 07:07:17 -06:00
Fenopy
4383b9d398 store deviceId in variable instead of line-splitting
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-23 07:06:33 -06:00
jp64k
b3ce3ab214 Adjusted wireless ADB dialog widths to fix layout
Increased width of 'Wireless ADB Options' and 'Wireless ADB Connection' dialogs by 6 pixels each to improve layout centering
2025-12-22 03:47:53 +01:00
jp64k
ae72432aee Improved FlexibleMessageBox button and text placement
Reduced left padding for the title and text labels, reduced right padding of buttons for better alignment, added code to resize the title panel to fix the close button position - https://puu.sh/KFOEG/5c3d72aaee.png
2025-12-22 03:43:48 +01:00
jp64k
5a939d6234 Refactored ADBWirelessToggle and device detection
Replaced message box prompts with custom dialogs for wireless ADB options and connection methods, providing clearer choices for users. Enhanced wireless ADB state detection by verifying actual device connection, and readded old automatic USB setup option as third option for wireless ADB ("Automatic (USB)"). Also refactored CheckForDevice() to use Task.Run/await pattern and optimized battery check
2025-12-22 02:42:34 +01:00
jp64k
e6ce947700 Refactored filter/selection logic, now preserves filter and selection state on game list refresh
Refactored methods to use RefreshGameListAsync() instead of reinitializing the list view, ensuring filter and selection state are preserved after refreshes. Added logic to restore the last selected item by package name in both list and gallery views. Also refactored ModernListView to simplify marquee calculation logic.
2025-12-22 01:25:50 +01:00
jp64k
d24df061df Minor ModernListView.cs cleanup and fixed hovered text disappearing after scroll
Removed MarqueeEnabled and MarqueeOnlyWhenFocused fields and related conditional logic, as they were no longer used. Also fixed hover state updates after scrolling
2025-12-22 00:24:23 +01:00
Fenopy
1b06ab7981 Merge pull request #274 from jp64k/RSL-3.0-2
Implemented custom ModernListView class, added modern scrollbar to gallery view, reworked list view columns and sorting, added fix to skip 0 MB entries when MR-Fix version exists
2025-12-18 11:29:02 -06:00
jp64k
5f4cfc09fe Fixed "&" not being rendered in new list view
Included TextFormatFlags.NoPrefix in text rendering flags for both header and cell drawing to fix & rendering.
2025-12-18 16:11:37 +01:00
jp64k
1de339da75 Updated ModernListView.cs
Changed several public properties and enums in ModernListView to private, removed unused code, reduced text scroll speed (marquee)
2025-12-18 06:09:50 +01:00
jp64k
4f653f2131 Implemented Copilot suggestions 2025-12-18 05:53:52 +01:00
jp64k
3148ddcfa3 Implemented custom ModernListView class, added modern scrollbar to gallery view, reworked list view columns and sorting, added fix to skip 0 MB entries when MR-Fix version exists
Added custom ModernListView class with modern dark theme appearance and refined behavior with smooth text scrolling support. Required a lot of finicking to get the details right. Reworked ListView columns and sorting for size and popularity, including parsing with reformatted GB/MB size and popularity ranking. Updated GalleryView to support new formats and implemented modern scrollbars. Also added logic to skip 0 MB entries when an MR-Fix version of same game exists
2025-12-18 05:44:54 +01:00
fenopy
b793d2a140 bump rclone to 1.72.1 2025-12-17 15:36:55 -06:00
Fenopy
5f16ad13e2 Merge pull request #273 from jp64k/RSL-3.0-2
Fixed update sorting for gallery view to consider exact date+time (and not just date)
2025-12-17 15:20:01 -06:00
Fenopy
ddb503feec Update to handle UTC timestamps
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-17 15:19:44 -06:00
jp64k
1bcfbd132d Updated icon in forms I missed earlier; now reusing existing icon; dropping .exe size from 3,38 MB to 1,03 MB
Updated icon in forms I missed earlier; now reusing existing icon; dropping .exe size from 3,38 MB to 1,03 MB
2025-12-17 22:18:19 +01:00
jp64k
9862a6a8ca Fixed update sorting for gallery view to consider exact date+time (and not just date)
Updated ParseDate to use DateTime.TryParseExact with a specific format handling ' UTC' suffix
2025-12-17 19:43:39 +01:00
Fenopy
9ef3c9264e Merge pull request #272 from jp64k/RSL-3.0-2
Fixed UI lag during progress updates, unified DLS+ETA progress labels and implemented ETA tracking for extraction, installation, and copy operations
2025-12-17 06:11:27 -06:00
jp64k
12c371da84 Fixed gallery view placeholder text alignment for missing thumbnails
Adjusted the rectangle used for placeholder text when a thumbnail is missing to use a stable, non-scaled base thumbnail size to ensure consistent text layout regardless of thumbnail scaling
2025-12-17 05:13:10 +01:00
jp64k
782ead1c1e Remove InstalledVersion column, now showing it unified in single version tab
Removed the InstalledVersion column from the ListView and its designer references. Installed version information is now appended to the Version column text "Version (vs Installed)" when installed, improving clarity and reducing column clutter.
2025-12-17 05:07:08 +01:00
jp64k
e9f77449f0 Added 'Installed Version' column to games list
Introduces a 'Installed' column to the games ListView, displaying the installed version for each game. Updates sorting logic to handle the new column numerically and defines the corresponding index in RCLONE.cs.
2025-12-17 04:51:57 +01:00
jp64k
fd77c4db8b Reworked tile design in game gallery
For a cleaner UI and to allow larger thumbnails, game names are now shown on hover, except for tiles that don't have thumbnails. Adjusted thumbnail padding and corner radius to further increase thumbnail size and keep rounding consistent. Refined positioning of the delete button to align with the new tile layout
2025-12-17 04:18:36 +01:00
jp64k
d9a8d1c460 Fixed trailer player regression
For some reason this fixes trailers sometimes not playing correctly
2025-12-17 02:53:57 +01:00
jp64k
c5b151471a Refactored progress bar to show fractional percentages and improved ETA smoothing
Progress values and callbacks (labels, progressbar) now use float instead of int to reflect fractional percentages for a better / more responsive user experience. Improved ETA display for APK install, OBB copy and ZIP extraction operations by introducing a reusable EtaEstimator class for smoother and more accurate ETA calculations
2025-12-17 02:20:40 +01:00
jp64k
2f843bc458 Refactored game list refresh logic to use existing RefreshGameListAsync() method
Replaced manual list view initialization and checks with a call to the existing RefreshGameListAsync() method, simplifying the refresh logic and ensuring that the game list is properly refreshed when a filter is active and a game is updated (e.g., when installing an already installed game (updating) while in 'update available' view, the game status now correctly changes to reflect that it has been updated)
2025-12-17 00:31:25 +01:00
jp64k
f528520024 Simplified extraction and installation progress labels in inner progress bar
- Removed extraction filenames from progress status label in inner progress bar to keep it clean and simple, and to avoid confusion with OBB file copy operations
- Removed redundant ETA text for install progress in inner progress bar to match the style of other progress labels
- Changed the trailer player.html directory from 'webroot' to 'trailer', shortened YouTube player initialization script, added fs: 0 to hide unnecessary fullscreen toggle, iv_load_policy: 3 to hide video annotations
2025-12-17 00:05:03 +01:00
jp64k
a92d4c0267 Unified DLS+ETA progress labels and implemented ETA tracking for extraction, installation, and copy operations
- Removed separate DLS+ETA labels, unified into a clearer single label
- Repositioned and resized that label slightly to avoid top of label getting cut off
- Added guards to prevent brief progress bar flashes during multi-file downloads
- Added ETA for file extraction, APK installation, and OBB copy operations by tracking elapsed time and calculating a smoothed ETA based on the rate of progress
2025-12-16 22:50:55 +01:00
jp64k
acaea1d243 Fixed UI lag during progress updates
Added throttling to progress and status callbacks during APK installation and OBB copy operations. UI updates are now limited to at most once every 100 ms or when the progress percentage changes, preventing excessive UI thread updates and associated lag.
2025-12-16 21:10:53 +01:00
Fenopy
c48043a178 Merge pull request #271 from jp64k/RSL-3.0-2
Added efficient automatic Cloudflare DNS fallback with local proxy for RCLONE, fixed several error messages being opened behind mainwindow, refactored ShowError_QuotaExceeded() logic, fixed proxy settings parsing, fixed broken config preventing startup, fixed ampersands (&) not being rendered in selectedGameLabel and rookieStatusLabel
2025-12-15 06:15:31 -06:00
Fenopy
15f0c1ee72 chore: update proxy handling order
changed setRcloneProxy to default to user's proxy first.
2025-12-15 06:14:03 -06:00
jp64k
44df1666f4 Fixed ampersands (&) not being rendered in selectedGameLabel and rookieStatusLabel 2025-12-15 02:42:43 +01:00
jp64k
6cbfdbe52c Refactored file download logic to use DNS fallback for 7-zip and WebView2 downloads, fixed crash craused by corrupted user.config preventing startup
Refactored all file download logic to use DNS fallback and applied it to 7-zip and WebView2 runtime downloads. Moved WebView2 runtime download logic to GetDependencies and ensures it is downloaded at startup if missing. Added robust handling for corrupted user.config files in Program.cs, including auto-repair and fallback guidance.
2025-12-15 00:07:54 +01:00
jp64k
75d22ab504 Parse proxy settings only when proxy is enabled
Updated the applyButton_Click handler to parse and validate proxy address and port only if the proxy toggle is checked. This prevents unnecessary validation and error messages when the proxy is not enabled
2025-12-14 20:45:13 +01:00
jp64k
0c20841db3 Added efficient Cloudflare DNS fallback with local proxy for RCLONE
Introduced DnsHelper to detect system DNS failures and fall back to Cloudflare (1.1.1.1 / 1.0.0.1) DNS resolving, with caching and helpers for downloads. When fallback is required a lightweight local proxy is started and HTTP_PROXY/HTTPS_PROXY are set for spawned rclone processes so rclone uses the proxy’s DNS resolution; the proxy is cleaned up on exit. This finally resolves the very common ISP DNS blockage issues of users.
2025-12-14 20:25:48 +01:00
jp64k
b33251d98b Moved ShowError_QuotaExceeded() outside of try-catch, updated message, close application after showing the error
Moved quota exceeded check outside of try-catch block so it always runs the check. Updated the error message in ShowError_QuotaExceeded(). The application now exits after showing the error.
2025-12-14 18:45:21 +01:00
jp64k
3ef0652a85 Fixed several error messages being opened behind mainwindow
Updated all FlexibleMessageBox.Show invocations to include Program.form as the parent form. This ensures message boxes are properly parented to the main application window, putting them infront of the main window, instead of behind it.
2025-12-14 16:54:35 +01:00
fenopy
2ced3aa961 Support for HTTP proxy for Rclone (sourced from pr 252) 2025-12-12 08:19:14 -06:00
Fenopy
0ecb4255e9 Merge pull request #269 from jp64k/RSL-3.0
Added modern progress bar, real-time install/OBB progress updates using AdvancedSharpAdbClient, updated sideload confirmation messages, a new icon based on the VRP server icon, prompt to ask if users want to delete game files after install when enabling sideloading
2025-12-11 17:50:07 -06:00
jp64k
f8dea1e135 Added modern progress bar, real-time install/OBB progress updates using AdvancedSharpAdbClient, updated sideload confirmation messages, and a new icon based on the VRP server icon.
Introduce ModernProgressBar, a custom control with gradient fill, rounded corners, indeterminate animation, and optional status text. Integrated AdvancedSharpAdbClient to provide real-time progress updates for APK installs and OBB copies, updating the UI to reflect the current operation and its progress. Refactored ADB methods to support async progress reporting, updated MainForm to use the new progress bar features, improved user feedback with clearer confirmation messages during and after sideloading, added a new icon based on the VRP server icon and enabled it in the window title bar.
2025-12-11 23:06:49 +01:00
jp64k
f714d2cb92 Added prompt to ask if users want to delete game files after install when enabling sideloading
Added a dialog to ask users if they want to delete game files after install when enabling sideloading from the toggle from the left side navigation. Also moved UpdateStatusLabels() to ensure sideload status labels are already updated as they see the prompt.
2025-12-11 16:01:17 +01:00
fenopy
f3a93afccc qol: add github link to about 2025-12-11 08:48:09 -06:00
fenopy
123fb7261c feat: add firmware version to Device Model Label 2025-12-11 08:33:41 -06:00
Fenopy
79225fe21d Merge pull request #268 from jp64k/RSL-3.0
Updated rookie icon, reduced file size, improved wireless ADB setup, added rookie, device ID, mirror and sideloading status labels, fixed UI-thread blocking regression in ProcessNewApps, fixed Game Gallery install status regression
2025-12-11 08:32:08 -06:00
jp64k
062d702877 Improve wireless ADB setup, added rookie, device ID, mirror and sideloading status labels, fixed a UI-thread blocking regression in ProcessNewApps...
Reworked Wireless ADB options: Manual IP address field now prefills the first 3 octets by getting the local IPv4 of the system. Automatic no longer requires USB connection to establish a connection, instead now performs a network scan to find the device and connects to it. Included multi-device selection support (untested - I only have a single device.) Nonetheless Wireless ADB still requires a one-time USB setup for the ADB 'tcpip 5555' command (I wasn't able to ever establish a connection without that in Rookie, neither here nor in 2.34). Updated ADB button label to match new logic. Updated ADB messages to provide more guidance for the user. Synced sideloading button text to correctly match the sideloading status. Added rookie application status updates, device ID, mirror and sideloading status labels in bottom left corner. Fixed ProcessNewApps regression (no longer blocks UI thread). Replaced Program.form.changeTitle calls with local changeTitle for consistency. Fixed Game Gallery application install status not updating after installation.
2025-12-11 03:47:32 +01:00
jp64k
e9e8c1298e Updated rookie icon and reduced file size from 400 KB to 32 KB
Updated icon to match new accent color and reduced file size by 368 KB (400 KB -> 32 KB) by removing unnecessarily large resolutions; sticking to appropriate resolutions (16x16, 32x32, 48x48, 64x64), effectively decreasing the application size from 3,57 MB to 3,20 MB
2025-12-10 03:50:45 +01:00
Fenopy
9b6104c350 Merge pull request #267 from jp64k/beta/RSL-2.35-yt
RSL 3.0 - Massive Rookie overhaul. UI, UX/QoL, performance, reliability, gallery view, see full notes below...
2025-12-09 16:19:05 -06:00
jp64k
3db36a16c7 Added automatic device detection
Added a 1-second timer to detect when a device connects and trigger a full UI/data refresh, resetting flags and gallery data sources so install statuses stay accurate
2025-12-09 05:21:36 +01:00
jp64k
b06ee70c38 Added a newline to the "game already exists" message for clearer prompts 2025-12-09 04:57:35 +01:00
jp64k
769358b999 Fixed init when no device is connected and refined download-only flow
Fixed 'no device connected' error preventing application initialization. Improved download-only mode logic to preserve downloaded files until successful installation. Clarified user prompts and updated UI titles
2025-12-09 04:55:55 +01:00
jp64k
66058b3134 Refactored favorite context menu logic, removed early return from favorite filter; now shows empty list
Refactored favorite context menu logic to eliminate code duplication and improve clarity. Removed early return from favorite filter to intentionally display empty list when no favorites exist
2025-12-09 03:13:58 +01:00
jp64k
a65b369836 Refactor uninstall logic, fixed active filter resetting after uninstalling, and list to gallery switch now jumps to selected title
Added FastGalleryPanel.ScrollToPackage to center the previously selected package in gallery view when switching from list view to gallery view. Refactored uninstall logic to have list and gallery views both use a single uninstall method, and added RefreshGameListAsync to update UI and remember/apply active filters after uninstall.
2025-12-09 02:56:54 +01:00
jp64k
5b16ec4ef7 Fixed 'NEWER THAN LIST' resizing, added search box resizing
Search box now resizes correctly with proper anchoring and appropriate minimum size constraints, preventing 'NEWER THAN LIST' getting crushed on resize.
2025-12-09 02:00:42 +01:00
jp64k
3e950833f9 Redesigned SettingsForm to use less vertical space, refined UI text
Redesigned SettingsForm layout to reduce its vertical height. Reorganized and repositioned controls, adjusted sizes, and refined UI text throughout
2025-12-09 01:00:36 +01:00
jp64k
4c333453b4 Fixed flickering of comboboxes (share app, uninstall app, pull app to desktop, etc.) and updated/shortened changelog notes
Changed ComboBox FlatStyle from Flat to Standard to fix flickering. Updated changelog to shorten it.
2025-12-08 23:09:56 +01:00
jp64k
3e4787d1a2 Reworked DonorsListView UI, NewApps UI and ADB command UI
Introduced a cleaner AdbCommandForm for running custom ADB commands and toggling OS updates. The DonorsListView and MainForm UIs are modernized with rounded, shadowed windows, updated color schemes, and improved button and label styling. Removed legacy data bindings and redundant controls.
2025-12-07 23:30:16 +01:00
jp64k
047f84e9e7 Fixed critical dependency download issue (when not awaiting)
Changed the background task for downloading dependencies to be awaited.
2025-12-07 21:25:36 +01:00
jp64k
eb0245dfe2 oops - removed debug data 2025-12-07 21:02:15 +01:00
jp64k
602fafe779 Fixing dynamic layout sizing issues for bottom panels on resize 2025-12-07 20:40:49 +01:00
jp64k
bbe4050b40 Completed comprehensive rookie redesign with extensive UI/UX modernization, new theme and architectural improvements
Implemented a custom theme with a new color scheme and extensively refined UI logic and architecture for improved modernity and consistency. Relocated and reworked numerous options (mount device, select device, share app, uninstall app, pull-to-desktop, filters, etc.), and updated all message boxes to use the new themed styling with enhanced visual polish. All message boxes now use custom themed styling with enhanced visual polish. Corrected grammatical or logical flaws across text, tooltips, and title updates throughout the application. Added smooth animations to left-side navigation / container elements. Fine-tuned sizing, positioning, and colors across numerous UI components. Enhanced GalleryView with proper favorites support including context menu integration, favorite border styling and favorite badge, as well as some bug fixes. Implemented custom modern ToggleSwitch component (iOS-like) with animations. Completely overhauled quest option and rookie option menus to utilize new toggle switches in modernized layouts. Refined sorting and installation status logic to streamline UX; rookie now also functions as an efficient installed-quest-app browser with easily accessible view/uninstall controls. Added WebView2.dll validation to ensure runtime dependencies exist. Re-implemented trailer option. GalleryView is now shown on very first launch, but rookie remembers your preferred view thereafter, so list-view users won't be bothered, while everyone still gets to see the new gallery view at least once. Gallery performance has also been validated on very-low-spec hardware and confirmed to run fine and fast there, due to numerous optimizations. Given the extensive scope of changes across this commit series for beta-2.35-yt, I believe this update represents a significant milestone warranting v3.0 designation. In my opinion these changes represent one of the most significant set of logical and visual changes and enhancements the rookie application has seen in years. Changes have been summarized in changelog.txt for update.
2025-12-07 19:57:09 +01:00
jp64k
a25ae6dbb7 Startup performance optimizations (metadata extraction and game list initialization)
Improved performance and reliability of metadata extraction by using faster directory deletion, parallel operations, and atomic game list updates. Refactored game list initialization for better memory usage and parallelized device connection and metadata updates. Logger initialization message is now more prominent. Minor UI and code cleanups included.
2025-12-05 05:27:45 +01:00
jp64k
d89c9dd739 Fixed trailer runtime download regression
Readded automatic WebView2 runtime download and extraction if not present. Added additional error handling + message.
2025-12-05 01:17:40 +01:00
jp64k
9f719672ac Gallery view: Fixed incorrect tile selection after sorting 2025-12-04 23:21:50 +01:00
jp64k
8962cf6d4f Added *uninstall* buttons to list and gallery views for better UX, added sorting buttons to gallery view
- Added a uninstall button for installed items to list view and gallery view.

- Fixed uninstall (TodaysDate) prompt text.

- Added sorting functionality with custom sort panel / buttons for Name, Updated, Size, and Popularity fields, including ascending/descending toggles, to gallery view.
2025-12-04 23:01:36 +01:00
jp64k
ef3ef7d714 Added custom high-performance gallery view
Introduced a virtual gallery panel for displaying games in a high-performance, animated gallery view. Added a toggle button for toggling between list and gallery view, with synchronized data and selection between views. The new gallery view supports dynamic scaling, smooth scrolling, hover animations, status badges, search, filters, and is optimized for displaying large numbers of items with LRU image caching.
2025-12-04 04:42:34 +01:00
jp64k
2c1f0ec259 Overhauled UI (WIP)
Overhauled UI (left side navigation bar, device information, search, background, text colors) for a cleaner and more modern-looking interface. Removed no longer used resources. Adjusted layout and visibility of certain panels and controls for a cleaner interface.

TODO:
- Finish the full UI overhaul.
- Fix MainForm Designer so it doesn't override the changes.
2025-12-02 23:01:45 +01:00
jp64k
31b19ca0f6 Fixed list view initialization
Changed list view initialization from a background task to run directly on the UI thread.
2025-12-02 05:44:44 +01:00
jp64k
4682646d36 Massively improved game list initialization time (~20x faster)
Massively improved game list initialization and metadata extraction, reducing game list processing time from ~11 seconds to under 1 second (~20x faster) on my end. Refactored initListView with batched version code retrieval and parallelized blacklist/whitelist loading. Optimized Sideloader/RCLONE metadata extraction and improved directory operations. Minor UI and logging tweaks.
2025-12-02 04:23:45 +01:00
jp64k
352a86334f Add retry logic for RCLONE initialization
Added a retry mechanism in initMirrors to handle cases where RCLONE is not immediately available, and error handling.
2025-12-02 02:20:59 +01:00
jp64k
6c6732c8d1 Removed splash screen and reworked start logic for a quick, efficient launch
Removed the splash form, associated images, and all references to splash screen logic from the project. Refactored startup logic to run directly, with improved asynchronous initialization and cleanup.
2025-12-02 02:14:45 +01:00
jp64k
b5dfcef34f Reworked search bar design and massively improved search performance
Improved the search functionality by adding a search index for faster lookups and updating the debounce timer for quicker response. Modified the search bar color and changed the "Search" text placeholder color.
2025-12-02 01:50:47 +01:00
jp64k
fea63cc03e Added local blacklist support for donation requests
Added a local blacklist button to DonorsListView, allowing users to permanently skip donation requests for locally blacklisted apps. These apps are stored in a local blacklist.json file and locally merged with the server blacklist, enabling users to stop donation prompts for selected apps without waiting for them to be blacklisted on the server side. Also performed some minor code cleanup.
2025-12-01 23:36:02 +01:00
jp64k
39b946b94b No longer prompt for update when local version is greater than server version
Replaced string equality check with semantic version comparison in Updater.cs to ensure updates are only prompted when the server version is greater than the local version. Also removed unused usings from MainForm.cs for code cleanliness.
2025-12-01 20:58:48 +01:00
jp64k
de6c96a7ac Trailers are now shown next to thumbnails, and massively improved list filter performance
Trailers are now initialized and shown next to thumbnails. Removed the 'Use Trailers instead of Thumbnails' setting and related UI/control logic. Cleaned up associated code, settings, and designer elements for a simpler, unified trailer experience. Also cleaned up list filtering logic with massively improved performance (from several seconds to now being instant on 'UP TO DATE' / 'UPDATE AVAILABLE' / 'NEWER THAN LIST' button clicks).
2025-12-01 20:28:09 +01:00
jp64k
2590eebfff Fixed Youtube trailer playback issues on beta 2.35
Replaced embedded video URL loading with a local player.html using the YouTube IFrame API and WebView2 message bridge. Added per-game video ID caching, improved trailer search reliability, streamlined initialization and navigation logic for faster trailer loading.
2025-12-01 17:39:16 +01:00
fgsfds
b63c2c0466 added support for http proxy for rclone 2025-09-29 15:04:29 +05:00
Sombody101
dbaeea4e8b Moved WV2 event subscriptions to WV2 initialization 2025-09-23 23:04:04 +02:00
Sombody101
b120d18014 Added better error logging for trailer requests 2025-09-23 23:04:04 +02:00
Sombody101
85804ee69c Fix WebView2 'too many automatic redirections' error 2025-09-23 23:04:04 +02:00
Maxine
2b770b30a8 Revert "Restructure and project file organization"
This reverts commit 0edc1deed0.
2025-09-14 08:56:11 -07:00
Maxine
e6d178cb2a Revert "Minor MainForm field cleanup"
This reverts commit 630fe98d31.
2025-09-14 08:56:11 -07:00
Maxine
c2f5b20e83 Revert "Minor Main file organization"
This reverts commit bd193c0d3f.
2025-09-14 08:56:11 -07:00
Maxine
952251be35 Revert "Renamed 'ADB' to 'AdbManager'"
This reverts commit 252955da0a.
2025-09-14 08:56:11 -07:00
Maxine
385fe45d5d Revert "Fix WebView2 'too many automatic redirections' error"
This reverts commit 06dc91f130.
2025-09-14 08:56:11 -07:00
Maxine
5db0ea301d Revert "Fix unmoved line"
This reverts commit 523efba81c.
2025-09-14 08:56:11 -07:00
Chax
5dc8c947c9 Bumpin! 2025-09-14 17:54:34 +02:00
Sombody101
523efba81c Fix unmoved line 2025-09-14 08:43:59 -07:00
Sombody101
06dc91f130 Fix WebView2 'too many automatic redirections' error 2025-09-14 08:43:59 -07:00
Sombody101
252955da0a Renamed 'ADB' to 'AdbManager' 2025-09-14 08:43:59 -07:00
Sombody101
bd193c0d3f Minor Main file organization 2025-09-14 08:43:59 -07:00
Sombody101
630fe98d31 Minor MainForm field cleanup 2025-09-14 08:43:59 -07:00
Sombody101
0edc1deed0 Restructure and project file organization 2025-09-14 08:43:59 -07:00
Chax
413113e732 Merge branch 'beta/RSL-2.35' of https://github.com/VRPirates/rookie into beta/RSL-2.35 2025-09-13 23:17:09 +02:00
Chax
26f7044053 Remove hideAdultContent due to there not being any anymore 2025-09-13 23:16:58 +02:00
Ashus
5a7c4971df Changed C:\RSL\platform-tools path to platform-tools in working dir 2025-09-11 17:27:27 -07:00
fenopy
a397a1a985 fix- donor window only displays unique entries 2025-07-16 15:56:45 -05:00
fenopy
44e8bdef7b fix- cleanup packages listing 2025-07-16 15:56:09 -05:00
Chax
6aa5c7baea bump it while its hot 2025-04-13 11:41:43 +02:00
Chax
da09545168 Fix "Cannot add or insert the item" bug. 2025-04-13 11:37:33 +02:00
Chax
e07e9a09fa bump 2025-04-10 22:33:52 +02:00
Chax
49deba5771 Update Discord 2025-04-10 16:21:57 +02:00
Chax
56947d0648 Add favorite mechanism 2025-04-10 16:18:53 +02:00
Chax
f2c285c141 Show release notes with trailers enabled, this fixes #198 2025-03-22 13:15:51 +01:00
Chax
b4b2a42db5 Merge branch 'master' of https://github.com/VRPirates/rookie 2025-02-22 15:31:08 +01:00
Chax
3195116fdf Remove obsolete code 2025-02-22 15:31:00 +01:00
tastyratz
5df51cfcba Update AddDefenderExceptions.ps1
Pulled duplicate folder out
2025-02-22 15:29:59 +01:00
tastyratz
a6525bf885 Update AddDefenderExceptions.ps1
Tweaked for requested changes
2025-02-22 15:29:59 +01:00
tastyratz
516618bd5a Update AddDefenderExceptions.ps1
Added an auto elevate to admin section instead of admin check.

Replaced array of paths with psscriptroot for 2 reasons; 
1. This lets users exclude whatever folder they want regardless of install path
2. Defender excludes all subdirectories and files recursively making all those entries redundant https://support.microsoft.com/en-us/windows/add-an-exclusion-to-windows-security-811816c0-4dfd-af4a-47e4-c301afe13b26

Finally,
I added some sleeps and pauses for users to review messages and results/errors
2025-02-22 15:29:59 +01:00
Chax
098c0a07a3 Bump ver 2025-02-22 15:29:32 +01:00
Chax
74fcf91572 Lower ADB version to solve ADB outputting pushes to stderr 2025-02-22 15:25:37 +01:00
Chax
1fb77054d1 Allow users to cancel backup 2025-02-22 15:25:25 +01:00
Chax
c53db4bfc3 Revert 2c513ac7, too buggy. 2025-02-22 15:15:42 +01:00
Maxine
19909a82cb Merge pull request #171 from VRPirates/beta/RSL-2.32
RSL 2.32
2024-12-29 02:23:48 +01:00
fenopy
46196d128b feat: switch to offline mode when unable to connect (dont kill) 2024-12-27 09:27:50 -06:00
fenopy
f32bd15d98 feat: allow switching between pub and priv configs 2024-12-27 09:00:32 -06:00
fenopy
ce4cd9ad46 chore: add defender exceptions ps 2024-12-27 07:16:45 -06:00
fenopy
d91c41b19c version bump 2024-12-27 06:48:39 -06:00
fenopy
b38b8d1771 fix: move labels to avoid covering queues (#169) 2024-12-27 06:47:36 -06:00
fenopy
51941f2b73 fix: changes to mirror init to fix vip issues 2024-12-27 06:43:30 -06:00
Chax
5907e48018 Remove unneeded flush call 2024-12-21 14:59:47 +01:00
Chax
8d4d2c9711 This fixes #167 2024-12-21 14:59:09 +01:00
Chax
2c513ac7a8 Add saving the ColumnWidths, Window Sizes/Position 2024-12-14 14:02:40 +01:00
Chax
9a17dd70c1 Use Environment.CurrentDirectory instead 2024-12-14 12:57:57 +01:00
fenopy
72d2db3fcc Merge branch 'beta/RSL-2.32' of github.com:VRPirates/rookie into beta/RSL-2.32 2024-12-09 10:59:10 -06:00
fenopy
05cff37dd5 bump 7z to 24.09 2024-12-09 10:59:04 -06:00
fenopy
094d996249 bump adb aapt deps 2024-12-09 10:46:45 -06:00
Fenopy
c079f87fc6 Merge pull request #161 from VRPirates/beta/RSL-2.31.2
version bump
2024-12-09 10:30:41 -06:00
fenopy
288da1736e version bump 2024-12-09 10:30:04 -06:00
Fenopy
1160c24ad2 Merge pull request #156 from VRPirates/beta/RSL-2.31.1
Beta/rsl 2.31.1
2024-12-06 19:35:26 -06:00
fenopy
b2c8dd7d10 chore: linting 2024-12-06 18:33:04 -06:00
fenopy
59674fe781 fix: adult content filtering 2024-12-06 18:32:54 -06:00
fenopy
4b3b9b0558 version bump 2024-12-06 18:32:09 -06:00
Fenopy
866f8821a5 Merge pull request #155 from VRPirates/beta/RSL-2.31
fix: column sort for new popularity ratings
2024-12-06 13:26:07 -06:00
fenopy
2b4ec9af38 fix: column sort for new popularity ratings 2024-12-06 13:25:31 -06:00
Fenopy
6527a7ce31 Merge pull request #154 from VRPirates/beta/RSL-2.31
Beta/rsl 2.31
2024-12-06 13:13:48 -06:00
fenopy
aa9babfb18 chore: bump rclone to 1.68.2 2024-12-06 13:11:29 -06:00
fenopy
6e9b8a19f5 version bump 2024-12-06 12:49:30 -06:00
fenopy
62515ef59c fix: change "downloads" to "popularity" 2024-12-06 12:46:12 -06:00
fenopy
1d3830e0b2 fix: case insensitive packagename checks 2024-12-06 12:42:48 -06:00
fenopy
b62307310c fix: wrap trailer fetching in catch 2024-11-14 09:22:00 -06:00
fenopy
6b2841cd0c fix: change adult filter for new format 2024-11-14 08:35:19 -06:00
fenopy
a5b0e60acc chore: bump rclone to 1.68.1 2024-11-10 10:50:44 -06:00
fenopy
c084cf9bfb fix: always create log 2024-11-10 08:28:01 -06:00
fenopy
005918aec0 feat: preserve vrp.download.config on rclone update 2024-11-10 08:26:16 -06:00
fenopy
5eaf7d5498 fix: adult content filter 2024-11-10 08:23:35 -06:00
fenopy
b3ca9bcd66 Merge branch 'beta/RSL-2.31' of https://github.com/VRPirates/rookie into beta/RSL-2.31 2024-11-09 21:22:33 -06:00
fenopy
9aed677d3a chore: add build.cmd 2024-11-09 21:21:50 -06:00
Chax
1b66beb957 Fix OBB pushing on drag and drop. 2024-11-10 04:14:43 +01:00
fenopy
0e092df064 fix: update cleanupinstall to use userprofile instead of username 2024-11-09 21:07:24 -06:00
Chax
d80dc1c465 Add safeguard to prevent deletion of current directory 2024-11-05 00:19:56 +01:00
Chax
eed21ff56f Add intricate logging and trycatch block to initListView when checking for unrecognized app 2024-10-21 21:37:14 +02:00
fenopy
1471b6bb0b Merge remote-tracking branch 'origin/beta/RSL-2.30.1' into beta/RSL-2.31 2024-10-11 13:47:22 -05:00
Chax
f85d43d701 Revert putting the check in ShowPrcOutput and instead check in downloadInstallGameButton_Click & refreshGamesListAsync 2024-10-07 19:46:44 +02:00
Chax
73f0a840e9 Do not show ShowPrcOutput messages when settings.EnableMessageBoxes is disabled 2024-10-07 19:35:32 +02:00
Chax
a09e469491 Remove redundant SettingsManager calls & add HideAdultContent checkbox 2024-10-06 18:11:54 +02:00
Chax
1db593c634 Initiate SettingsManager in Logger.cs and Zip.cs & Save() when creating Instance 2024-10-05 18:34:30 +02:00
Chax
eb88d746c1 Switch away from using inbuilt settings and use a json 2024-10-05 17:09:12 +02:00
fenopy
f7b5c94136 fix: wrap adb in try to prevent crash 2024-10-04 08:44:15 -05:00
fenopy
58aa1be7c5 chore: update readme 2024-10-02 06:33:12 -05:00
Chax
426d99e519 Fix sorting in all numeric columns.
This closes #124
2024-09-23 20:47:16 +02:00
Fenopy
8dec5d906a Merge pull request #137 from VRPirates/beta/RSL-2.30
Beta/rsl 2.30
2024-09-17 10:12:05 -05:00
fenopy
ae02b5c135 chore: update readme with latest VT-Scan 2024-09-17 10:11:00 -05:00
fenopy
fe94119627 fix: set dialog parent 2024-09-17 09:59:22 -05:00
Fenopy
ee13bd08cb Merge pull request #134 from VRPirates/beta/RSL-2.30-readme
chore: update readme
2024-09-12 08:47:39 -05:00
fenopy
6a464ad3e7 chore: update readme 2024-09-12 08:45:50 -05:00
fenopy
f0d2a42ee3 fix: update pub config toggles prompt on/off 2024-09-10 12:08:44 -05:00
fenopy
b18ce51e4c fix: fix downloading rclone when folder exists but exe does not 2024-09-10 08:14:43 -05:00
fenopy
535b3826f8 fix: change device unauthorized prompt 2024-09-05 10:28:50 -05:00
fenopy
f1a884e200 chore: change to dl-args 2024-08-29 15:25:20 -05:00
fenopy
072e2246a8 fix: avoid 0 bwlimit 2024-08-29 13:49:17 -05:00
fenopy
2c3ecf654a feat: disable-app-check also prevents new-app dono box 2024-08-28 15:10:08 -05:00
fenopy
8699136d6d chore: update changelog for 2.30 2024-08-28 14:57:40 -05:00
Fenopy
35803b7014 Merge pull request #130 from VRPirates/fix/DragAndDroppingCustomInstall
Fix Rookie not running custom install.txts when drag and dropping who…
2024-08-28 14:47:33 -05:00
fenopy
af358f8f99 Merge branch 'beta/RSL-2.30' of https://github.com/VRPirates/rookie into beta/RSL-2.30 2024-08-28 14:46:41 -05:00
Fenopy
138183798c Merge pull request #129 from VRPirates/fix/CopyGameList
Change copying the gamelist to grab the items of the listview directly.
2024-08-28 14:46:21 -05:00
fenopy
8b3066cf99 Merge branch 'feat/BandwidthLimiter' into beta/RSL-2.30 2024-08-28 14:42:44 -05:00
fenopy
91e21f59af chore: label buttons 2024-08-28 13:16:31 -05:00
Chax
85144e339f Allow spaces in username 2024-08-28 19:55:27 +02:00
Chax
dbd9b145ea Merge 2024-08-28 19:51:49 +02:00
fenopy
287181622d fix: lock adb command size 2024-08-28 12:48:39 -05:00
Chax
7f76bba866 Adjust window size to account for downloads column 2024-08-28 19:48:32 +02:00
fenopy
4b791fe86b chore: version bump 2024-08-28 12:30:32 -05:00
fenopy
e17a582ae7 fix: add inplace to sponsored downloads 2024-08-28 12:29:13 -05:00
fenopy
a271088e9a chore: remove reused code 2024-08-28 12:29:02 -05:00
fenopy
712d686559 fix: issues switching to sponsored mirror 2024-08-28 12:28:51 -05:00
fenopy
481dd1b3e2 fix: change splash during get deps 2024-08-28 11:23:17 -05:00
fenopy
bd435094b6 chore: bump rclone to 1.67.0 2024-08-28 11:09:13 -05:00
fenopy
16fa08d952 fix: actually use getdeps 2024-08-28 11:08:56 -05:00
fenopy
aa89953bf5 chore: rename assets from random names; add offline splash 2024-08-28 09:57:33 -05:00
fenopy
06cba22ad8 chore: fix error message language 2024-08-28 09:20:16 -05:00
fenopy
5923ce5ebc chore: fix wording 2024-08-28 07:32:31 -05:00
fenopy
01eac1aec6 fix: changes for new upload dir 2024-08-28 07:32:23 -05:00
fenopy
46189dcd43 chore: metric change 2024-07-19 10:11:05 -05:00
fenopy
1d9f304735 feat: add Send Command button to adb form 2024-07-17 15:17:19 -05:00
fenopy
362b74e6a5 feat: add downloads column 2024-07-16 11:42:50 -05:00
Chax
ee68e3f01b Add a bandwidth limiter 2024-07-16 18:39:29 +02:00
Chax
09e2de0390 Fix Rookie not running custom install.txts when drag and dropping whole folders. 2024-07-16 17:38:11 +02:00
fenopy
12c9972b24 feat: download metrics 2024-07-16 10:30:32 -05:00
Chax
df32f93095 Change copying the gamelist to grab the items of the listview directly. 2024-07-16 16:44:21 +02:00
fenopy
70d95b353e feat: added disable-app-check for testers sanity 2024-07-12 13:43:43 -05:00
fenopy
13eba3b3a6 fix: settings form updates 2024-07-11 08:55:44 -05:00
fenopy
1afdc9e7e3 feat: supress prompt to re-download 2024-07-11 08:55:35 -05:00
fenopy
38a113b5fb chore: add cleanupinstall to build 2024-06-27 06:58:38 -05:00
fenopy
25f478e265 fix: init ordering 2024-06-27 06:58:23 -05:00
fenopy
6a25662566 fix: change deps downloads, add splashes 2024-06-20 12:32:28 -05:00
fenopy
55af990071 fix: remove unused deps 2024-06-20 12:32:04 -05:00
fenopy
927a4bbaa4 fix: changes to splash init, add splashes 2024-06-20 12:30:22 -05:00
fenopy
a101c621ec chore: remove msg to start offline-mode 2024-06-20 12:27:10 -05:00
fenopy
e25338e817 fix: change unable to connect msg 2024-06-20 12:26:46 -05:00
fenopy
ecb1751cf5 feat: add cleanup-install dl 2024-06-20 11:59:09 -05:00
fenopy
aa54047d44 feat: add cleanup-install cmd 2024-06-20 11:57:25 -05:00
Fenopy
e02f82ee31 Merge pull request #117 from frxctura/fix/PublicMirrorNotAppearing
Fix Public Mirror not displaying on lblMirror control
2024-06-20 10:46:34 -05:00
Fenopy
e8ff31d8d9 Merge pull request #118 from frxctura/fix/AskForJSON
Fix Rookie prompting for .json every launch w/out it
2024-06-20 10:46:14 -05:00
Chax
072805dd8f Fix Rookie prompting for .json every launch w/out it 2024-06-20 13:43:29 +02:00
Fenopy
5626e0f5bc Merge pull request #116 from hellidox/patch-2
Wrap username variable in quotes
2024-06-20 06:04:36 -05:00
Maxine
9fee7dc787 Update README.md 2024-06-15 23:34:39 +02:00
hellidox
f86d97a96f allow spaces in username (the second)
this does work actually i just tested it
2024-06-15 16:33:17 -05:00
fenopy
6b0e8efadc fix: remove random int on initMirrors 2024-06-05 16:54:50 -05:00
Chax
7dba65c2cc Fix Public Mirror not displaying on lblMirror control 2024-06-05 23:18:39 +02:00
fenopy
497c670a98 fix: quotes around some command paths 2024-06-03 08:37:49 -05:00
Fenopy
d77790c6fa Merge pull request #108 from VRPirates/beta/RSL-2.29.2
chore: changelog update
2024-05-22 06:47:18 -05:00
fenopy
2c8311a0ff chore: changelog update 2024-05-22 06:46:38 -05:00
Fenopy
7bf5a8eaec Merge pull request #107 from VRPirates/beta/RSL-2.29.2
Beta/rsl 2.29.2
2024-05-22 06:45:46 -05:00
fenopy
bdffbc0757 chore: update credits 2024-05-22 06:40:17 -05:00
fenopy
c085eaa0a3 fix: initilization order 2024-05-22 06:40:11 -05:00
fenopy
5580e0b29e Merge branch 'beta/RSL-2.29.2' of https://github.com/VRPirates/rookie into beta/RSL-2.29.2 2024-05-22 06:11:00 -05:00
fenopy
3b174c6b4b chore: version bump 2024-05-22 06:10:58 -05:00
Fenopy
d5acdde3c4 Merge pull request #106 from VRPirates/master
Merge pull request #105 from VRPirates/beta/RSL-2.29.1
2024-05-22 06:09:41 -05:00
fenopy
a1afa2ddee fix: fix uploads on new rclone 2024-05-22 06:08:17 -05:00
Fenopy
66d2c04834 Merge pull request #105 from VRPirates/beta/RSL-2.29.1
Beta/rsl 2.29.1
2024-05-20 12:57:49 -05:00
fenopy
81559b6a8f version bump 2024-05-20 12:57:33 -05:00
fenopy
3d8c06e726 fix: initial gui name 2024-05-20 11:54:48 -05:00
fenopy
32d30df8a3 chore: bump package deps 2024-05-20 11:54:40 -05:00
fenopy
1b0746df6d fix: initilization error 2024-05-20 11:54:01 -05:00
Fenopy
a0b9a43e15 Merge pull request #104 from VRPirates/beta/RSL-2.29
Beta/rsl 2.29
2024-05-20 11:34:00 -05:00
fenopy
58f770cb6d chore: version bump 2024-05-20 11:27:03 -05:00
Fenopy
0db624c3c6 Merge pull request #96 from Chax1/QoL/AdditionalTroubleshooting
additional troubleshooting
2024-05-20 11:24:09 -05:00
fenopy
79718d6ade feat: better progress bar and download tracking 2024-05-09 16:29:43 -05:00
fenopy
2bcad00193 chore: bump rclone to 1.66.0 2024-05-09 16:26:36 -05:00
fenopy
b22893eb54 fix: change uuid calc 2024-04-05 06:24:40 -05:00
Fenopy
9a8a97dbac Merge pull request #95 from Chax1/gitdepdencies
Add dependencies
2024-04-04 08:36:05 -05:00
Chax
9d1f05eab4 Put the rclone dependencies into a folder 2024-04-04 15:34:50 +02:00
Chax
cb74756752 Add a fallback for rclone 2024-04-04 15:32:08 +02:00
Maxine
2ea00d12f3 Merge branch 'VRPirates:master' into gitdepdencies 2024-04-04 15:23:16 +02:00
Fenopy
b59a816a8b Merge pull request #94 from VRPirates/beta/RSL-2.28
rsl 2.28 release
2024-04-04 08:11:40 -05:00
fenopy
9363b2e26c chore: default to singlethread; change app setting dir 2024-04-01 16:11:44 -05:00
fenopy
0932602bbb version bump 2024-04-01 06:59:43 -05:00
Maxine
85ad2c0097 Add files via upload 2024-03-11 18:29:15 +01:00
fenopy
19916388f2 fix: wording updates 2024-03-04 12:51:36 -06:00
fenopy
d604107b7f Merge branch 'beta/RSL-2.28' of https://github.com/VRPirates/rookie into beta/RSL-2.28 2024-03-04 07:31:14 -06:00
fenopy
f329965b3b fix: default to single-threading 2024-03-04 07:31:12 -06:00
Chax
c4e8c9048d Rookie will ask if the user wants to create the public config file if necessary 2024-03-01 14:32:26 +01:00
Fenopy
da8e1d1183 Merge pull request #91 from Chax1/beta/RSL-2.28
Update README
2024-02-29 09:41:38 -06:00
Maxine
b1bf8462da Update README.md 2024-02-29 15:01:25 +01:00
Maxine
665a288af5 Update README.md 2024-02-29 15:00:00 +01:00
Fenopy
ec3e0ccf6d Merge pull request #90 from Chax1/beta/RSL-2.28
Fixes
2024-02-29 06:09:18 -06:00
Chax
ea8e16c418 Check if devices list is empty before trying to access to ensure Index was out of range exception doesn't occur. 2024-02-29 00:43:01 +01:00
Chax
0b8ca66692 Disable searchbar until fully loaded to prevent race condition 2024-02-29 00:20:26 +01:00
Chax
921b8340f0 Fix crashlogs being just called "UUID" 2024-02-29 00:06:35 +01:00
fenopy
873be55e78 fix: floating ui; ab-restore messages 2024-02-28 17:01:05 -06:00
Fenopy
772fa9a4df Merge pull request #89 from VRPirates/beta/RSL-2.28-dep
chore: update dependencies
2024-02-28 16:31:45 -06:00
fenopy
121d9e2c58 chore: update dependencies 2024-02-28 16:31:12 -06:00
fenopy
bca3488748 chore: clean up old dependencies 2024-02-28 16:28:56 -06:00
fenopy
0af6a30347 chore: update dependencies [code] 2024-02-28 16:28:36 -06:00
fenopy
d38d775cc9 chore: update dependencies 2024-02-28 16:27:25 -06:00
fenopy
5fc1151e90 chore: remove unused files 2024-02-28 15:36:22 -06:00
fenopy
737a61a3e9 chore: formatting 2024-02-28 15:32:05 -06:00
fenopy
4c8caec6bc feat: enable restoring of gamedata or ab files 2024-02-28 15:22:12 -06:00
fenopy
9cccc9f0cc chore: add public mirror json by default 2024-02-28 11:56:40 -06:00
fenopy
7cfee896b1 Merge branch 'beta/RSL-2.28' of https://github.com/VRPirates/rookie into beta/RSL-2.28 2024-02-28 11:54:41 -06:00
fenopy
1dddf5c41a feat: form redesign; add osupdate toggle; add adb backup 2024-02-28 11:54:38 -06:00
fenopy
dc75776d3f chore: readme update 2024-02-28 11:53:56 -06:00
Fenopy
018c30b73f Merge pull request #85 from Chax1/fix/UpdateSettingsAfterUpdate
Upgrade settings after application update
2024-02-28 11:53:44 -06:00
Fenopy
0d8f776e8d Merge pull request #88 from VRPirates/master
merge master to latest beta branch
2024-02-28 11:53:25 -06:00
fenopy
df0a8b10d1 fix: allow adb commands with or without prefix 2024-02-28 11:51:21 -06:00
Chax
aeaec5465b Upgrade settings after application update 2024-02-10 14:51:01 +01:00
Fenopy
8f15d65c5b Merge pull request #82 from Chax1/fix/7zCrash
Fix 7z Crash by not using x.ExitCode anymore
2024-02-07 13:43:31 -06:00
Chax
7e19403aa9 Fix 7z Crash by not using x.ExitCode anymore 2024-02-07 20:19:52 +01:00
Fenopy
bd18acc6a1 Merge pull request #81 from Chax1/master
Version Bump + Donation Fix
2024-02-06 11:28:37 -06:00
Chax
7046458db6 Version Bump + Donation Fix 2024-02-06 18:26:15 +01:00
fenopy
beab57218c codename add 2024-02-06 07:35:07 -06:00
fenopy
46cd27c012 fix: codenames 2024-02-06 06:01:37 -06:00
Fenopy
18123b2755 Merge pull request #80 from VRPirates/beta/RSL-2.27
version bump
2024-02-02 06:44:46 -06:00
fenopy
bbda0f33bd version bump 2024-02-02 06:42:13 -06:00
Fenopy
1edd364a33 Merge pull request #79 from VRPirates/beta/RSL-2.27
Fix: correct codenames url
2024-02-02 06:38:48 -06:00
fenopy
89d6acb623 Fix: correct codenames url 2024-02-02 06:37:40 -06:00
Fenopy
ff2f648379 Merge pull request #78 from VRPirates/beta/RSL-2.27
Beta/rsl 2.27
2024-02-01 16:05:46 -06:00
fenopy
0d9cf1cd91 fix: add exit on 7z error 2024-02-01 16:02:14 -06:00
Fenopy
40505f7628 Merge pull request #77 from Chax1/fix/No7z
Throw error message if unable to get 7z
2024-02-01 15:59:25 -06:00
Chax
3dee5372f2 Throw error message if unable to get 7z 2024-02-01 22:58:19 +01:00
Fenopy
6ee5b1014b Merge pull request #76 from Chax1/chore/ChangelogBump
Ver bump
2024-02-01 15:47:32 -06:00
Fenopy
189ba62385 Merge pull request #75 from Chax1/QoL/ProtonIsFree
Mention that ProtonVPN is free.
2024-02-01 15:47:05 -06:00
Chax
b09e7ff2bc Ver bump 2024-01-25 20:21:30 +01:00
Chax
31492a0af3 Mention that ProtonVPN is free. 2024-01-25 19:49:02 +01:00
fenopy
f3d81252f0 chore: prettier 2024-01-08 15:47:27 -06:00
fenopy
e795b85ab8 chore: clean up path concats; thx @Krutonium 2024-01-08 15:43:25 -06:00
fenopy
3c4e4677d4 fix: lol 2024-01-08 15:11:19 -06:00
fenopy
9f584da9ab feat: change main config location; add fallback 2024-01-08 15:03:02 -06:00
fenopy
a440848584 Merge branch 'beta/RSL-2.27' of https://github.com/VRPirates/rookie into beta/RSL-2.27 2024-01-08 08:34:37 -06:00
Fenopy
2301aa6425 Merge pull request #72 from Chax1/fix/ZipThrow
Fix throwing an exception in Zip.cs
2024-01-08 08:34:42 -06:00
Fenopy
263f2bf4c1 Merge pull request #71 from Chax1/feature/SearchRelease
Improve search to also be able to search release names
2024-01-08 08:34:17 -06:00
fenopy
59d0c9a7ce fix: handle errors downloading codenames file 2024-01-08 08:33:46 -06:00
Chax
19432a6362 Fix throwing an exception in Zip.cs 2024-01-05 18:47:17 +01:00
Chax
97ec1cd385 Improve search to also be able to search release names 2024-01-03 14:38:24 +01:00
fenopy
162d60bbdc fix: codename filtering 2023-12-29 07:20:33 -06:00
Fenopy
3bcdc7871d Merge pull request #65 from Chax1/fix/7zipArchitectureCheck
Check for architecture when downloading 7z files
2023-12-14 15:49:45 -06:00
Fenopy
1f845a3682 Merge pull request #66 from Chax1/fix/AsyncSyncRead
Fix mixing async and sync read
2023-12-14 15:49:03 -06:00
Fenopy
a0a97a9451 Merge pull request #67 from Chax1/fix/CrossThreadCrash
Fix crash when downloading without enough space
2023-12-14 15:48:45 -06:00
Fenopy
c6261687b3 Merge pull request #68 from Chax1/feature/AddCodenameToUpload
Add codename to upload
2023-12-14 15:48:30 -06:00
Chax
6deb311ecc Update to include filtering 2023-12-14 16:14:25 +01:00
Chax
0c6c8d5546 Add codename to upload
Changes
2023-12-14 08:40:23 +01:00
Chax
4e391c0e56 Fix crash when downloading without enough space 2023-12-12 11:56:28 +01:00
Chax
9788aa494a Check for architecture when download 7z files 2023-12-12 11:39:43 +01:00
Chax
4f0cc7ab71 Fix mixing async and sync read 2023-12-09 21:35:49 +01:00
Fenopy
0bf9a00af2 Merge pull request #62 from VRPirates/beta/RSL-2.26
beta/rsl 2.26
2023-12-08 09:37:33 -06:00
Fenopy
fa9baced7a Merge pull request #52 from Chax1/beta/RSL-2.26
chore: version bump
2023-12-06 14:31:46 -06:00
Fenopy
1f51f41954 Merge pull request #61 from Chax1/feature/ShowExtractionProgress
Change labels on extraction
2023-12-06 14:29:27 -06:00
fenopy
bc8599315e fix: correct tooltips for new leftnav 2023-12-06 14:28:29 -06:00
Chax
1c49a33651 Change labels on extraction 2023-12-06 21:26:24 +01:00
Maxine
13ab8a74d6 Update changelog.txt 2023-12-06 21:10:24 +01:00
Maxine
51155f105c Update changelog.txt 2023-12-06 21:10:03 +01:00
Fenopy
af46aa7c78 Merge pull request #60 from Chax1/feature/ShowExtractionProgress
Show extraction progress.
2023-12-06 14:08:26 -06:00
fenopy
a7d46753aa Merge branch 'beta/RSL-2.26' of https://github.com/VRPirates/rookie into beta/RSL-2.26 2023-12-06 14:07:56 -06:00
Fenopy
161f257985 Merge pull request #59 from Chax1/fix/NoDeviceModeOutput
Fix no device mode output
2023-12-06 14:07:30 -06:00
fenopy
223de0ba4a chore: remove qu settings 2023-12-06 14:06:37 -06:00
Maxine
2fa9c34e98 Update changelog.txt 2023-12-06 20:52:30 +01:00
Chax
5d5011e409 Show extraction progress. 2023-12-06 20:46:15 +01:00
Maxine
fd4357bdad Update changelog.txt 2023-12-06 20:29:05 +01:00
Chax
1a073f9661 Fix no device mode output 2023-12-06 20:12:25 +01:00
fenopy
e73c204d1c feat: sideloading toggle button 2023-12-06 12:55:23 -06:00
fenopy
80cb0b46bc feat: add open DLs and run adb commands to leftnav 2023-12-06 12:30:31 -06:00
fenopy
680a5f256a feat: remove dumb enter-key toggle for install 2023-12-06 12:28:00 -06:00
fenopy
2b02d04219 fix: counting of installed games 2023-12-06 11:08:51 -06:00
fenopy
7a61d73e65 fix: gui freeze on launch 2023-12-06 11:08:37 -06:00
Fenopy
972116ee96 Merge pull request #58 from Chax1/fix/WebViewNukingItself
Fix crash due to WebView21 not existing
2023-12-06 11:01:56 -06:00
Chax
d780607363 Fix crash due to WebView21 not existing 2023-11-30 22:26:39 +01:00
Fenopy
236004aef9 Merge pull request #56 from Chax1/feature/NumberedUpdateStates
feature/numbered update states
2023-11-30 08:21:11 -06:00
Chax
07bc133096 update changelog.txt 2023-11-24 06:00:20 +01:00
Chax
50427ef2d3 Add count to update state labels. 2023-11-24 05:48:13 +01:00
Chax
63eef42ca0 chore: version bump 2023-11-23 16:40:45 +01:00
Fenopy
8723b195df Merge pull request #50 from Chax1/fix/AdditionalQuest3Support
Fix CopyOBB for Quest 3
2023-11-22 18:52:52 -06:00
Fenopy
ae86b47268 Merge pull request #51 from Chax1/fix/CrashOnIPStore
Fix crash when trying to write to read-only StoredIP.txt
2023-11-22 18:52:20 -06:00
Chax
9901a0d770 Update MainForm.cs 2023-11-22 23:43:35 +01:00
Chax
da48607154 Fix CopyOBB for Quest 3 2023-11-22 23:38:19 +01:00
Fenopy
35f3a80eb1 Merge pull request #47 from Chax1/fix/WebViewKill
Fix crash due to unkilled WebView2 instances.
2023-11-22 16:13:39 -06:00
Fenopy
0da1bf7a3b Merge pull request #48 from Chax1/fix/MessageBoxParents
Add missing parents to messageboxes
2023-11-22 16:13:20 -06:00
Fenopy
c027fca339 Merge pull request #49 from Chax1/QoL/MoreApparantError
Make message more friendly.
2023-11-22 16:13:01 -06:00
Chax
5ac98f5ef8 Make message more friendly. 2023-11-22 22:34:01 +01:00
Chax
3cb0cca801 Add missing parents to messageboxes 2023-11-18 15:00:01 +01:00
Chax
b4fd4c76a0 Fix crash due to unkilled WebView2 instances. 2023-11-16 21:18:47 +01:00
fenopy
ecf19115c6 chore: readme update 2023-11-07 15:41:33 -06:00
Fenopy
d475936bbb Merge pull request #43 from VRPirates/beta/RSL-2.25
Beta/rsl 2.25.1
2023-11-04 15:23:19 -05:00
Fenopy
c404d69b54 Merge pull request #42 from Chax1/beta/RSL-2.25
Fix Device not authorized prompt showing up after authorization.
2023-11-04 15:22:54 -05:00
Chax
6edc9cf7e9 Change not authorized message to be more clear on what to do
Some additional info
2023-11-04 16:23:01 +01:00
Chax
71d89284f1 Chore: Bump Version. 2023-11-04 16:02:29 +01:00
Chax
39473ba15c - Fix: Device not authorized prompt showing up after authorization. 2023-11-04 16:02:06 +01:00
Fenopy
3f3fa3dc11 Merge pull request #39 from VRPirates/beta/RSL-2.25
Beta/rsl 2.25
2023-11-03 12:54:10 -05:00
fenopy
3f06099d38 chore: version bump 2023-11-03 12:46:14 -05:00
Fenopy
450cf282aa Merge pull request #35 from Chax1/fix/UnauthFreeze
fix: fix unauthorized bug
2023-11-02 14:12:09 -05:00
fenopy
804c452f6d chore: update wiki url 2023-11-02 10:00:29 -05:00
Chax
c576f62d28 Fix the unauthorized bug, hopefully. 2023-10-31 09:35:12 +01:00
Fenopy
73651723dd Merge pull request #29 from VRPirates/beta/RSL-2.24
RSL 2.24
2023-10-13 18:22:18 -05:00
fenopy
82cc713372 version bump 2023-10-13 18:21:35 -05:00
Fenopy
6d0788268d Merge pull request #28 from Chax1/fix/searchbar
Fix searchbar not being anchored properly
2023-10-13 11:11:39 -05:00
Chax
c183cb6d7a Fix searchbar not being anchored properly 2023-10-13 16:55:06 +02:00
Fenopy
3b83db0177 Merge pull request #27 from Chax1/fix/Quest3Support
Quest 3 stuff
2023-10-12 14:12:16 -05:00
Chax
03f4dcb687 Fix Quest 3 stuff 2023-10-12 21:02:24 +02:00
Chax
9415daba07 Fix Rookie pushing OBB several times.
add installTxtPath
2023-10-12 21:02:21 +02:00
Chax
553571027d Revert changeTitlebarToDevice() changes
Change this too.
2023-10-12 21:02:16 +02:00
Fenopy
273ddda076 Merge pull request #25 from Chax1/fix/CrashOnBackup
Fix crash when trying to backup
2023-10-09 08:35:25 -05:00
Chax
3c8f08a380 Fix crash when trying to backup 2023-10-09 15:19:44 +02:00
Fenopy
b5ac8175dc Merge pull request #24 from Chax1/QoL/CloseOnApply
Close Settings Form when applying changes
2023-10-09 08:02:59 -05:00
Fenopy
d765a9b6eb Merge pull request #23 from Chax1/feature/DirectoryButtons
Add Open Directory Buttons
2023-10-09 08:02:49 -05:00
Chax
409afd8eac Add Open Directory Buttons
Remove console writelines
2023-10-09 14:50:35 +02:00
Chax
d7640d4c5d Close Settings Form when applying changes 2023-10-09 14:45:31 +02:00
fenopy
3828da4864 chore: reorder startup tasks 2023-10-06 07:55:34 -05:00
fenopy
a45f7db85c chore: additional logging to startup 2023-10-06 07:54:44 -05:00
Fenopy
df6a9e9286 Merge pull request #19 from Chax1/feature/ImproveSearch
fix: improve search functionality
2023-09-18 08:28:52 -05:00
Fenopy
c516f80dc1 Merge pull request #17 from Chax1/fix/OutputMessage
Handle remote write failed
2023-09-18 08:27:47 -05:00
Fenopy
0ef2f296f5 Merge pull request #16 from Chax1/QoL/CodeCleanup
Move startup tasks into seperate methods for cleaner code.
2023-09-18 08:27:35 -05:00
Chax
48597983e2 Remove the sorting to top as its not needed. 2023-09-15 15:21:22 +02:00
Chax
a6a4a057fe Improve search drastically. 2023-09-15 11:56:44 +02:00
Fenopy
c7cfc07c9b Merge pull request #12 from VRPirates/beta/RSL-2.23
rsl 2.23
2023-09-06 12:13:45 -05:00
fenopy
51ad390584 chore: version bump 2023-09-06 12:12:11 -05:00
Chax
b4c7795d25 Handle remote write failed 2023-09-01 16:10:39 +02:00
Fenopy
78ef2a0355 Merge pull request #11 from Chax1/fix/UploadCrash
Fix upload crash
2023-08-31 06:14:13 -05:00
Chax
4530424dec fix crash when trying to delete non existant directory and make sure the files exist before attempting to delete them too 2023-08-31 12:37:03 +02:00
Fenopy
3543314ff0 Merge pull request #10 from Chax1/fix/Fullscreen
Fullscreen Fix
2023-08-30 11:32:07 -05:00
fenopy
4ade18bd31 fix: only donate newest versions 2023-08-30 11:18:59 -05:00
Chax
951a899705 Move startup tasks into seperate methods for cleaner code. 2023-08-13 19:27:44 +02:00
Chax
6d678a2e6e Enable maximize button 2023-08-11 19:26:31 +02:00
Chax
8b912e68ee Fix controls not having proper anchor 2023-08-11 19:25:28 +02:00
Fenopy
8059834e91 Merge pull request #8 from VRPirates/beta/RSL-2.22
merge 2.22 changes to master
2023-08-04 07:16:14 -05:00
fenopy
4349603f51 fix: fix double-escape on \\RSL paths 2023-08-04 07:15:34 -05:00
fenopy
160f254e57 chore: update to 2.22 2023-08-04 07:15:29 -05:00
Fenopy
45497396d1 Merge pull request #6 from VRPirates/beta/RSL-2.21
Merge pull request #5 from VRPirates/master
2023-08-04 06:19:11 -05:00
Fenopy
d041ea6b6a Merge pull request #5 from VRPirates/master
beta2.21-master-merge
2023-08-04 06:18:05 -05:00
Fenopy
a2e601ced0 Merge pull request #3 from Chax1/remove/WakeDevice
Remove ADB.WakeDevice due to it being nonfunctional.
2023-08-04 06:15:08 -05:00
Fenopy
35277ca729 Merge pull request #4 from Chax1/fix/changeTitlebarCrash
Make changeTitlebarToDevice less prone to crashes and more robust.
2023-08-04 06:14:38 -05:00
Chax
f3ffe5d037 Make changeTitlebarToDevice less prone to crashes and more robust. 2023-07-28 22:49:49 +02:00
Chax
38866547ec Resolve syntax issue 2023-07-25 20:16:55 +02:00
Fenopy
f4cd9b3b9f Merge pull request #2 from Chax1/fix/NoDeviceModeOutput
Fix No Device Mode output.
2023-07-25 12:53:13 -05:00
Fenopy
e687c3f2ba Merge pull request #1 from Chax1/fix/TrailerControls
Fix trailer controls
2023-07-25 12:53:05 -05:00
Chax
97cbfdea4e Remove ADB.WakeDevice due to redundancy. 2023-07-20 11:57:00 +02:00
Chax
af97e8aef1 Fix No Device Mode having the wrong output message with repeats 2023-07-20 11:44:52 +02:00
Chax
2094ecb0ba Fix trailer controls 2023-07-19 02:14:37 +02:00
fenopy
928436eda3 chore: switch to new git home 2023-07-18 15:16:04 -05:00
fenopy
5b1a7de921 Merge branch 'beta/RSL-2.21' 2023-07-18 14:58:12 -05:00
fenopy
e679f2a1b2 chore: changelog update 2023-07-18 14:57:36 -05:00
Fenopy
9113b376b9 Merge pull request #174 from Chax1/fix/CrashFixes
Fix crash occuring on failed extractions.
2023-07-18 14:51:49 -05:00
Chax
9169ec9b81 Fix crash occuring on failed extractions. 2023-07-14 13:05:19 +02:00
fenopy
41605aabb7 Merge branch 'beta/RSL-2.21' of https://github.com/nerdunit/androidsideloader into beta/RSL-2.21 2023-07-08 14:58:55 -05:00
fenopy
fe56be4ea1 Merge branch 'Chax1-release/RSL-2.21' into beta/RSL-2.21 2023-07-08 14:58:25 -05:00
fenopy
e916ef9b45 Merge branch 'release/RSL-2.21' of https://github.com/Chax1/androidsideloader into Chax1-release/RSL-2.21 2023-07-08 14:57:36 -05:00
Chax
aa51f380b5 Fix downloads 2023-07-08 20:20:04 +02:00
Chax
a9883261da Fix merge conflict 2023-07-08 19:59:30 +02:00
Chax
314a06d43f Attempt to fix merge conflict. 2023-07-08 19:56:03 +02:00
Chax
0992636fa3 Remove additional PCVR Fixes 2023-07-08 19:43:44 +02:00
Chax
184b7c9cbb Remove PCVR 2023-07-08 19:36:18 +02:00
Fenopy
47fd128509 Merge pull request #170 from Chax1/release/RSL-2.21
Fix download mode bugs.
2023-07-07 08:06:14 -05:00
Chax
5f88c09b1c Fix Download Mode Bugs 2023-07-07 14:55:18 +02:00
Fenopy
525c99c0ae Merge branch 'beta/RSL-2.21' of https://github.com/nerdunit/androidsideloader into beta/RSL-2.21 2023-07-06 15:44:29 -05:00
Fenopy
46bcdf0adf chore: update about dialog 2023-07-06 15:31:25 -05:00
Fenopy
4b2a0d7e4e Merge pull request #169 from Chax1/release/RSL-2.21
Fix crash due to Trailers in PCVR Mode
2023-07-06 15:21:02 -05:00
Chax
53f2508941 Fix crash due to Trailers in PCVR Mode 2023-07-06 22:15:51 +02:00
Fenopy
cb0ddaf084 Merge pull request #168 from Chax1/release/RSL-2.21
Update to 2.21
2023-07-06 14:54:25 -05:00
Chax
3dd9b2c694 Update 7z to function on 32 Bit 2023-07-06 21:33:58 +02:00
Chax
8516b14d4a Update to 2.21 2023-07-06 21:27:08 +02:00
Fenopy
cdc8f43a66 Merge pull request #167 from Chax1/beta/RSL-2.21
pr of doom; 2.21 changes
2023-07-06 13:41:53 -05:00
Chax
ce5cf80f96 Change all C:/ paths to {Path.GetPathRoot(Environment.SystemDirectory)}
This commit closes #161 as it's now redundant.
2023-07-05 19:40:05 +02:00
Chax
c7522ccf10 Change to Download w/ instead of Install w/ 2023-06-27 20:45:53 +02:00
Chax
f817cb7be5 Adjust making webView clickable again once disclaimer disappears. 2023-06-27 20:36:37 +02:00
Chax
f22acb4bd6 Hide Quest Mode elements in PCVR Mode 2023-06-27 20:19:57 +02:00
Chax
26d894e02c Add debouncing to search and make it way less clunky. 2023-06-27 20:13:53 +02:00
Chax
32f37cd98a Update PCVR 2023-06-27 19:33:33 +02:00
Chax
66bd13d5d5 Add PCVR 2023-06-27 18:13:15 +02:00
Chax
31e294ae4f Add innerException to CrashLog 2023-06-26 12:29:42 +02:00
Chax
3b771408ae Remove spoofer out of project file 2023-06-25 17:31:02 +02:00
Chax
e55f19c7e5 Update Sideloader Launcher to check for Rookie and tell the user if it doesn't exist 2023-06-25 17:30:36 +02:00
Chax
e24f6ee0e4 Remove bloat methods 2023-06-24 03:23:53 +02:00
Chax
d1e92391d4 Add lime-green color to various states after downloading 2023-06-22 13:48:30 +02:00
Chax
c24829f6a4 Update comments & add comments 2023-06-21 17:42:06 +02:00
Chax
7f9ddb4875 Improve readability on debuglog deletion code 2023-06-21 17:04:24 +02:00
Chax
c644e3e675 Do not delete games files on obbMismatch 2023-06-21 15:37:11 +02:00
Chax
a9864c7fb5 Fix spelling mistake 2023-06-20 17:55:20 +02:00
Chax
2d57e47d55 Remove redundant == true checks.
Signed-off-by: Chax <chaxprivate@gmail.com>
2023-06-20 17:40:49 +02:00
Chax
377e5a6751 Remove redundant changes from Logger.cs rewrite, we may be able to use these in the near future however.
Signed-off-by: Chax <chaxprivate@gmail.com>
2023-06-20 17:39:52 +02:00
Chax
c17cc3f601 Further adjust names to fit naming convention 2023-06-20 17:26:25 +02:00
Chax
8bfd84fa16 Change ShowError_QuotaExceeded() to be more up to date 2023-06-20 16:57:14 +02:00
Chax
629d4c3ee9 Adjust DonorsListView.cs to also take renamed bool 2023-06-20 16:26:03 +02:00
Chax
50284d0eb6 Adjust names to use our naming convention 2023-06-20 16:19:46 +02:00
Chax
6a848513b1 Fix variable name 2023-06-20 15:51:29 +02:00
Chax
5b5d3abd2b Change to string.Empty 2023-06-20 15:33:43 +02:00
Chax
c06d606164 Improve logging system 2023-06-19 01:05:04 +02:00
Chax
6b6fb66a7d Rewrite updater 2023-06-18 21:07:17 +02:00
Chax
80a85f3858 Add --no-rclone-updating flag 2023-06-18 15:52:31 +02:00
Chax
ee5222165c Add a setting to disable file preallocating, this will allow virtual filesystems to be compatible.
This commit fixes #163
2023-06-16 14:43:47 +02:00
Chax
73367c9698 Rewrite entire OBB Comparison. 2023-06-15 18:36:59 +02:00
Chax
ce370d4575 Create a new process for every ADB command, this could prevent Rookie from trying to access exited or inproperly started processes. 2023-06-15 14:27:41 +02:00
Chax
374685eeae Fix webview blocking the freeDisclaimer click. 2023-06-09 20:41:00 +02:00
Chax
3453ca1c5b Add a more apparent message to no device mode output. 2023-06-09 00:25:50 +02:00
Chax
650913f1e8 Remove unecessary spoofer warning. 2023-06-06 08:00:33 +02:00
Chax
07683d2342 Add icons to all forms 2023-05-31 05:52:36 +02:00
Chax
519ec07af8 Change MyHandler to CrashHandler 2023-05-31 05:38:08 +02:00
Chax
d356b68c33 Replace "" with String.Empty 2023-05-31 05:36:31 +02:00
Chax
69f3509c17 Remove uselesss spoofer init out of the MainForm.cs 2023-05-31 05:17:37 +02:00
Chax
23d68ac9cc Remove spoofer, we don't use this anymore. 2023-05-31 05:06:00 +02:00
Chax
d4219bdd3f Optimize ItemChecked code 2023-05-31 05:00:53 +02:00
Chax
ff396081e1 Switch to round buttons in the DonorListView. 2023-05-31 05:00:17 +02:00
Chax
b65df31ff9 Optimize Donors.cs 2023-05-31 04:50:47 +02:00
Chax
c88c2c58a5 Add check for "Only one use of each socket address"
This mainly occurs when there is already an rclone connection towards our servers and will provide the user with steps on how to fix it if there is this issue.
2023-05-31 04:41:01 +02:00
Chax
1d72df573a Add logging to killRclone() catch block 2023-05-31 04:40:01 +02:00
Chax
0f6001b312 Put error we repeat 3 times into a singular method to remove bloated code. 2023-05-30 06:37:04 +02:00
Chax
f9f33336db Fix spelling mistakes. 2023-05-30 06:34:59 +02:00
Chax
8ba8322836 Optimize name conversions. 2023-05-30 06:14:12 +02:00
Chax
1142a67f88 Optimize GetAvailableSpace() 2023-05-30 05:40:44 +02:00
Chax
95eb1d545e Clean up messy code. 2023-05-30 05:25:42 +02:00
Chax
f234bae2a3 Make that stacktrace log slightly more cleaner 2023-05-30 05:20:16 +02:00
Chax
5ea6a48ee1 Remove redundant code 2023-05-30 00:13:18 +02:00
Chax
e6ac747f5d Improve catchblock for OBB Comparison to avoid freezes. 2023-05-29 04:01:58 +02:00
Chax
11e637bcb7 Change search to filter gamelist instead of finding the nearest name 2023-05-28 19:26:11 +02:00
Chax
89935e4f80 Add "click" mouse on filter hover. 2023-05-28 19:26:11 +02:00
Chax
f1f9ec47be Add operator. 2023-05-28 19:26:11 +02:00
Chax
ca69e44f99 Prevent deleting the entire obb/data folders 2023-05-28 19:26:11 +02:00
Chax
820f5d1bb5 Add stack trace to crashlog. 2023-05-28 19:26:11 +02:00
Chax
ee9ed6386d Remove addition operator to remove unecessary repetition. 2023-05-28 19:26:11 +02:00
Fenopy
a97ca0b240 Merge pull request #158 from Chax1/master
Update changelog.txt
2023-05-25 15:06:21 -05:00
Chax
b5dc15cb9a Update changelog.txt 2023-05-25 21:53:41 +02:00
Fenopy
75a41fb028 Merge pull request #157 from nerdunit/beta/RSL-2.20
Release/RSL-2.20
2023-05-25 14:35:13 -05:00
Chax
7cf1baec25 Fix crashing on filtering. 2023-05-24 16:14:18 -05:00
Chax
0763917adf Remove refreshing the gamelist when adding to queue and only refresh it once queue is empty after downloads, also wont refresh if in nodevicemode 2023-05-23 15:27:57 -05:00
Fenopy
2344d7ce51 chore: bump versions 2023-05-23 14:59:42 -05:00
Fenopy
f8724bae66 Merge pull request #156 from Chax1/fix/QualityOfLife
Quality of Life Changes
2023-05-23 12:13:55 -05:00
Chax
b705c56948 Move extraction into a thread to avoid UI Freezes. 2023-05-23 19:09:50 +02:00
Chax
60b9fe5809 Use better code for getting thumbnail paths 2023-05-23 19:09:50 +02:00
Chax
32ea38867a Check for parent process when killing off rclone to avoid killing off unrelated rclone processes. 2023-05-23 19:09:50 +02:00
Chax
dd1044ee3d Optimize Progressbar Code 2023-05-23 19:09:50 +02:00
Chax
7f4e828567 Optimize trailer code slightly 2023-05-23 19:09:50 +02:00
Chax
64ec55ba54 Cleaner code for dropdowns 2023-05-23 19:09:50 +02:00
Chax
39fa784540 Remove redundant standardinput.writeline and flush, we already give ADB the command in " adb.StartInfo.Arguments = command;" 2023-05-23 19:09:49 +02:00
Chax
4a20f2fd89 Check for filter clicks before attempting to reload gamelist. 2023-05-23 19:09:49 +02:00
Chax
1a860399d9 Invoke MessageBox to avoid crashes during debugging. 2023-05-23 19:09:49 +02:00
Chax
502b259b8c Add newlines to "All tasks finished." 2023-05-23 19:09:49 +02:00
Fenopy
c010fda0db fix: storage display for v50 or below 2023-05-19 10:14:16 -05:00
Fenopy
743b9d1d54 Merge pull request #152 from Chax1/beta/RSL-2.20
fix storage display for Android 12; fix to rclone update; added loglevels;
2023-05-17 13:29:04 -05:00
Chax
7506282950 Re-add check for singular queue when removing active download. 2023-05-17 20:10:23 +02:00
Chax
2346accccc Add loglevels to exceptions etc. 2023-05-17 16:49:01 +02:00
Chax
dd69b75c7f Fix storage display 2023-05-17 16:43:49 +02:00
Chax
a71ebaedc3 Add loglevel to logger and change DateTime to UtcNow 2023-05-17 16:23:03 +02:00
Chax
1c85b91f32 Update RCLONE if version doesn't match required. 2023-05-17 16:22:14 +02:00
Fenopy
b130644c47 fix: 1 retry for non-pub configs 2023-05-16 07:26:53 -05:00
Fenopy
0febf9ef0d Merge pull request #151 from Chax1/beta/RSL-2.20
Download newer rclone
2023-05-16 07:25:35 -05:00
Chax
c1e9a5dcfc Download newer rclone 2023-05-16 14:01:25 +02:00
Fenopy
3ddba35295 Merge branch 'beta/RSL-2.20' of https://github.com/nerdunit/androidsideloader into beta/RSL-2.20 2023-05-16 06:49:25 -05:00
Fenopy
2aede4a341 Merge pull request #150 from Chax1/fix/Checkbox
Fix the checkbox inside of Settings
2023-05-16 06:45:14 -05:00
Fenopy
19d6e25f06 Merge pull request #149 from Chax1/fix/Crash
fix/Crash
2023-05-16 06:44:55 -05:00
Fenopy
0b5d3bfddd Merge pull request #148 from Chax1/fix/Queue
fix/Queue
2023-05-16 06:44:44 -05:00
Fenopy
823c2607d9 fix: settings dialog display adjustments 2023-05-16 06:16:13 -05:00
Fenopy
d8bbac3096 Merge pull request #140 from Chax1/fix/CheckInternet
Removal of HasInternet due to redundancy
2023-05-16 06:08:28 -05:00
Fenopy
8af2b7dd66 Merge pull request #144 from Chax1/beta/RSL-2.20
Give users the ability to enter an IP manually to connect via ADB over WiFi
2023-05-16 06:07:06 -05:00
Chax
d480f13024 Add a check for devices for install.txts 2023-05-04 15:05:33 +02:00
Chax
40a02cc8e0 Cleanup code 2023-05-03 20:45:28 +02:00
Chax
505c145794 Cleanup code. 2023-05-03 20:42:19 +02:00
Chax
79648f2e07 Possibly fix the crash upon trying to remove current game in queue when it does not exist in the queue anymore. 2023-05-03 14:09:43 +02:00
Chax
02b03328d1 Fix "Serving remote control" catch, which causes Rookie to not extract games on first download apparently. 2023-04-29 15:57:18 +02:00
Chax
34046ecb4b Fix the checkbox inside of Settings 2023-04-19 02:13:11 +02:00
Chax
6ecba949c2 Delete old OBB folders after download to make sure that no space gets wasted on the quest. 2023-04-18 23:25:19 +02:00
Chax
3edadfa475 Give users the ability to enter an IP manually to connect via ADB over WiFi, also add border to ADB Box. 2023-04-17 20:24:06 +02:00
Chax
99aab8bc2e Merge branch 'beta/RSL-2.20' of https://github.com/nerdunit/androidsideloader into beta/RSL-2.20 2023-04-17 19:19:02 +02:00
Chax
0c52656a5e Fix displaying storage with a connected device, the MainForm.nodeviceonstart was interrupting displaying storage, should only check for when a device is connected, which it does above this method. 2023-04-12 09:53:22 -05:00
Chax
1e0be54c43 Fix displaying storage with a connected device, the MainForm.nodeviceonstart was interrupting displaying storage, should only check for when a device is connected, which it does above this method. 2023-04-01 23:07:41 +02:00
Chax
bf91c1f9f3 Possibly fix one crash, add single threaded mode for downloads to help compatibility wise, and 2023-03-28 03:56:11 +02:00
Chax
3314228b9c Remove redundant Internet Check for Rookie, Rookie already checks for the internet by attempting to download dependencies on start, which will fail if no internet exists and therefore not let Rookie launch. 2023-03-25 17:48:26 +01:00
Fenopy
8c26242af7 chg: changelog update 2023-03-24 13:25:05 -05:00
Fenopy
a75bd355cf feat: allow donation-uploads while in offline-mode 2023-03-23 16:18:46 -05:00
Chax
ea19b936cb Added a check for devices before attempting to get available space of devices to hopefully avoid crashes 2023-03-20 11:33:35 -05:00
Chax
3dbd5cee3a Remove bandwidth limit, rearrange settings, make nodevicemode disable delete after download and install 2023-03-20 11:02:06 -05:00
Chax
07c312fa33 fix obb still pushing with nodevicemode 2023-03-20 11:01:57 -05:00
Chax
8d15dcb5c9 Properly handle the error now. 2023-03-20 11:01:09 -05:00
Fenopy
fd9d2bfccc Version Bump: 2.19 2023-03-08 10:30:56 -06:00
Chax
3497270b26 Hopefully fix crash on filtering. 2023-03-08 09:53:30 -06:00
Chax
1f505e1a28 Fix pictureBox 2023-03-08 09:33:38 -06:00
Fenopy
96758aba7e fix: labels when filtering gamelist 2023-03-08 09:32:59 -06:00
Fenopy
b538244b6f Merge remote-tracking branch 'chax/development/fixing' into beta/RSL-2.19 2023-03-08 08:45:17 -06:00
Chax
04e7e54556 custom backup directory, wee. 2023-01-19 22:15:30 +01:00
Chax
372002dfa5 Add Filtering to all game types (top right) 2023-01-13 15:07:52 +01:00
Chax
401b21b715 Revert "Filter update available."
This reverts commit d73f84dbb4.
2023-01-13 14:40:10 +01:00
Chax
e09d1fec5d Revert "Remove all of the uneccessary code and make it switch back more than once lol."
This reverts commit c060038e50.
2023-01-13 14:37:30 +01:00
Chax
2e8ab3bd43 Change messagebox to show raw.githubusercontent.com instead of just GitHub 2023-01-12 16:36:04 -06:00
Chax
c060038e50 Remove all of the uneccessary code and make it switch back more than once lol. 2023-01-10 16:04:35 +01:00
Chax
d73f84dbb4 Filter update available. 2023-01-10 15:53:23 +01:00
Chax
3e69119df2 Prevent issues with extracting due to Environment.CurrentDirectory not being useable inside of Application Settings, Rookie will now check if a boolean is set to true or false (by default it is false), when it is false it will set the downloadDir to its currentDirectory, otherwise it will use a set custom one. 2023-01-06 09:16:04 -06:00
Chax
a9e11e3734 Download runtimes 2023-01-05 13:41:11 -06:00
Fenopy
ca742146d9 gitignore cleanup 2023-01-05 12:33:28 -06:00
Fenopy
de47ab6ad3 Remove Packages folder 2023-01-05 11:50:01 -06:00
Fenopy
9cff4a32b2 Checks for Device States before sideloading and pushing 2023-01-05 10:57:24 -06:00
Chax
1744ef86bf Hopefully fixes the "Set Download Directory" 2023-01-05 09:56:13 -06:00
Fenopy
46b3bafeb7 Merge remote-tracking branch 'origin/master' into beta/RSL-2.19 2023-01-05 08:52:20 -06:00
Chax
76b372f822 Comparing OBBs with no Device should not cause an exception anymore. 2023-01-05 08:50:06 -06:00
Chax
782485edd0 Make the settings reset button ressent the downloadDir as Default.Reset didnt seem to do so. 2023-01-05 08:46:58 -06:00
Chax
2bfb402c25 Hide Webview if Trailers arent enabled. 2023-01-04 15:44:47 -06:00
Fenopy
13e1866820 Properly exit out of Rookie when needed files were unable to be downloaded and catched. 2023-01-04 14:41:34 -06:00
Chax
5fbe50b2f9 added Custom Download Directories 2023-01-04 09:12:24 -06:00
Fenopy
d747596ce1 Merge remote-tracking branch 'chax/Trailers' into beta/RSL-2.19 2023-01-04 09:08:06 -06:00
Fenopy
696fff3f5d change comparison operator 2023-01-04 09:07:42 -06:00
Chax
87108d5640 Change TaskResult to just a normal return. 2023-01-04 09:07:27 -06:00
Chax
f48d2ef40d Seperate backing up game with uninstall game and create seperate method, ask the user if they want to backup and only backup if they actually say yes. 2023-01-04 09:04:54 -06:00
Chax
df2e35d683 This should hopefully fix the fullscreen mode not just like, revealing a little of the mainform. 2022-12-24 06:29:44 +01:00
Chax
19074465a0 Merge branch 'Trailers' of https://github.com/Chax1/androidsideloader into Trailers 2022-12-24 02:26:15 +01:00
Chax
7d8e143963 Ultimate Fixes, this now deletes the webview enviroment folder on startup if one exists to keep space low, the cache gets deleted on every new selection of a game and the enviroment creation only happens once due to a bool having to be set. 2022-12-24 02:25:55 +01:00
Chax
48c004794c this is not supposed to be here. 2022-12-24 01:19:48 +01:00
Chax
b011b3e7a2 more fixing 2022-12-24 01:19:09 +01:00
Chax
43daff617a Clear cache as soon as new game is listed. 2022-12-24 01:15:24 +01:00
Chax
ef6a1dc5a4 Trailers, kill me. 2022-12-23 22:27:12 +01:00
130 changed files with 22425 additions and 51566 deletions

21
.gitignore vendored
View File

@@ -1,21 +1,8 @@
################################################################################
# This .gitignore file was automatically created by Microsoft(R) Visual Studio.
################################################################################
*.pdb
*.xml
*.xsd
bin/
adb/
rclone/
thumbnails/
notes/
AndroidSideloader.csproj.user
.vs/
/.vs/AndroidSideloader/v16/.suo
/adb/adb.exe
installedPackages.json
AndroidSideloader.csproj.user
AndroidSideloader.csproj.user
AndroidSideloader.csproj.user
/AndroidSideloader v2.1.exe
/crashlog.txt
/debuglog.txt
bin/
packages/

BIN
7z.dll

Binary file not shown.

BIN
7z.exe

Binary file not shown.

BIN
7z64.dll Normal file

Binary file not shown.

BIN
7z64.exe Normal file

Binary file not shown.

978
ADB.cs

File diff suppressed because it is too large Load Diff

BIN
Ad.7z

Binary file not shown.

244
AdbCommandForm.cs Normal file
View File

@@ -0,0 +1,244 @@
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace AndroidSideloader
{
public partial class AdbCommandForm : Form
{
public string Command { get; private set; }
public bool ToggleUpdatesClicked { get; private set; }
public AdbCommandForm()
{
InitializeComponent();
// Use same icon as the executable
this.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
this.ShowIcon = true; // Enable icon
}
private void InitializeComponent()
{
this.lblTitle = new Label();
this.txtCommand = new TextBox();
this.btnSend = new RoundButton();
this.btnToggleUpdates = new RoundButton();
this.btnClose = new RoundButton();
this.separator = new Panel();
this.lblHint = new Label();
this.SuspendLayout();
//
// lblTitle
//
this.lblTitle.AutoSize = true;
this.lblTitle.Font = new Font("Segoe UI", 11F, FontStyle.Bold);
this.lblTitle.ForeColor = Color.FromArgb(93, 203, 173);
this.lblTitle.Location = new Point(20, 15);
this.lblTitle.Name = "lblTitle";
this.lblTitle.Size = new Size(140, 20);
this.lblTitle.TabIndex = 0;
this.lblTitle.Text = "Run ADB Command";
//
// txtCommand
//
this.txtCommand.BackColor = Color.FromArgb(40, 44, 52);
this.txtCommand.BorderStyle = BorderStyle.FixedSingle;
this.txtCommand.Font = new Font("Consolas", 10F);
this.txtCommand.ForeColor = Color.White;
this.txtCommand.Location = new Point(24, 50);
this.txtCommand.Name = "txtCommand";
this.txtCommand.Size = new Size(292, 23);
this.txtCommand.TabIndex = 1;
this.txtCommand.KeyPress += TxtCommand_KeyPress;
//
// lblHint
//
this.lblHint.AutoSize = true;
this.lblHint.Font = new Font("Segoe UI", 8F);
this.lblHint.ForeColor = Color.FromArgb(120, 120, 120);
this.lblHint.Location = new Point(24, 78);
this.lblHint.Name = "lblHint";
this.lblHint.Size = new Size(200, 13);
this.lblHint.TabIndex = 2;
this.lblHint.Text = "Enter command without \"adb\" prefix";
//
// separator
//
this.separator.BackColor = Color.FromArgb(50, 55, 65);
this.separator.Location = new Point(20, 105);
this.separator.Name = "separator";
this.separator.Size = new Size(300, 1);
this.separator.TabIndex = 3;
//
// btnSend
//
this.btnSend.Active1 = Color.FromArgb(113, 223, 193);
this.btnSend.Active2 = Color.FromArgb(113, 223, 193);
this.btnSend.BackColor = Color.Transparent;
this.btnSend.Cursor = Cursors.Hand;
this.btnSend.DialogResult = DialogResult.OK;
this.btnSend.Disabled1 = Color.FromArgb(32, 35, 45);
this.btnSend.Disabled2 = Color.FromArgb(25, 28, 35);
this.btnSend.DisabledStrokeColor = Color.FromArgb(50, 55, 65);
this.btnSend.Font = new Font("Segoe UI", 9F, FontStyle.Bold);
this.btnSend.ForeColor = Color.FromArgb(20, 24, 29);
this.btnSend.Inactive1 = Color.FromArgb(93, 203, 173);
this.btnSend.Inactive2 = Color.FromArgb(93, 203, 173);
this.btnSend.Location = new Point(24, 120);
this.btnSend.Name = "btnSend";
this.btnSend.Radius = 5;
this.btnSend.Size = new Size(140, 30);
this.btnSend.Stroke = false;
this.btnSend.StrokeColor = Color.FromArgb(93, 203, 173);
this.btnSend.TabIndex = 4;
this.btnSend.Text = "SEND COMMAND";
this.btnSend.Transparency = false;
this.btnSend.Click += BtnSend_Click;
//
// btnToggleUpdates
//
this.btnToggleUpdates.Active1 = Color.FromArgb(50, 55, 65);
this.btnToggleUpdates.Active2 = Color.FromArgb(50, 55, 65);
this.btnToggleUpdates.BackColor = Color.Transparent;
this.btnToggleUpdates.Cursor = Cursors.Hand;
this.btnToggleUpdates.DialogResult = DialogResult.None;
this.btnToggleUpdates.Disabled1 = Color.FromArgb(32, 35, 45);
this.btnToggleUpdates.Disabled2 = Color.FromArgb(25, 28, 35);
this.btnToggleUpdates.DisabledStrokeColor = Color.FromArgb(50, 55, 65);
this.btnToggleUpdates.Font = new Font("Segoe UI", 9F);
this.btnToggleUpdates.ForeColor = Color.White;
this.btnToggleUpdates.Inactive1 = Color.FromArgb(40, 44, 52);
this.btnToggleUpdates.Inactive2 = Color.FromArgb(40, 44, 52);
this.btnToggleUpdates.Location = new Point(176, 120);
this.btnToggleUpdates.Name = "btnToggleUpdates";
this.btnToggleUpdates.Radius = 5;
this.btnToggleUpdates.Size = new Size(140, 30);
this.btnToggleUpdates.Stroke = true;
this.btnToggleUpdates.StrokeColor = Color.FromArgb(60, 65, 75);
this.btnToggleUpdates.TabIndex = 5;
this.btnToggleUpdates.Text = "Toggle OS Updates";
this.btnToggleUpdates.Transparency = false;
this.btnToggleUpdates.Click += BtnToggleUpdates_Click;
//
// btnClose
//
this.btnClose.Active1 = Color.FromArgb(60, 65, 75);
this.btnClose.Active2 = Color.FromArgb(60, 65, 75);
this.btnClose.BackColor = Color.Transparent;
this.btnClose.Cursor = Cursors.Hand;
this.btnClose.DialogResult = DialogResult.Cancel;
this.btnClose.Disabled1 = Color.FromArgb(32, 35, 45);
this.btnClose.Disabled2 = Color.FromArgb(25, 28, 35);
this.btnClose.DisabledStrokeColor = Color.FromArgb(50, 55, 65);
this.btnClose.Font = new Font("Segoe UI", 9F);
this.btnClose.ForeColor = Color.White;
this.btnClose.Inactive1 = Color.FromArgb(50, 55, 65);
this.btnClose.Inactive2 = Color.FromArgb(50, 55, 65);
this.btnClose.Location = new Point(24, 160);
this.btnClose.Name = "btnClose";
this.btnClose.Radius = 5;
this.btnClose.Size = new Size(292, 30);
this.btnClose.Stroke = true;
this.btnClose.StrokeColor = Color.FromArgb(74, 74, 74);
this.btnClose.TabIndex = 6;
this.btnClose.Text = "Close";
this.btnClose.Transparency = false;
this.btnClose.Click += BtnClose_Click;
//
// AdbCommandForm
//
this.AcceptButton = this.btnSend;
this.AutoScaleDimensions = new SizeF(6F, 13F);
this.AutoScaleMode = AutoScaleMode.Font;
this.BackColor = Color.FromArgb(20, 24, 29);
this.CancelButton = this.btnClose;
this.ClientSize = new Size(340, 210);
this.Controls.Add(this.lblTitle);
this.Controls.Add(this.txtCommand);
this.Controls.Add(this.lblHint);
this.Controls.Add(this.separator);
this.Controls.Add(this.btnSend);
this.Controls.Add(this.btnToggleUpdates);
this.Controls.Add(this.btnClose);
this.FormBorderStyle = FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "AdbCommandForm";
this.ShowIcon = false;
this.StartPosition = FormStartPosition.CenterParent;
this.Text = "ADB Command";
this.Load += AdbCommandForm_Load;
this.ResumeLayout(false);
this.PerformLayout();
}
private void AdbCommandForm_Load(object sender, EventArgs e)
{
txtCommand.Focus();
}
private void TxtCommand_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Enter)
{
e.Handled = true;
BtnSend_Click(sender, e);
}
else if (e.KeyChar == (char)Keys.Escape)
{
e.Handled = true;
this.DialogResult = DialogResult.Cancel;
this.Close();
}
}
private void BtnSend_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(txtCommand.Text))
{
return;
}
Command = txtCommand.Text;
ToggleUpdatesClicked = false;
this.DialogResult = DialogResult.OK;
this.Close();
}
private void BtnToggleUpdates_Click(object sender, EventArgs e)
{
// Check current state and set the appropriate command
string adbResult = ADB.RunAdbCommandToString("shell pm list packages -d").Output;
bool isUpdatesDisabled = adbResult.Contains("com.oculus.updater");
if (isUpdatesDisabled)
{
Command = "shell pm enable com.oculus.updater";
}
else
{
Command = "shell pm disable-user --user 0 com.oculus.updater";
}
ToggleUpdatesClicked = true;
this.DialogResult = DialogResult.OK;
this.Close();
}
private void BtnClose_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
private Label lblTitle;
private TextBox txtCommand;
private Label lblHint;
private Panel separator;
private RoundButton btnSend;
private RoundButton btnToggleUpdates;
private RoundButton btnClose;
}
}

72
AddDefenderExceptions.ps1 Normal file
View File

@@ -0,0 +1,72 @@
# Run this script as Administrator
# powershell -ExecutionPolicy Bypass -File "C:\RSL\Rookie\AddDefenderExceptions.ps1"
################################################################
## Auto Elevate to Admin if not running as admin
################################################################
# Get the ID and security principal of the current user account
$WindowsID = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$WindowsPrincipal = New-Object System.Security.Principal.WindowsPrincipal ($WindowsID)
# Get the security principal for the Administrator role
$AdminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator
# Check to see if we are currently running "as Administrator"
if ($WindowsPrincipal.IsInRole($AdminRole)) {
# We are running "as Administrator" - so change the title and background color to indicate this
$Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + " (Elevated)"
$Host.UI.RawUI.BackgroundColor = "DarkBlue"
Clear-Host
} else {
# We are not running "as Administrator" - so relaunch as administrator
# Create a new process object that starts PowerShell
$NewProcess = New-Object System.Diagnostics.ProcessStartInfo "PowerShell";
# Specify the current script path and name as a parameter
$NewProcess.Arguments = $myInvocation.MyCommand.Definition;
# Indicate that the process should be elevated
$NewProcess.Verb = "runas";
# Start the new process
[System.Diagnostics.Process]::Start($NewProcess);
# Exit from the current unelevated process
exit
}
write-host "Run this script from the directory root with which Rookie will be run"
start-sleep -s 5
$paths = @(
"$PSScriptRoot", # Replaces 'C:\RSL' with the script's root directory
"$PSScriptRoot\rclone",
"$PSScriptRoot\Sideloader Launcher.exe",
"$PSScriptRoot\AndroidSideloader*.exe",
"$PSScriptRoot\rclone\rclone.exe"
)
foreach ($path in $paths) {
try {
Add-MpPreference -ExclusionPath $Path -ErrorAction Stop
Write-Host "Successfully added exclusion for: $path" -ForegroundColor Green
}
catch {
Write-Host "Failed to add exclusion for: $path " -ForegroundColor Red
Write-Host "Error: $_" -ForegroundColor Red
Start-Sleep -s 5
Pause
}
}
# Verify the exclusions
Write-Host "`nCurrent exclusions:" -ForegroundColor Cyan
$defenderPreferences = Get-MpPreference
$paths | ForEach-Object {
if ($defenderPreferences.ExclusionPath -contains $_) {
Write-Host "$_ is already excluded from Defender."
} else {
Write-Host "$_ is NOT excluded from Defender."
}
}
Pause
Start-Sleep -s 5

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="packages\Costura.Fody.4.1.0\build\Costura.Fody.props" Condition="Exists('packages\Costura.Fody.4.1.0\build\Costura.Fody.props')" />
<Import Project="packages\Costura.Fody.5.7.0\build\Costura.Fody.props" Condition="Exists('packages\Costura.Fody.5.7.0\build\Costura.Fody.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -49,14 +49,14 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>x64</PlatformTarget>
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>icon.ico</ApplicationIcon>
@@ -136,25 +136,47 @@
<ManifestKeyFile>AndroidSideloader_TemporaryKey.pfx</ManifestKeyFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="Costura, Version=4.1.0.0, Culture=neutral, PublicKeyToken=9919ef960d84173d, processorArchitecture=MSIL">
<HintPath>packages\Costura.Fody.4.1.0\lib\net40\Costura.dll</HintPath>
<Reference Include="AdvancedSharpAdbClient, Version=3.5.15.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\AdvancedSharpAdbClient.3.5.15\lib\net45\AdvancedSharpAdbClient.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<Reference Include="Costura, Version=5.7.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\Costura.Fody.5.7.0\lib\netstandard1.0\Costura.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Web.WebView2.Core, Version=1.0.2478.35, Culture=neutral, PublicKeyToken=2a8ab48044d2601e, processorArchitecture=MSIL">
<HintPath>packages\Microsoft.Web.WebView2.1.0.2478.35\lib\net45\Microsoft.Web.WebView2.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Web.WebView2.WinForms, Version=1.0.2478.35, Culture=neutral, PublicKeyToken=2a8ab48044d2601e, processorArchitecture=MSIL">
<HintPath>packages\Microsoft.Web.WebView2.1.0.2478.35\lib\net45\Microsoft.Web.WebView2.WinForms.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Web.WebView2.Wpf, Version=1.0.2478.35, Culture=neutral, PublicKeyToken=2a8ab48044d2601e, processorArchitecture=MSIL">
<HintPath>packages\Microsoft.Web.WebView2.1.0.2478.35\lib\net45\Microsoft.Web.WebView2.Wpf.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="SergeUtils">
<HintPath>.\SergeUtils.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System">
<HintPath>..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2\System.dll</HintPath>
</Reference>
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Core" />
<Reference Include="System.DirectoryServices" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Management" />
<Reference Include="System.Numerics" />
<Reference Include="System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Drawing">
<HintPath>..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2\System.Drawing.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
@@ -162,6 +184,24 @@
</ItemGroup>
<ItemGroup>
<Compile Include="ADB.cs" />
<Compile Include="AdbCommandForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="GalleryView.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="ModernListView.cs" />
<Compile Include="ModernProgessBar.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="ModernQueuePanel.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="RoundButton.cs">
<SubType>Component</SubType>
</Compile>
@@ -175,12 +215,8 @@
</Compile>
<Compile Include="FlexibleMessageBox.cs" />
<Compile Include="RoundedRectangleF.cs" />
<Compile Include="Splash.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Splash.Designer.cs">
<DependentUpon>Splash.cs</DependentUpon>
</Compile>
<Compile Include="Settings.cs" />
<Compile Include="Sideloader\GetDependencies.cs" />
<Compile Include="Models\PublicConfig.cs" />
<Compile Include="NewApps.cs">
<SubType>Form</SubType>
@@ -188,6 +224,9 @@
<Compile Include="NewApps.Designer.cs">
<DependentUpon>NewApps.cs</DependentUpon>
</Compile>
<Compile Include="ToggleSwitch.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Transparenter.cs" />
<Compile Include="UpdateForm.cs">
<SubType>Form</SubType>
@@ -204,6 +243,8 @@
<Compile Include="Sideloader\ProcessOutput.cs" />
<Compile Include="Sideloader\RCLONE.cs" />
<Compile Include="Sideloader\Utilities.cs" />
<Compile Include="Utilities\DnsHelper.cs" />
<Compile Include="Utilities\FileSystemUtilities.cs" />
<Compile Include="Utilities\Logger.cs" />
<Compile Include="QuestForm.cs">
<SubType>Form</SubType>
@@ -219,13 +260,6 @@
<DependentUpon>SettingsForm.cs</DependentUpon>
</Compile>
<Compile Include="Sideloader.cs" />
<Compile Include="spoofer.cs" />
<Compile Include="SpoofForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="SpoofForm.Designer.cs">
<DependentUpon>SpoofForm.cs</DependentUpon>
</Compile>
<Compile Include="Updater.cs" />
<Compile Include="UsernameForm.cs">
<SubType>Form</SubType>
@@ -236,17 +270,16 @@
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SelectFolder.cs" />
<Compile Include="Utilities\SettingsManager.cs" />
<Compile Include="Utilities\StringUtilities.cs" />
<Compile Include="Utilities\GeneralUtilities.cs" />
<Compile Include="Utilities\UpdateGameData.cs" />
<Compile Include="Utilities\UploadGame.cs" />
<Compile Include="Utilities\Zip.cs" />
<Compile Include="Utilities\Metrics.cs" />
<EmbeddedResource Include="DonorsListView.resx">
<DependentUpon>DonorsListView.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Splash.resx">
<DependentUpon>Splash.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="NewApps.resx">
<DependentUpon>NewApps.cs</DependentUpon>
</EmbeddedResource>
@@ -259,14 +292,9 @@
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<EmbeddedResource Include="QuestForm.resx">
<DependentUpon>QuestForm.cs</DependentUpon>
<SubType>Designer</SubType>
@@ -275,13 +303,11 @@
<DependentUpon>SettingsForm.cs</DependentUpon>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="SpoofForm.resx">
<DependentUpon>SpoofForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="UsernameForm.resx">
<DependentUpon>UsernameForm.cs</DependentUpon>
<SubType>Designer</SubType>
</EmbeddedResource>
<None Include="codenames" />
<None Include="packages.config" />
<None Include="Properties\DataSources\AndroidSideloader.Properties.Resources.datasource" />
<None Include="Properties\DataSources\AndroidSideloader.Properties.Settings.datasource" />
@@ -297,6 +323,12 @@
<None Include="Rookie Offline.cmd">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="CleanupInstall.cmd">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="AddDefenderExceptions.ps1">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="version" />
</ItemGroup>
<ItemGroup>
@@ -306,12 +338,7 @@
<Content Include="changelog.txt" />
<Content Include="icon.ico" />
<Content Include="ChangelogHistory.txt" />
<Content Include="Resources\pattern_cubes-1_1_1_0-0_0_1__000000_212121.png" />
<Content Include="Resources\pattern_herringbone-2_1_3_0-0_90_1__000000_1c1c1c.png" />
<Content Include="Resources\splash.jpg" />
<None Include="Resources\battery11.png" />
<None Include="Resources\battery.png" />
<None Include="Resources\battery1.png" />
<None Include="Resources\ajax-loader.gif" />
<None Include="Resources\SearchGlass.PNG" />
</ItemGroup>
@@ -329,13 +356,14 @@
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="packages\Fody.6.0.0\build\Fody.targets" Condition="Exists('packages\Fody.6.0.0\build\Fody.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('packages\Fody.6.0.0\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Fody.6.0.0\build\Fody.targets'))" />
<Error Condition="!Exists('packages\Costura.Fody.4.1.0\build\Costura.Fody.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Costura.Fody.4.1.0\build\Costura.Fody.props'))" />
<Error Condition="!Exists('packages\Microsoft.Web.WebView2.1.0.2478.35\build\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Web.WebView2.1.0.2478.35\build\Microsoft.Web.WebView2.targets'))" />
<Error Condition="!Exists('packages\Fody.6.8.1\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Fody.6.8.1\build\Fody.targets'))" />
<Error Condition="!Exists('packages\Costura.Fody.5.7.0\build\Costura.Fody.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Costura.Fody.5.7.0\build\Costura.Fody.props'))" />
<Error Condition="!Exists('packages\Costura.Fody.5.7.0\build\Costura.Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Costura.Fody.5.7.0\build\Costura.Fody.targets'))" />
</Target>
<PropertyGroup>
<PreBuildEvent>
@@ -345,4 +373,7 @@
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
<Import Project="packages\Microsoft.Web.WebView2.1.0.2478.35\build\Microsoft.Web.WebView2.targets" Condition="Exists('packages\Microsoft.Web.WebView2.1.0.2478.35\build\Microsoft.Web.WebView2.targets')" />
<Import Project="packages\Fody.6.8.1\build\Fody.targets" Condition="Exists('packages\Fody.6.8.1\build\Fody.targets')" />
<Import Project="packages\Costura.Fody.5.7.0\build\Costura.Fody.targets" Condition="Exists('packages\Costura.Fody.5.7.0\build\Costura.Fody.targets')" />
</Project>

View File

@@ -1,252 +1,267 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<section name="AndroidSideloader.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false"/>
<section name="AndroidADB.Sideloader.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false"/>
</sectionGroup>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/>
</startup>
<userSettings>
<AndroidSideloader.Properties.Settings>
<setting name="checkForUpdates" serializeAs="String">
<value>True</value>
</setting>
<setting name="enableMessageBoxes" serializeAs="String">
<value>True</value>
</setting>
<setting name="firstRun" serializeAs="String">
<value>True</value>
</setting>
<setting name="deleteAllAfterInstall" serializeAs="String">
<value>True</value>
</setting>
<setting name="autoUpdateConfig" serializeAs="String">
<value>True</value>
</setting>
<setting name="userJsonOnGameInstall" serializeAs="String">
<value>False</value>
</setting>
<setting name="CallUpgrade" serializeAs="String">
<value>True</value>
</setting>
<setting name="FontStyle" serializeAs="String">
<value>Microsoft Sans Serif, 11.25pt</value>
</setting>
<setting name="BackPicturePath" serializeAs="String">
<value/>
</setting>
<setting name="SpoofGames" serializeAs="String">
<value>False</value>
</setting>
<setting name="BandwidthLimit" serializeAs="String">
<value/>
</setting>
<setting name="BigFontStyle" serializeAs="String">
<value>Microsoft Sans Serif, 14pt</value>
</setting>
<setting name="ResignAPKs" serializeAs="String">
<value>False</value>
</setting>
<setting name="IPAddress" serializeAs="String">
<value/>
</setting>
<setting name="InstalledApps" serializeAs="String">
<value/>
</setting>
<setting name="ADBPath" serializeAs="String">
<value/>
</setting>
<setting name="QUsett" serializeAs="String">
<value>False</value>
</setting>
<setting name="QuChecked" serializeAs="String">
<value/>
</setting>
<setting name="QUhz" serializeAs="String">
<value>0</value>
</setting>
<setting name="QUres" serializeAs="String">
<value>0</value>
</setting>
<setting name="QUy" serializeAs="String">
<value>0</value>
</setting>
<setting name="QUx" serializeAs="String">
<value>0</value>
</setting>
<setting name="QUname" serializeAs="String">
<value>Change Me</value>
</setting>
<setting name="QUString" serializeAs="String">
<value/>
</setting>
<setting name="MainDir" serializeAs="String">
<value/>
</setting>
<setting name="QUStringF" serializeAs="String">
<value/>
</setting>
<setting name="delsh" serializeAs="String">
<value>False</value>
</setting>
<setting name="CurrPckg" serializeAs="String">
<value/>
</setting>
<setting name="ADBFolder" serializeAs="String">
<value/>
</setting>
<setting name="WirelessADB" serializeAs="String">
<value>False</value>
</setting>
<setting name="CurrentGamename" serializeAs="String">
<value/>
</setting>
<setting name="PackageNameToCB" serializeAs="String">
<value>False</value>
</setting>
<setting name="EnterKeyInstall" serializeAs="String">
<value>False</value>
</setting>
<setting name="DownUpHeld" serializeAs="String">
<value>False</value>
</setting>
<setting name="CurrentLogPath" serializeAs="String">
<value/>
</setting>
<setting name="CurrentLogName" serializeAs="String">
<value/>
</setting>
<setting name="CurrentCrashPath" serializeAs="String">
<value/>
</setting>
<setting name="CurrentCrashName" serializeAs="String">
<value/>
</setting>
<setting name="QUturnedon" serializeAs="String">
<value>False</value>
</setting>
<setting name="adbdebugwarned" serializeAs="String">
<value>False</value>
</setting>
<setting name="nodevicemode" serializeAs="String">
<value>False</value>
</setting>
<setting name="BMBFchecked" serializeAs="String">
<value>True</value>
</setting>
<setting name="GamesList" serializeAs="String">
<value/>
</setting>
<setting name="UploadedGameList" serializeAs="String">
<value>False</value>
</setting>
<setting name="GlobalUsername" serializeAs="String">
<value/>
</setting>
<setting name="lastTimeShared" serializeAs="String">
<value/>
</setting>
<setting name="AutoReinstall" serializeAs="String">
<value>False</value>
</setting>
<setting name="NonAppPackages" serializeAs="String">
<value/>
</setting>
<setting name="LastLaunch" serializeAs="String">
<value>04/20/1969 16:20:00</value>
</setting>
<setting name="SubmittedUpdates" serializeAs="String">
<value/>
</setting>
<setting name="ListUpped" serializeAs="String">
<value>False</value>
</setting>
<setting name="LastLaunch2" serializeAs="String">
<value>04/20/1969 16:20:00</value>
</setting>
<setting name="Wired" serializeAs="String">
<value>False</value>
</setting>
<setting name="FontColor" serializeAs="String">
<value>White</value>
</setting>
<setting name="ComboBoxColor" serializeAs="String">
<value>50, 50, 50</value>
</setting>
<setting name="SubButtonColor" serializeAs="String">
<value>40, 40, 40</value>
</setting>
<setting name="TextBoxColor" serializeAs="String">
<value>29, 29, 29</value>
</setting>
<setting name="ButtonColor" serializeAs="String">
<value>50, 50, 50</value>
</setting>
<setting name="BackColor" serializeAs="String">
<value>50, 50, 50</value>
</setting>
<setting name="AppPackages" serializeAs="String">
<value/>
</setting>
</AndroidSideloader.Properties.Settings>
<AndroidADB.Sideloader.Properties.Settings>
<setting name="checkForUpdates" serializeAs="String">
<value>True</value>
</setting>
<setting name="enableMessageBoxes" serializeAs="String">
<value>True</value>
</setting>
<setting name="logRclone" serializeAs="String">
<value>False</value>
</setting>
<setting name="firstRun" serializeAs="String">
<value>True</value>
</setting>
<setting name="deleteAllAfterInstall" serializeAs="String">
<value>True</value>
</setting>
<setting name="autoUpdateConfig" serializeAs="String">
<value>True</value>
</setting>
<setting name="userJsonOnGameInstall" serializeAs="String">
<value>True</value>
</setting>
<setting name="CallUpgrade" serializeAs="String">
<value>True</value>
</setting>
<setting name="BackColor" serializeAs="String">
<value>45, 45, 45</value>
</setting>
<setting name="PanelColor" serializeAs="String">
<value>ActiveCaptionText</value>
</setting>
<setting name="ButtonColor" serializeAs="String">
<value>ActiveCaptionText</value>
</setting>
<setting name="SubButtonColor" serializeAs="String">
<value>64, 64, 64</value>
</setting>
<setting name="TextBoxColor" serializeAs="String">
<value>45, 45, 45</value>
</setting>
<setting name="ComboBoxColor" serializeAs="String">
<value>45, 45, 45</value>
</setting>
<setting name="FontColor" serializeAs="String">
<value>White</value>
</setting>
<setting name="FontStyle" serializeAs="String">
<value>Microsoft Sans Serif, 11.25pt</value>
</setting>
<setting name="BackPicturePath" serializeAs="String">
<value/>
</setting>
<setting name="SpoofGames" serializeAs="String">
<value>False</value>
</setting>
</AndroidADB.Sideloader.Properties.Settings>
</userSettings>
</configuration>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<section name="AndroidSideloader.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false"/>
<section name="AndroidADB.Sideloader.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false"/>
</sectionGroup>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/>
</startup>
<userSettings>
<AndroidSideloader.Properties.Settings>
<setting name="checkForUpdates" serializeAs="String">
<value>True</value>
</setting>
<setting name="enableMessageBoxes" serializeAs="String">
<value>True</value>
</setting>
<setting name="firstRun" serializeAs="String">
<value>True</value>
</setting>
<setting name="deleteAllAfterInstall" serializeAs="String">
<value>True</value>
</setting>
<setting name="autoUpdateConfig" serializeAs="String">
<value>True</value>
</setting>
<setting name="userJsonOnGameInstall" serializeAs="String">
<value>False</value>
</setting>
<setting name="CallUpgrade" serializeAs="String">
<value>True</value>
</setting>
<setting name="FontStyle" serializeAs="String">
<value>Microsoft Sans Serif, 10pt</value>
</setting>
<setting name="BackPicturePath" serializeAs="String">
<value />
</setting>
<setting name="SpoofGames" serializeAs="String">
<value>False</value>
</setting>
<setting name="BigFontStyle" serializeAs="String">
<value>Microsoft Sans Serif, 14pt</value>
</setting>
<setting name="ResignAPKs" serializeAs="String">
<value>False</value>
</setting>
<setting name="IPAddress" serializeAs="String">
<value />
</setting>
<setting name="InstalledApps" serializeAs="String">
<value />
</setting>
<setting name="ADBPath" serializeAs="String">
<value />
</setting>
<setting name="MainDir" serializeAs="String">
<value />
</setting>
<setting name="delsh" serializeAs="String">
<value>False</value>
</setting>
<setting name="CurrPckg" serializeAs="String">
<value />
</setting>
<setting name="ADBFolder" serializeAs="String">
<value />
</setting>
<setting name="WirelessADB" serializeAs="String">
<value>False</value>
</setting>
<setting name="CurrentGamename" serializeAs="String">
<value />
</setting>
<setting name="PackageNameToCB" serializeAs="String">
<value>False</value>
</setting>
<setting name="DownUpHeld" serializeAs="String">
<value>False</value>
</setting>
<setting name="CurrentLogPath" serializeAs="String">
<value />
</setting>
<setting name="CurrentLogName" serializeAs="String">
<value />
</setting>
<setting name="CurrentCrashPath" serializeAs="String">
<value />
</setting>
<setting name="CurrentCrashName" serializeAs="String">
<value />
</setting>
<setting name="adbdebugwarned" serializeAs="String">
<value>False</value>
</setting>
<setting name="nodevicemode" serializeAs="String">
<value>False</value>
</setting>
<setting name="BMBFchecked" serializeAs="String">
<value>True</value>
</setting>
<setting name="GamesList" serializeAs="String">
<value />
</setting>
<setting name="UploadedGameList" serializeAs="String">
<value>False</value>
</setting>
<setting name="GlobalUsername" serializeAs="String">
<value />
</setting>
<setting name="lastTimeShared" serializeAs="String">
<value />
</setting>
<setting name="AutoReinstall" serializeAs="String">
<value>False</value>
</setting>
<setting name="NonAppPackages" serializeAs="String">
<value />
</setting>
<setting name="LastLaunch" serializeAs="String">
<value>04/20/1969 16:20:00</value>
</setting>
<setting name="SubmittedUpdates" serializeAs="String">
<value />
</setting>
<setting name="ListUpped" serializeAs="String">
<value>False</value>
</setting>
<setting name="LastLaunch2" serializeAs="String">
<value>04/20/1969 16:20:00</value>
</setting>
<setting name="Wired" serializeAs="String">
<value>False</value>
</setting>
<setting name="FontColor" serializeAs="String">
<value>White</value>
</setting>
<setting name="ComboBoxColor" serializeAs="String">
<value>25, 25, 25</value>
</setting>
<setting name="SubButtonColor" serializeAs="String">
<value>42, 45, 58</value>
</setting>
<setting name="TextBoxColor" serializeAs="String">
<value>25, 25, 25</value>
</setting>
<setting name="ButtonColor" serializeAs="String">
<value>32, 35, 45</value>
</setting>
<setting name="BackColor" serializeAs="String">
<value>31, 34, 42</value>
</setting>
<setting name="AppPackages" serializeAs="String">
<value />
</setting>
<setting name="TrailersOn" serializeAs="String">
<value>False</value>
</setting>
<setting name="downloadDir" serializeAs="String">
<value />
</setting>
<setting name="customDownloadDir" serializeAs="String">
<value>False</value>
</setting>
<setting name="customBackupDir" serializeAs="String">
<value>False</value>
</setting>
<setting name="backupDir" serializeAs="String">
<value />
</setting>
<setting name="singleThreadMode" serializeAs="String">
<value>True</value>
</setting>
<setting name="virtualFilesystemCompatibility" serializeAs="String">
<value>False</value>
</setting>
<setting name="UpdateSettings" serializeAs="String">
<value>True</value>
</setting>
<setting name="UUID" serializeAs="String">
<value />
</setting>
<setting name="createPubMirrorFile" serializeAs="String">
<value>True</value>
</setting>
<setting name="useDownloadedFiles" serializeAs="String">
<value>False</value>
</setting>
<setting name="bandwidthLimit" serializeAs="String">
<value>0</value>
</setting>
<setting name="useProxy" serializeAs="String">
<value>False</value>
</setting>
<setting name="proxyAddress" serializeAs="String">
<value />
</setting>
<setting name="proxyPort" serializeAs="String">
<value />
</setting>
<setting name="selectedMirror" serializeAs="String">
<value />
</setting>
</AndroidSideloader.Properties.Settings>
<AndroidADB.Sideloader.Properties.Settings>
<setting name="checkForUpdates" serializeAs="String">
<value>True</value>
</setting>
<setting name="enableMessageBoxes" serializeAs="String">
<value>True</value>
</setting>
<setting name="logRclone" serializeAs="String">
<value>False</value>
</setting>
<setting name="firstRun" serializeAs="String">
<value>True</value>
</setting>
<setting name="deleteAllAfterInstall" serializeAs="String">
<value>True</value>
</setting>
<setting name="autoUpdateConfig" serializeAs="String">
<value>True</value>
</setting>
<setting name="singleThreadMode" serializeAs="String">
<value>True</value>
</setting>
<setting name="userJsonOnGameInstall" serializeAs="String">
<value>True</value>
</setting>
<setting name="CallUpgrade" serializeAs="String">
<value>True</value>
</setting>
<setting name="BackColor" serializeAs="String">
<value>45, 45, 45</value>
</setting>
<setting name="PanelColor" serializeAs="String">
<value>ActiveCaptionText</value>
</setting>
<setting name="ButtonColor" serializeAs="String">
<value>ActiveCaptionText</value>
</setting>
<setting name="SubButtonColor" serializeAs="String">
<value>64, 64, 64</value>
</setting>
<setting name="TextBoxColor" serializeAs="String">
<value>45, 45, 45</value>
</setting>
<setting name="ComboBoxColor" serializeAs="String">
<value>45, 45, 45</value>
</setting>
<setting name="FontColor" serializeAs="String">
<value>White</value>
</setting>
<setting name="FontStyle" serializeAs="String">
<value>Microsoft Sans Serif, 11.25pt</value>
</setting>
<setting name="BackPicturePath" serializeAs="String">
<value/>
</setting>
<setting name="SpoofGames" serializeAs="String">
<value>False</value>
</setting>
</AndroidADB.Sideloader.Properties.Settings>
</userSettings>
</configuration>

View File

@@ -1,4 +1,268 @@
RSL 2.17
RSL 3.0
Major Rookie overhaul with modernized UI, significant performance improvements and upgraded UX.
- Added high-performance Gallery View with search, filters, sorting, favorites, hover animations, smooth scrolling and uninstall buttons
- Toggle seamlessly between List and Gallery views with your preference remembered across launches
- Complete UI redesign with new dark theme, modernized components and subtle animations throughout
- Refined navigation, layouts, sizing and color consistency across the entire application
- Added uninstall buttons directly in List and Gallery views for quicker app management
- Improved startup performance through overhaul of initialization logic, removal of splash screen, parallelized async loading, batched version retrieval, optimized metadata extraction and game list initialization
- Instant list filtering through caching and streamlined filter logic (INSTALLED / UPDATE AVAILABLE / NEWER THAN LIST)
- Improved search speed and responsiveness
- Fixed and improved trailer handling with faster trailer loading
- Fixed multiple startup issues including connection errors and zombie ADB instances
- Added local blacklist support allowing users to permanently suppress donation prompts for specific apps
- Reduced application size by removal of now unused assets
RSL 2.34
- Feature: Allow users to favorite games (right click on game)
- Fix: Release Notes not showing with trailers enabled
- Fix: Correct Discord Invite link on connection error
RSL 2.33
- Feature: Allow users to cancel backups
- QoL: Lower ADB Version
- QoL: Revert Add saving the ColumnWidths, Window Sizes/Position
RSL 2.32
- Feature: Allow switching between Public and Private configs
- Feature: Switch to Offline Mode when unable to connect
- Feature: Save window position and column sizes
- Fix: An issue that caused the VIP mirror to fail
- Fix: Make video window flexible
- Fix: Issue when saving Settings file
- Fix: Change Label position to avoid overlapping Queue/Notes windows
- Chore: Bump aapt/aapt2/adb to latest
- Chore: Bump 7z to 24.09
- Chore: Add AddDefenderExceptions.ps1
RSL 2.31.1
- Fix: Filtering when Adult Content is shown
RSL 2.31
- Feature: Preserve download.config during rclone update
- Feature: Added toggle to hide Adult Content
- Fix: Changed settings configuration to settings.json
- Fix: Always create debuglog on first-launch
- Fix: Fixed numeric sorting
- Fix: Wrap ADB commands to prevent crash
- Fix: Update CleanupInstall to use UserProfile instead of Username
- Fix: Fix to OBB pushing when Dragging and Dropping
- Fix: Do not show ShowPrcOutput messages when "Enable Message Boxes" is disabled
- Fix: Add safeguard to prevent deletion of current directory
- Fix: Case insensitive package matching
- Chore: Add build batch
- Chore: Bump rclone to 1.68.2 (from 1.67.0)
RSL 2.30
- Feature: Added Download Metrics
-- Upon downloading a game, Rookie will attempt to log the download count with our server
-- No additional data is collected except for the Package Name and VersionCode of the Game being downloaded
- Feature: Added Downloads column to Gamelist
-- Downloads counts are updated nightly
- Feature: Added "Send Command" button to ADB Commands prompt (Enter still works)
- Feature: Small changes to how the Upload Mirror works
-- Rookie can now verify game uploads are successful
- Feature: Add Cleanup Install script
- Feature: Added additional Splashes to indicate loading status
- Feature: Added setting to supress Re-Download prompt
- Feature: Added "Disable Checking for New Apps" argument (for Testers sanity)
- Feature: Added Bandwidth Limiter to Settings
- Fix: Fixes for using Sponsored Mirrors
-- Eliminated downloading of vrp.download.config from the wiki
-- Fixed issue where Rookie would check for the mirror before it was loaded in
- Fix: Add quotes around all ADB path commands
- Fix: Public Mirror label not displaying
- Fix: Allow spaces when setting username
- Fix: Prevent Rookie from constantly prompting for the Public Config
- Fix: Fixed issue where the Game List doesn't copy/export properly
- Fix: Fixed issue where dropping an entire folder doesn't read the install.txt
- Chore: Remove random mirror picker (no longer used)
- Chore: Changed unable to connect message
- Chore: Remove Offline Mode popup
- Chore: Remove unused dependencies
- Chore: Rename random assets
- Chore: Bump rclone to 1.67.0 (from 1.66.0)
RSL 2.29
- Feature: Prompt to keep temporary files and allow resuming of downloads
- Feature: Fixed progress bar jump-back
- Feature: Added part tracking during download
- Fix: Change UUID calculation. No longer uses system details (caused virus flags)
- Fix: Added rclone fallback download
- Fix: Additional connect/startup troubleshooting steps
- Chore: Bump rclone to 1.66.0
RSL 2.29.1
- Fix: Correct initilization sequence to prevent crashes
- Chore: bump package dependencies
- Chore: Fix initial GUI name
RSL 2.29.2
- Fix: Fix for failed uploads using new rclone version
- Fix: Changes to initilization order to prevent crashes and display splash
RSL 2.28
- Feature: Added ADB-Backup Support. Now supports creation and restoration of .AB backups
- Feature: Added OS-Updates toggle to ADB-Command window (Click to toggle on/off)
- Feature: Allow ADB Commands with or without prefix
- Fix: Left-Navigation UI Reorganization
- Fix: 7zip crash, empty device list crash
- Fix: Update settings after version update
- Fix: Crashlog naming
- Fix: Disable searchbar until Rookie is fully loaded
- Chore: Add public mirror as a default to Git-downloads
- Chore: Readme Updates
- Chore: Code Cleanup / Beautification
- Chore: Change from _adb dependency files to dependencies.7z file
- Chore: Remove unused files
- Chore: Default to Single-Thread Mode
RSL 2.27.2
- Feature: Searchbar can now also search release names
- Feature: Rookie will now download 64 bit 7zip binaries for faster extractions (when possible)
- Fix: 7zip error handler
- Fix: Crash when downloading without enough space
- Fix: Rookie will no longer deny game donations.
- Chore: Cleanup code
RSL 2.26
- Feature: Top right update labels will now show the amount of games per state
- Feature: Open Download Dir and Run ADB Commands added to left sidebar
- Feature: Toggle sideloading button
- Feature: Show progress of extraction
- Fix: Crash when failing to write to the StoredIP.txt
- Fix: Drag and drop + Copy OBB for Quest 3 users
- Fix: Properly kill off WebView2 instances
- Fix: Added parents to dialogs
- Fix: GUI freezing on launch
- Fix: No Device Mode output
- Chore: Make missing space message more user-friendly
- Chore: Remove QU Settings
RSL 2.25.1
- Fix: Device not authorized prompt showing up after authorization.
RSL 2.25
- Fix: Fix to unauthorized device bug
- Chore: Update Wiki URL
RSL 2.24
- Feature: Added "Open Download" and "Open Backup" Directory buttons to Settings Menu
- Fix: OBB Pushes for Quest 3 Devices
- Fix: Crash when attempting to backup
- Fix: Searchbar Anchoring when resizing
- Fix: Remove instances where OBBs are pushed multiple times
- Fix: Additional Logging and Startup Optimizations
RSL 2.23
- Fix: Rookie should now only prompt for newer versions of all games
- Fix: Crash when attempting to delete invalid directories or files
- Feature: Enable Maximize Button
RSL 2.22
- Fix: Crash on failed extractions.
- Fix: Trailers controls not functioning properly.
- Fix: No Device Mode having the wrong output message.
- Chore: Remove ADB.WakeDevice() due to being non-functional.
- Chore: Make changeTitlebarToDevice less prone to crashes and more robust.
RSL 2.21
- Feature: Search now shows all results found
- Feature: Add --no-rclone-updating launch flag
- Feature: Add lime-green color to various after download jobs
- Feature: Update Sideloader Launcher to check for Rookie
- Feature: Add the ability to download into virtual filesystems (Settings)
- Feature: Add stacktrace & innerexception to crashlog
- Feature: Add CallerInfo to debuglog
- Feature: Rookie will now tell the user when there's a running RCLONE Job
- Fix: Rookie will no longer repeat "All tasks finished." in No Device Mode
- Fix: Rookie should no longer delete the entire OBB/Data folder when uninstalling a game
- Fix: Rookie should no longer freeze on the OBB Comparison
- Fix: The free disclaimer on launch is no longer broken
- Fix: Use new process for every ADB command
- Fix: Do not delete game files on an OBB Mismatch anymore
- Fix: Change all hardcoded C:/ paths to dynamically grab the system drive for further compatability
- Fix: Rookie should now function on 32 Bit again
- Fix: Fix crash on failed extraction
- Chore: Update quota message to be up to date
- Chore: Code rewrites for logging system, obb comparison, updater
- Chore: Several redundant code removals
- Chore: Change Install w/ Enter Key to Download w/ Enter Key
- Chore: Several code cleanups & optimizations
~ Chax
RSL 2.20
- Feature: Added manual IP on Wireless ADB
- Feature: Rookie will now delete old OBB Folders before pushing new obb folders
- Feature: Add Single-Thread mode in settings (For users with download issues using multiple threads)
- Fix: Device Storage display
- Fix: Games that come with install.txt will now check for a device to avoid freezes
- Fix: "No Device Mode" checkbox can't have "Delete after Download and Install" enabled at the same time anymore
- Fix: Removing Queue Items should no longer ever crash Rookie
- Fix: Rookie will no longer reload the gamelist if you have filtered it
- Fix: Check if the rclone we are killing is ours
- Fix: Put zip extraction into a thread to avoid freezing the UI
- Fix: Filters should no longer cause crashes.
- Chore: Update RCLONE to 1.62.2
- Chore: Cleaning up the code for the Trailers, progress bar, dropdown categories, redundant internet checks, and other code cleanup
- Chore: Other UI and message box display cleanup
- Chore: Optimize the code for getting the thumbnail paths
- Chore: Log Levels added to Debuglog
- Thank you to Chax for the new features and fixes!
~ fenopy
RSL 2.19
- Feature: Added streaming Game Trailers (enable in Settings)
- Feature: Added custom Download Directories
- Feature: Added custom Backup Directories
- Feature: Added Game Filtering (click on the Colored-Labels in the Top-Right)
- Feature: When uninstalling games, Rookie will now ask if you want to back up save data
- Feature: "No Device Mode" now automatically disables "Delete after Download and Install"
- Feature: Offline Mode now has the ability to donate new games/updates
- Fix: OBBs will no longer be attempted to push in "No Device Mode"
- Fix: Comparing OBB sizes will no longer error during "No Device Mode"
- Fix: Comparing OBB Sizes will no longer state "Input string was not in a correct format", it will now properly tell the user what is wrong
- Fix: The GitHub Error when starting up will now show the actual URL it tries to access
- Fix: Added a check to make Rookie not attempt to get available space when no devices are connected
- Removed Bandwidth Limit settings
- Thank you to Chax for the new features!
- Thank you to JP for the Trailers framework!
~ fenopy
RSL 2.18
- Moving Rookie onto a different Directory than C: will not break Pull App To Desktop anymore
- Rookie should now tell the user why it couldnt get the Rclone/ADB etc. Files
- Dialogs will should now no longer hide behind Rookie.
- ADB was updated to the latest version.
~ Chax & fenopy
RSL 2.17
- Various bugfixes

26
CleanupInstall.cmd Normal file
View File

@@ -0,0 +1,26 @@
@echo off
echo Killing AndroidSideloader processes...
taskkill /F /FI "IMAGENAME eq AndroidSideloader*" /T
echo Killing adb.exe processes...
taskkill /F /FI "IMAGENAME eq adb.exe" /T
set "folderPath=%userprofile%\AppData\Local\Rookie.WTF\"
echo Deleting contents of %folderPath%...
for /D %%i in ("%folderPath%\*") do (
rd /s /q "%%i"
)
del /q "%folderPath%\*.*"
set "folderPath=%userprofile%\AppData\Local\Rookie.AndroidSideloader\"
echo Deleting contents of %folderPath%...
for /D %%i in ("%folderPath%\*") do (
rd /s /q "%%i"
)
del /q "%folderPath%\*.*"
echo Cleanup complete.
pause

View File

@@ -1,91 +1,165 @@
using System.Collections;
using System;
using System.Collections;
using System.Windows.Forms;
/// <summary>
/// This class is an implementation of the 'IComparer' interface.
/// A custom comparer for sorting ListView columns, implementing the 'IComparer' interface.
/// </summary>
public class ListViewColumnSorter : IComparer
{
/// <summary>
/// Case insensitive comparer object
/// Case-insensitive comparer object used for comparing strings.
/// </summary>
private readonly CaseInsensitiveComparer ObjectCompare;
/// <summary>
/// Class constructor. Initializes various elements
/// Initializes a new instance of the ListViewColumnSorter class and sets default sorting parameters.
/// </summary>
public ListViewColumnSorter()
{
// Initialize the column to '0'
// Default column index for sorting
SortColumn = 0;
// Initialize the sort order to 'none'
// Default sorting order
Order = SortOrder.Ascending;
// Initialize the CaseInsensitiveComparer object
// Initialize the case-insensitive comparer
ObjectCompare = new CaseInsensitiveComparer();
}
/// <summary>
/// This method is inherited from the IComparer interface. It compares the two objects passed using a case insensitive comparison.
/// Compares two ListViewItem objects based on the specified column index and sorting order.
/// </summary>
/// <param name="x">First object to be compared</param>
/// <param name="y">Second object to be compared</param>
/// <returns>The result of the comparison. "0" if equal, negative if 'x' is less than 'y' and positive if 'x' is greater than 'y'</returns>
/// <param name="x">First ListViewItem object to compare.</param>
/// <param name="y">Second ListViewItem object to compare.</param>
/// <returns>
/// A signed integer indicating the relative values of x and y:
/// "0" if equal, a negative number if x is less than y, and a positive number if x is greater than y.
/// </returns>
public int Compare(object x, object y)
{
int compareResult;
ListViewItem listviewX, listviewY;
ListViewItem listviewX = (ListViewItem)x;
ListViewItem listviewY = (ListViewItem)y;
// Cast the objects to be compared to ListViewItem objects
listviewX = (ListViewItem)x;
listviewY = (ListViewItem)y;
if (SortColumn == 5)
// Special handling for column 6 (Popularity ranking)
if (SortColumn == 6)
{
try
string textX = listviewX.SubItems[SortColumn].Text;
string textY = listviewY.SubItems[SortColumn].Text;
// Extract numeric values from "#1", "#10", etc.
// "-" represents unranked and should go to the end
int rankX = int.MaxValue; // Default for unranked (-)
int rankY = int.MaxValue;
if (textX.StartsWith("#") && int.TryParse(textX.Substring(1), out int parsedX))
{
int yNum = int.Parse(cleanNumber(listviewY.SubItems[SortColumn].Text));
int xNum = int.Parse(cleanNumber(listviewX.SubItems[SortColumn].Text));
return xNum == yNum ? 0 : xNum > yNum && Order == SortOrder.Ascending ? -1 : 1;
rankX = parsedX;
}
catch { }
if (textY.StartsWith("#") && int.TryParse(textY.Substring(1), out int parsedY))
{
rankY = parsedY;
}
// Compare the numeric ranks
compareResult = rankX.CompareTo(rankY);
}
// Special handling for column 5 (Size)
else if (SortColumn == 5)
{
string textX = listviewX.SubItems[SortColumn].Text;
string textY = listviewY.SubItems[SortColumn].Text;
double sizeX = ParseSize(textX);
double sizeY = ParseSize(textY);
// Compare the numeric sizes
compareResult = sizeX.CompareTo(sizeY);
}
else
{
// Default to string comparison for non-numeric columns
compareResult = ObjectCompare.Compare(listviewX.SubItems[SortColumn].Text, listviewY.SubItems[SortColumn].Text);
}
// Compare the two items
compareResult = ObjectCompare.Compare(listviewX.SubItems[SortColumn].Text, listviewY.SubItems[SortColumn].Text);
// Calculate correct return value based on object comparison
// Determine the return value based on the specified sort order
if (Order == SortOrder.Ascending)
{
// Ascending sort is selected, return normal result of compare operation
return compareResult;
}
else if (Order == SortOrder.Descending)
{
// Descending sort is selected, return negative result of compare operation
return -compareResult;
}
else
{
// Return '0' to indicate they are equal
return 0;
return 0; // Indicate equality
}
}
/// <summary>
/// Gets or sets the number of the column to which to apply the sorting operation (Defaults to '0').
/// Parses a numeric value from a string for accurate numeric comparison.
/// </summary>
public int SortColumn { set; get; }
/// <summary>
/// Gets or sets the order of sorting to apply (for example, 'Ascending' or 'Descending').
/// </summary>
public SortOrder Order { set; get; }
private string cleanNumber(string number)
/// <param name="text">The string representation of the number.</param>
/// <returns>The parsed integer value; returns 0 if parsing fails.</returns>
private int ParseNumber(string text)
{
return number.Substring(0);
// Directly attempt to parse the string as an integer
return int.TryParse(text, out int result) ? result : 0;
}
}
/// <summary>
/// Parses size strings with units (GB/MB) and converts them to MB for comparison.
/// </summary>
/// <param name="sizeStr">Size string (e.g., "1.23 GB", "123 MB")</param>
/// <returns>Size in MB as a double</returns>
private double ParseSize(string sizeStr)
{
if (string.IsNullOrEmpty(sizeStr))
return 0;
// Remove whitespace
sizeStr = sizeStr.Trim();
// Handle new format: "1.23 GB" or "123 MB"
if (sizeStr.EndsWith(" GB", StringComparison.OrdinalIgnoreCase))
{
string numPart = sizeStr.Substring(0, sizeStr.Length - 3).Trim();
if (double.TryParse(numPart, System.Globalization.NumberStyles.Any,
System.Globalization.CultureInfo.InvariantCulture, out double gb))
{
return gb * 1024.0; // Convert GB to MB for consistent sorting
}
}
else if (sizeStr.EndsWith(" MB", StringComparison.OrdinalIgnoreCase))
{
string numPart = sizeStr.Substring(0, sizeStr.Length - 3).Trim();
if (double.TryParse(numPart, System.Globalization.NumberStyles.Any,
System.Globalization.CultureInfo.InvariantCulture, out double mb))
{
return mb;
}
}
// Fallback: try parsing as raw number
if (double.TryParse(sizeStr, System.Globalization.NumberStyles.Any,
System.Globalization.CultureInfo.InvariantCulture, out double rawMb))
{
return rawMb;
}
return 0;
}
/// <summary>
/// Gets or sets the index of the column to be sorted (default is '0').
/// </summary>
public int SortColumn { get; set; }
/// <summary>
/// Gets or sets the order of sorting (Ascending or Descending).
/// </summary>
public SortOrder Order { get; set; }
}

View File

@@ -22,38 +22,33 @@ namespace AndroidSideloader
{
donorGameProperties.Clear();
donorGames.Clear();
if (!MainForm.DonorApps.Equals(""))
if (!string.IsNullOrEmpty(MainForm.donorApps))
{
string[] gameListSplited = MainForm.DonorApps.Split(new[] { '\n' });
string[] gameListSplited = MainForm.donorApps.Split('\n');
foreach (string game in gameListSplited)
{
if (game.Length > 1)
{
string[] splitGame = game.Split(';');
donorGames.Add(splitGame);
donorGames.Add(game.Split(';'));
}
}
}
}
public static void initNewApps()
{
newApps.Clear();
if (!DonorsListViewForm.newAppsForList.Equals(""))
if (!string.IsNullOrEmpty(DonorsListViewForm.newAppsForList))
{
string[] newListSplited = DonorsListViewForm.newAppsForList.Split(new[] { '\n' });
string[] newListSplited = DonorsListViewForm.newAppsForList.Split('\n');
foreach (string game in newListSplited)
{
if (game.Length > 1)
{
string[] splitGame = game.Split(';');
newApps.Add(splitGame);
newApps.Add(game.Split(';'));
}
}
}
}
}
}

View File

@@ -30,30 +30,148 @@ namespace AndroidSideloader
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.DonationTimer = new System.Windows.Forms.Timer(this.components);
this.panel1 = new System.Windows.Forms.Panel();
this.skip_forever = new AndroidSideloader.RoundButton();
this.SkipButton = new AndroidSideloader.RoundButton();
this.DonateButton = new AndroidSideloader.RoundButton();
this.panel2 = new System.Windows.Forms.Panel();
this.DonorsListView = new System.Windows.Forms.ListView();
this.GameNameIndex = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.PackageNameIndex = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.VersionCodeIndex = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.UpdateOrNew = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.panel1 = new System.Windows.Forms.Panel();
this.SkipButton = new System.Windows.Forms.Button();
this.panel2 = new System.Windows.Forms.Panel();
this.bothdet = new System.Windows.Forms.Label();
this.newdet = new System.Windows.Forms.Label();
this.upddet = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.TimerDesc = new System.Windows.Forms.Label();
this.DonateButton = new System.Windows.Forms.Button();
this.DonationTimer = new System.Windows.Forms.Timer(this.components);
this.panel1.SuspendLayout();
this.panel2.SuspendLayout();
this.SuspendLayout();
//
// panel1
//
this.panel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(24)))), ((int)(((byte)(29)))));
this.panel1.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None;
this.panel1.Controls.Add(this.skip_forever);
this.panel1.Controls.Add(this.SkipButton);
this.panel1.Controls.Add(this.DonateButton);
this.panel1.Controls.Add(this.panel2);
this.panel1.Controls.Add(this.bothdet);
this.panel1.Controls.Add(this.newdet);
this.panel1.Controls.Add(this.upddet);
this.panel1.Controls.Add(this.label2);
this.panel1.Controls.Add(this.TimerDesc);
this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.panel1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(160)))), ((int)(((byte)(165)))), ((int)(((byte)(175)))));
this.panel1.Location = new System.Drawing.Point(0, 0);
this.panel1.Name = "panel1";
this.panel1.Padding = new System.Windows.Forms.Padding(16);
this.panel1.Size = new System.Drawing.Size(460, 420);
this.panel1.TabIndex = 1;
this.panel1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseDown);
this.panel1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseMove);
this.panel1.MouseUp += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseUp);
//
// skip_forever
//
this.skip_forever.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(52)))), ((int)(((byte)(62)))));
this.skip_forever.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(52)))), ((int)(((byte)(62)))));
this.skip_forever.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.skip_forever.BackColor = System.Drawing.Color.Transparent;
this.skip_forever.DialogResult = System.Windows.Forms.DialogResult.OK;
this.skip_forever.Disabled1 = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
this.skip_forever.Disabled2 = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
this.skip_forever.DisabledStrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
this.skip_forever.Font = new System.Drawing.Font("Segoe UI Semibold", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.skip_forever.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(160)))), ((int)(((byte)(165)))), ((int)(((byte)(175)))));
this.skip_forever.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(35)))), ((int)(((byte)(42)))));
this.skip_forever.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(35)))), ((int)(((byte)(42)))));
this.skip_forever.Location = new System.Drawing.Point(20, 380);
this.skip_forever.Margin = new System.Windows.Forms.Padding(0);
this.skip_forever.Name = "skip_forever";
this.skip_forever.Radius = 4;
this.skip_forever.Size = new System.Drawing.Size(420, 32);
this.skip_forever.Stroke = true;
this.skip_forever.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
this.skip_forever.TabIndex = 97;
this.skip_forever.Text = "Add to blacklist / Never ask for the selected apps again";
this.skip_forever.Transparency = false;
this.skip_forever.Click += new System.EventHandler(this.skip_forever_Click);
//
// SkipButton
//
this.SkipButton.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(52)))), ((int)(((byte)(62)))));
this.SkipButton.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(52)))), ((int)(((byte)(62)))));
this.SkipButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.SkipButton.BackColor = System.Drawing.Color.Transparent;
this.SkipButton.DialogResult = System.Windows.Forms.DialogResult.OK;
this.SkipButton.Disabled1 = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
this.SkipButton.Disabled2 = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
this.SkipButton.DisabledStrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
this.SkipButton.Font = new System.Drawing.Font("Segoe UI Semibold", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.SkipButton.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(160)))), ((int)(((byte)(165)))), ((int)(((byte)(175)))));
this.SkipButton.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(35)))), ((int)(((byte)(42)))));
this.SkipButton.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(35)))), ((int)(((byte)(42)))));
this.SkipButton.Location = new System.Drawing.Point(20, 326);
this.SkipButton.Margin = new System.Windows.Forms.Padding(0);
this.SkipButton.Name = "SkipButton";
this.SkipButton.Radius = 4;
this.SkipButton.Size = new System.Drawing.Size(100, 32);
this.SkipButton.Stroke = true;
this.SkipButton.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
this.SkipButton.TabIndex = 96;
this.SkipButton.Text = "Skip";
this.SkipButton.Transparency = false;
this.SkipButton.Click += new System.EventHandler(this.SkipButton_Click);
//
// DonateButton
//
this.DonateButton.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(140)))), ((int)(((byte)(115)))));
this.DonateButton.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(125)))), ((int)(((byte)(105)))));
this.DonateButton.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.DonateButton.BackColor = System.Drawing.Color.Transparent;
this.DonateButton.DialogResult = System.Windows.Forms.DialogResult.OK;
this.DonateButton.Disabled1 = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
this.DonateButton.Disabled2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(28)))), ((int)(((byte)(35)))));
this.DonateButton.DisabledStrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
this.DonateButton.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Bold);
this.DonateButton.ForeColor = System.Drawing.Color.White;
this.DonateButton.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(120)))), ((int)(((byte)(100)))));
this.DonateButton.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(35)))), ((int)(((byte)(100)))), ((int)(((byte)(85)))));
this.DonateButton.Location = new System.Drawing.Point(128, 326);
this.DonateButton.Margin = new System.Windows.Forms.Padding(0);
this.DonateButton.Name = "DonateButton";
this.DonateButton.Radius = 4;
this.DonateButton.Size = new System.Drawing.Size(312, 32);
this.DonateButton.Stroke = true;
this.DonateButton.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(150)))), ((int)(((byte)(125)))));
this.DonateButton.TabIndex = 95;
this.DonateButton.Text = "Share Selected Apps";
this.DonateButton.Transparency = false;
this.DonateButton.Click += new System.EventHandler(this.DonateButton_Click);
//
// panel2
//
this.panel2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panel2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(28)))), ((int)(((byte)(32)))), ((int)(((byte)(38)))));
this.panel2.Controls.Add(this.DonorsListView);
this.panel2.Location = new System.Drawing.Point(20, 70);
this.panel2.Name = "panel2";
this.panel2.Padding = new System.Windows.Forms.Padding(1);
this.panel2.Size = new System.Drawing.Size(420, 250);
this.panel2.TabIndex = 2;
//
// DonorsListView
//
this.DonorsListView.AccessibleRole = System.Windows.Forms.AccessibleRole.None;
this.DonorsListView.BackColor = global::AndroidSideloader.Properties.Settings.Default.ComboBoxColor;
this.DonorsListView.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.DonorsListView.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(28)))), ((int)(((byte)(32)))), ((int)(((byte)(38)))));
this.DonorsListView.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.DonorsListView.CausesValidation = false;
this.DonorsListView.CheckBoxes = true;
this.DonorsListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
@@ -61,22 +179,17 @@ namespace AndroidSideloader
this.PackageNameIndex,
this.VersionCodeIndex,
this.UpdateOrNew});
this.DonorsListView.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "ComboBoxColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.DonorsListView.DataBindings.Add(new System.Windows.Forms.Binding("Font", global::AndroidSideloader.Properties.Settings.Default, "FontStyle", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.DonorsListView.DataBindings.Add(new System.Windows.Forms.Binding("ForeColor", global::AndroidSideloader.Properties.Settings.Default, "FontColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.DonorsListView.Font = global::AndroidSideloader.Properties.Settings.Default.FontStyle;
this.DonorsListView.ForeColor = global::AndroidSideloader.Properties.Settings.Default.FontColor;
this.DonorsListView.Dock = System.Windows.Forms.DockStyle.Fill;
this.DonorsListView.Font = new System.Drawing.Font("Segoe UI", 9.5F);
this.DonorsListView.ForeColor = System.Drawing.Color.White;
this.DonorsListView.FullRowSelect = true;
this.DonorsListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
this.DonorsListView.HideSelection = false;
this.DonorsListView.ImeMode = System.Windows.Forms.ImeMode.On;
this.DonorsListView.Location = new System.Drawing.Point(6, 6);
this.DonorsListView.Location = new System.Drawing.Point(1, 1);
this.DonorsListView.MinimumSize = new System.Drawing.Size(100, 100);
this.DonorsListView.Name = "DonorsListView";
this.DonorsListView.RightToLeftLayout = true;
this.DonorsListView.Size = new System.Drawing.Size(419, 219);
this.DonorsListView.Size = new System.Drawing.Size(418, 248);
this.DonorsListView.TabIndex = 0;
this.DonorsListView.TileSize = new System.Drawing.Size(100, 100);
this.DonorsListView.UseCompatibleStateImageBehavior = false;
this.DonorsListView.View = System.Windows.Forms.View.Details;
this.DonorsListView.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.DonorsListView_ItemChecked);
@@ -86,86 +199,38 @@ namespace AndroidSideloader
//
// GameNameIndex
//
this.GameNameIndex.Text = "Game Name";
this.GameNameIndex.Width = 219;
this.GameNameIndex.Text = "App Name";
this.GameNameIndex.Width = 220;
//
// PackageNameIndex
//
this.PackageNameIndex.DisplayIndex = 2;
this.PackageNameIndex.Text = "Packagename";
this.PackageNameIndex.Text = "Package";
this.PackageNameIndex.Width = 0;
//
// VersionCodeIndex
//
this.VersionCodeIndex.DisplayIndex = 3;
this.VersionCodeIndex.Text = "Version";
this.VersionCodeIndex.Width = 113;
this.VersionCodeIndex.Width = 100;
//
// UpdateOrNew
//
this.UpdateOrNew.DisplayIndex = 1;
this.UpdateOrNew.Text = "Donation Type";
this.UpdateOrNew.Width = 85;
//
// panel1
//
this.panel1.BackColor = global::AndroidSideloader.Properties.Settings.Default.BackColor;
this.panel1.BackgroundImage = global::AndroidSideloader.Properties.Resources.pattern_cubes_1_1_1_0_0_0_1__000000_212121;
this.panel1.Controls.Add(this.SkipButton);
this.panel1.Controls.Add(this.panel2);
this.panel1.Controls.Add(this.bothdet);
this.panel1.Controls.Add(this.newdet);
this.panel1.Controls.Add(this.upddet);
this.panel1.Controls.Add(this.label2);
this.panel1.Controls.Add(this.TimerDesc);
this.panel1.Controls.Add(this.DonateButton);
this.panel1.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "BackColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.panel1.Location = new System.Drawing.Point(-7, -7);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(463, 345);
this.panel1.TabIndex = 1;
this.panel1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseDown);
this.panel1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseMove);
this.panel1.MouseUp += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseUp);
//
// SkipButton
//
this.SkipButton.BackColor = global::AndroidSideloader.Properties.Settings.Default.SubButtonColor;
this.SkipButton.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "SubButtonColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.SkipButton.DataBindings.Add(new System.Windows.Forms.Binding("ForeColor", global::AndroidSideloader.Properties.Settings.Default, "FontColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.SkipButton.DataBindings.Add(new System.Windows.Forms.Binding("Font", global::AndroidSideloader.Properties.Settings.Default, "FontStyle", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.SkipButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.SkipButton.Font = global::AndroidSideloader.Properties.Settings.Default.FontStyle;
this.SkipButton.ForeColor = global::AndroidSideloader.Properties.Settings.Default.FontColor;
this.SkipButton.Location = new System.Drawing.Point(22, 277);
this.SkipButton.Name = "SkipButton";
this.SkipButton.Size = new System.Drawing.Size(102, 36);
this.SkipButton.TabIndex = 1;
this.SkipButton.Text = "Skip";
this.SkipButton.UseVisualStyleBackColor = true;
this.SkipButton.Click += new System.EventHandler(this.SkipButton_Click);
//
// panel2
//
this.panel2.BackColor = global::AndroidSideloader.Properties.Settings.Default.SubButtonColor;
this.panel2.Controls.Add(this.DonorsListView);
this.panel2.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "SubButtonColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.panel2.Location = new System.Drawing.Point(16, 43);
this.panel2.Name = "panel2";
this.panel2.Size = new System.Drawing.Size(430, 230);
this.panel2.TabIndex = 2;
this.UpdateOrNew.Text = "Type";
this.UpdateOrNew.Width = 80;
//
// bothdet
//
this.bothdet.AutoSize = true;
this.bothdet.BackColor = System.Drawing.Color.Transparent;
this.bothdet.Font = new System.Drawing.Font("Microsoft Sans Serif", 10.25F, System.Drawing.FontStyle.Bold);
this.bothdet.Location = new System.Drawing.Point(125, 7);
this.bothdet.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Bold);
this.bothdet.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
this.bothdet.Location = new System.Drawing.Point(20, 15);
this.bothdet.Name = "bothdet";
this.bothdet.Size = new System.Drawing.Size(213, 17);
this.bothdet.Size = new System.Drawing.Size(228, 20);
this.bothdet.TabIndex = 3;
this.bothdet.Text = "Updates/new apps detected!";
this.bothdet.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.bothdet.Text = "Updates && New Apps Available";
this.bothdet.Visible = false;
this.bothdet.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseDown);
this.bothdet.MouseMove += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseMove);
@@ -175,13 +240,13 @@ namespace AndroidSideloader
//
this.newdet.AutoSize = true;
this.newdet.BackColor = System.Drawing.Color.Transparent;
this.newdet.Font = new System.Drawing.Font("Microsoft Sans Serif", 10.25F, System.Drawing.FontStyle.Bold);
this.newdet.Location = new System.Drawing.Point(120, 7);
this.newdet.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Bold);
this.newdet.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
this.newdet.Location = new System.Drawing.Point(20, 15);
this.newdet.Name = "newdet";
this.newdet.Size = new System.Drawing.Size(150, 17);
this.newdet.Size = new System.Drawing.Size(149, 20);
this.newdet.TabIndex = 3;
this.newdet.Text = "New apps detected!";
this.newdet.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.newdet.Text = "New Apps Available";
this.newdet.Visible = false;
this.newdet.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseDown);
this.newdet.MouseMove += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseMove);
@@ -191,13 +256,13 @@ namespace AndroidSideloader
//
this.upddet.AutoSize = true;
this.upddet.BackColor = System.Drawing.Color.Transparent;
this.upddet.Font = new System.Drawing.Font("Microsoft Sans Serif", 10.25F, System.Drawing.FontStyle.Bold);
this.upddet.Location = new System.Drawing.Point(120, 7);
this.upddet.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Bold);
this.upddet.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
this.upddet.Location = new System.Drawing.Point(20, 15);
this.upddet.Name = "upddet";
this.upddet.Size = new System.Drawing.Size(185, 17);
this.upddet.Size = new System.Drawing.Size(135, 20);
this.upddet.TabIndex = 3;
this.upddet.Text = "Game Updates Detected";
this.upddet.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.upddet.Text = "Updates Available";
this.upddet.Visible = false;
this.upddet.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseDown);
this.upddet.MouseMove += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseMove);
@@ -207,58 +272,43 @@ namespace AndroidSideloader
//
this.label2.AutoSize = true;
this.label2.BackColor = System.Drawing.Color.Transparent;
this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label2.Location = new System.Drawing.Point(23, 23);
this.label2.Font = new System.Drawing.Font("Segoe UI", 9F);
this.label2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(160)))), ((int)(((byte)(165)))), ((int)(((byte)(175)))));
this.label2.Location = new System.Drawing.Point(20, 42);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(416, 15);
this.label2.Size = new System.Drawing.Size(338, 15);
this.label2.TabIndex = 3;
this.label2.Text = "All Apps are donated by users! Without them none of this would be possible!";
this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.label2.Text = "All apps are donated by users! Help the community by sharing.";
this.label2.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseDown);
this.label2.MouseMove += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseMove);
this.label2.MouseUp += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseUp);
//
// TimerDesc
//
this.TimerDesc.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.TimerDesc.AutoSize = true;
this.TimerDesc.BackColor = System.Drawing.Color.Transparent;
this.TimerDesc.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.TimerDesc.Location = new System.Drawing.Point(28, 321);
this.TimerDesc.Font = new System.Drawing.Font("Segoe UI", 8F);
this.TimerDesc.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(120)))), ((int)(((byte)(125)))), ((int)(((byte)(135)))));
this.TimerDesc.Location = new System.Drawing.Point(79, 362);
this.TimerDesc.Name = "TimerDesc";
this.TimerDesc.Size = new System.Drawing.Size(406, 13);
this.TimerDesc.Size = new System.Drawing.Size(292, 13);
this.TimerDesc.TabIndex = 3;
this.TimerDesc.Text = "Don\'t share free apps. Rookie will extract/upload apps in background.";
this.TimerDesc.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.TimerDesc.Text = "Don\'t share free apps. Upload happens in background.";
this.TimerDesc.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseDown);
this.TimerDesc.MouseMove += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseMove);
this.TimerDesc.MouseUp += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseUp);
//
// DonateButton
//
this.DonateButton.BackColor = global::AndroidSideloader.Properties.Settings.Default.SubButtonColor;
this.DonateButton.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "SubButtonColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.DonateButton.Enabled = false;
this.DonateButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.DonateButton.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F);
this.DonateButton.ForeColor = System.Drawing.Color.White;
this.DonateButton.Location = new System.Drawing.Point(130, 277);
this.DonateButton.Name = "DonateButton";
this.DonateButton.Size = new System.Drawing.Size(311, 36);
this.DonateButton.TabIndex = 1;
this.DonateButton.Text = "Automatically share selected apps";
this.DonateButton.UseVisualStyleBackColor = true;
this.DonateButton.Click += new System.EventHandler(this.DonateButton_Click);
//
// DonorsListViewForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.Gainsboro;
this.ClientSize = new System.Drawing.Size(449, 336);
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(24)))), ((int)(((byte)(29)))));
this.ClientSize = new System.Drawing.Size(460, 420);
this.ControlBox = false;
this.Controls.Add(this.panel1);
this.ForeColor = System.Drawing.Color.White;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.Name = "DonorsListViewForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Load += new System.EventHandler(this.DonorsListViewForm_Load);
@@ -276,18 +326,19 @@ namespace AndroidSideloader
private System.Windows.Forms.ListView DonorsListView;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Button DonateButton;
private System.Windows.Forms.Label TimerDesc;
private System.Windows.Forms.ColumnHeader GameNameIndex;
private System.Windows.Forms.ColumnHeader PackageNameIndex;
private System.Windows.Forms.ColumnHeader VersionCodeIndex;
private System.Windows.Forms.ColumnHeader UpdateOrNew;
public System.Windows.Forms.Timer DonationTimer;
private System.Windows.Forms.Button SkipButton;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label bothdet;
private System.Windows.Forms.Label newdet;
private System.Windows.Forms.Label upddet;
private System.Windows.Forms.Panel panel2;
private RoundButton DonateButton;
private RoundButton SkipButton;
private RoundButton skip_forever;
}
}

View File

@@ -1,26 +1,69 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace AndroidSideloader
{
public partial class DonorsListViewForm : Form
{
// Modern theme colors
private static readonly Color BackgroundColor = Color.FromArgb(20, 24, 29);
private static readonly Color BorderColor = Color.FromArgb(70, 80, 100);
private static readonly Color UpdateHighlightColor = Color.FromArgb(0, 79, 97);
private bool mouseDown;
private Point lastLocation;
// Shadow and corner settings
private const int CS_DROPSHADOW = 0x00020000;
private const int WM_NCLBUTTONDOWN = 0xA1;
private const int HT_CAPTION = 0x2;
private const int SHADOW_SIZE = 2;
private const int CONTENT_RADIUS = 10;
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
private static extern bool ReleaseCapture();
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ClassStyle |= CS_DROPSHADOW;
return cp;
}
}
public DonorsListViewForm()
{
InitializeComponent();
// Use same icon as the executable
this.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
ApplyModernTheme();
CenterToScreen();
Donors.initDonorGames();
List<ListViewItem> DGameList = new List<ListViewItem>();
var seen = new HashSet<string>();
var DGameList = new List<ListViewItem>();
foreach (string[] release in Donors.donorGames)
{
ListViewItem DGame = new ListViewItem(release);
DGameList.Add(DGame);
if (release.Length == 0) continue;
string key = release[0];
if (seen.Add(key))
{
DGameList.Add(new ListViewItem(release));
}
}
ListViewItem[] arr = DGameList.ToArray();
DonorsListView.BeginUpdate();
DonorsListView.Items.Clear();
@@ -28,35 +71,145 @@ namespace AndroidSideloader
DonorsListView.EndUpdate();
}
public static string DonorsLocal = MainForm.DonorApps;
private void ApplyModernTheme()
{
this.FormBorderStyle = FormBorderStyle.None;
this.BackColor = Color.FromArgb(25, 25, 30);
this.Padding = new Padding(5);
panel1.BackColor = BackgroundColor;
panel1.Location = new Point(6, 6);
panel1.Size = new Size(this.ClientSize.Width - 12, this.ClientSize.Height - 12);
panel1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
this.Paint += Form_Paint;
// Close button
var closeButton = new Button
{
Text = "✕",
Font = new Font("Segoe UI", 9F),
ForeColor = Color.White,
BackColor = BackgroundColor,
FlatStyle = FlatStyle.Flat,
Size = new Size(30, 28),
Location = new Point(panel1.Width - 35, 5),
Cursor = Cursors.Hand,
TabStop = false
};
closeButton.FlatAppearance.BorderSize = 0;
closeButton.FlatAppearance.MouseOverBackColor = Color.FromArgb(200, 60, 60);
closeButton.Click += (s, e) => Close();
panel1.Controls.Add(closeButton);
closeButton.BringToFront();
// Enable dragging
panel1.MouseDown += TitleArea_MouseDown;
foreach (Control ctrl in panel1.Controls)
{
if (ctrl is Label)
{
ctrl.MouseDown += TitleArea_MouseDown;
}
}
}
private void TitleArea_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
ReleaseCapture();
SendMessage(this.Handle, WM_NCLBUTTONDOWN, (IntPtr)HT_CAPTION, IntPtr.Zero);
}
}
private void Form_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
int w = this.Width;
int h = this.Height;
// Draw shadow layers
for (int i = SHADOW_SIZE; i >= 1; i--)
{
int alpha = (SHADOW_SIZE - i + 1) * 12;
Rectangle shadowRect = new Rectangle(
SHADOW_SIZE - i,
SHADOW_SIZE - i,
w - (SHADOW_SIZE - i) * 2 - 1,
h - (SHADOW_SIZE - i) * 2 - 1);
using (Pen shadowPen = new Pen(Color.FromArgb(alpha, 0, 0, 0), 1))
using (GraphicsPath shadowPath = CreateRoundedRectPath(shadowRect, CONTENT_RADIUS + i))
{
e.Graphics.DrawPath(shadowPen, shadowPath);
}
}
// Draw content background
Rectangle contentRect = new Rectangle(SHADOW_SIZE, SHADOW_SIZE, w - SHADOW_SIZE * 2, h - SHADOW_SIZE * 2);
using (GraphicsPath contentPath = CreateRoundedRectPath(contentRect, CONTENT_RADIUS))
{
using (SolidBrush bgBrush = new SolidBrush(BackgroundColor))
{
e.Graphics.FillPath(bgBrush, contentPath);
}
using (Pen borderPen = new Pen(BorderColor, 1f))
{
e.Graphics.DrawPath(borderPen, contentPath);
}
}
// Apply rounded region
using (GraphicsPath regionPath = CreateRoundedRectPath(new Rectangle(0, 0, w, h), CONTENT_RADIUS + SHADOW_SIZE))
{
this.Region = new Region(regionPath);
}
}
private GraphicsPath CreateRoundedRectPath(Rectangle rect, int radius)
{
GraphicsPath path = new GraphicsPath();
if (radius <= 0)
{
path.AddRectangle(rect);
return path;
}
int diameter = Math.Min(radius * 2, Math.Min(rect.Width, rect.Height));
radius = diameter / 2;
Rectangle arcRect = new Rectangle(rect.Location, new Size(diameter, diameter));
path.AddArc(arcRect, 180, 90);
arcRect.X = rect.Right - diameter;
path.AddArc(arcRect, 270, 90);
arcRect.Y = rect.Bottom - diameter;
path.AddArc(arcRect, 0, 90);
arcRect.X = rect.Left;
path.AddArc(arcRect, 90, 90);
path.CloseFigure();
return path;
}
public static string DonorsLocal = MainForm.donorApps;
public static bool ifuploads = false;
public static string newAppsForList = "";
private void DonorsListViewForm_Load(object sender, EventArgs e)
{
MainForm.updatesnotified = true;
if (MainForm.updates && MainForm.newapps)
{
bothdet.Visible = true;
}
else if (MainForm.updates && !MainForm.newapps)
{
upddet.Visible = true;
}
else
{
newdet.Visible = true;
}
MainForm.updatesNotified = true;
bothdet.Visible = MainForm.updates && MainForm.newapps;
upddet.Visible = MainForm.updates && !MainForm.newapps;
newdet.Visible = !MainForm.updates;
foreach (ListViewItem listItem in DonorsListView.Items)
{
if (listItem.SubItems[Donors.UpdateOrNew].Text.Contains("Update"))
{
listItem.BackColor = Color.FromArgb(0, 79, 97);
}
listItem.BackColor = UpdateHighlightColor;
}
}
private async void DonateButton_Click(object sender, EventArgs e)
@@ -66,107 +219,106 @@ namespace AndroidSideloader
bool uncheckednewapps = false;
foreach (ListViewItem listItem in DonorsListView.Items)
{
if (!listItem.Checked)
if (!listItem.Checked && listItem.SubItems[Donors.UpdateOrNew].Text.Contains("New"))
{
if (listItem.SubItems[Donors.UpdateOrNew].Text.Contains("New"))
{
uncheckednewapps = true;
newAppsForList += listItem.SubItems[Donors.GameNameIndex].Text + ";" + listItem.SubItems[Donors.PackageNameIndex].Text + "\n";
}
uncheckednewapps = true;
newAppsForList += listItem.SubItems[Donors.GameNameIndex].Text + ";" + listItem.SubItems[Donors.PackageNameIndex].Text + "\n";
}
}
if (uncheckednewapps)
{
NewApps NewAppForm = new NewApps();
_ = NewAppForm.ShowDialog();
new NewApps().ShowDialog();
Hide();
}
else
{
Hide();
}
int count = DonorsListView.CheckedItems.Count;
_ = new string[count];
for (int i = 0; i < count; i++)
for (int i = 0; i < DonorsListView.CheckedItems.Count; i++)
{
ulong vcode = Convert.ToUInt64(DonorsListView.CheckedItems[i].SubItems[Donors.VersionCodeIndex].Text);
if (DonorsListView.CheckedItems[i].SubItems[Donors.UpdateOrNew].Text.Contains("Update"))
{
await Program.form.extractAndPrepareGameToUploadAsync(DonorsListView.CheckedItems[i].SubItems[Donors.GameNameIndex].Text, DonorsListView.CheckedItems[i].SubItems[Donors.PackageNameIndex].Text, vcode, true);
}
else
{
await Program.form.extractAndPrepareGameToUploadAsync(DonorsListView.CheckedItems[i].SubItems[Donors.GameNameIndex].Text, DonorsListView.CheckedItems[i].SubItems[Donors.PackageNameIndex].Text, vcode, false);
}
bool isUpdate = DonorsListView.CheckedItems[i].SubItems[Donors.UpdateOrNew].Text.Contains("Update");
await Program.form.extractAndPrepareGameToUploadAsync(
DonorsListView.CheckedItems[i].SubItems[Donors.GameNameIndex].Text,
DonorsListView.CheckedItems[i].SubItems[Donors.PackageNameIndex].Text,
vcode, isUpdate);
ifuploads = true;
}
}
if (ifuploads)
{
MainForm.DoUpload();
}
if (ifuploads) MainForm.doUpload();
Close();
}
private void DonorsListView_ItemChecked(object sender, ItemCheckedEventArgs e)
{
if (DonorsListView.CheckedItems.Count == 0)
{
SkipButton.Enabled = true;
DonateButton.Enabled = false;
}
else
{
DonateButton.Enabled = true;
SkipButton.Enabled = false;
}
SkipButton.Enabled = DonorsListView.CheckedItems.Count == 0;
DonateButton.Enabled = !SkipButton.Enabled;
skip_forever.Enabled = DonorsListView.CheckedItems.Count > 0;
}
private void SkipButton_Click(object sender, EventArgs e)
{
bool uncheckednewapps = false;
foreach (ListViewItem listItem in DonorsListView.Items)
{
if (!listItem.Checked)
{
if (listItem.SubItems[Donors.UpdateOrNew].Text.Contains("New"))
{
uncheckednewapps = true;
newAppsForList += listItem.SubItems[Donors.GameNameIndex].Text + ";" + listItem.SubItems[Donors.PackageNameIndex].Text + "\n";
}
}
}
if (uncheckednewapps)
{
NewApps NewAppForm = new NewApps();
_ = NewAppForm.ShowDialog();
if (!listItem.Checked && listItem.SubItems[Donors.UpdateOrNew].Text.Contains("New"))
newAppsForList += listItem.SubItems[Donors.GameNameIndex].Text + ";" + listItem.SubItems[Donors.PackageNameIndex].Text + "\n";
}
if (!string.IsNullOrEmpty(newAppsForList))
new NewApps().ShowDialog();
Close();
}
private void DonorsListViewForm_MouseDown(object sender, MouseEventArgs e)
{
mouseDown = true;
lastLocation = e.Location;
}
private void DonorsListViewForm_MouseDown(object sender, MouseEventArgs e) => TitleArea_MouseDown(sender, e);
private void DonorsListViewForm_MouseMove(object sender, MouseEventArgs e) { }
private void DonorsListViewForm_MouseUp(object sender, MouseEventArgs e) { }
private void DonorsListViewForm_MouseMove(object sender, MouseEventArgs e)
private void skip_forever_Click(object sender, EventArgs e)
{
if (mouseDown)
var appsToBlacklist = DonorsListView.CheckedItems.Cast<ListViewItem>()
.Select(item => item.SubItems[Donors.PackageNameIndex].Text).ToList();
if (appsToBlacklist.Count == 0)
{
Location = new Point(
Location.X - lastLocation.X + e.X, Location.Y - lastLocation.Y + e.Y);
Update();
MessageBox.Show("No apps selected to blacklist.", "Info", MessageBoxButtons.OK);
return;
}
if (MessageBox.Show(
$"Permanently skip donation requests for {appsToBlacklist.Count} app(s)?",
"Confirm Blacklist", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
return;
string blacklistPath = Path.Combine(Environment.CurrentDirectory, "blacklist.json");
try
{
var existingBlacklist = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
if (File.Exists(blacklistPath))
{
var jsonArray = Newtonsoft.Json.JsonConvert.DeserializeObject<string[]>(File.ReadAllText(blacklistPath));
if (jsonArray != null)
foreach (string entry in jsonArray.Where(ee => !string.IsNullOrWhiteSpace(ee)))
existingBlacklist.Add(entry.Trim());
}
foreach (string pkg in appsToBlacklist) existingBlacklist.Add(pkg);
File.WriteAllText(blacklistPath, Newtonsoft.Json.JsonConvert.SerializeObject(existingBlacklist.ToArray(), Newtonsoft.Json.Formatting.Indented));
Logger.Log($"Added {appsToBlacklist.Count} apps to local blacklist");
MessageBox.Show($"{appsToBlacklist.Count} {(appsToBlacklist.Count == 1 ? "app" : "apps")} added to blacklist.",
"Success",
MessageBoxButtons.OK, MessageBoxIcon.Information);
Close();
}
catch (Exception ex)
{
Logger.Log($"Error saving blacklist: {ex.Message}", LogLevel.ERROR);
MessageBox.Show($"Error: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void DonorsListViewForm_MouseUp(object sender, MouseEventArgs e)
{
mouseDown = false;
}
}
}
}

File diff suppressed because it is too large Load Diff

1773
GalleryView.cs Normal file

File diff suppressed because it is too large Load Diff

3248
MainForm.Designer.cs generated

File diff suppressed because it is too large Load Diff

9612
MainForm.cs Normal file → Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -117,67 +117,73 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="speedLabel_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>1165, 17</value>
</metadata>
<metadata name="startsideloadbutton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
<value>966, 17</value>
</metadata>
<metadata name="devicesbutton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>197, 95</value>
<value>705, 91</value>
</metadata>
<metadata name="obbcopybutton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 95</value>
<value>526, 91</value>
</metadata>
<metadata name="backupadbbutton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>390, 17</value>
</metadata>
<metadata name="backupbutton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>1454, 56</value>
<value>355, 91</value>
</metadata>
<metadata name="restorebutton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>1284, 56</value>
<value>187, 91</value>
</metadata>
<metadata name="getApkButton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>1113, 56</value>
<value>17, 91</value>
</metadata>
<metadata name="uninstallAppButton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>913, 56</value>
<value>1157, 54</value>
</metadata>
<metadata name="pullAppToDesktopBtn_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>698, 56</value>
<value>945, 54</value>
</metadata>
<metadata name="copyBulkObbButton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>493, 56</value>
<value>741, 54</value>
</metadata>
<metadata name="aboutBtn_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>369, 95</value>
<value>876, 91</value>
</metadata>
<metadata name="settingsButton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>319, 56</value>
</metadata>
<metadata name="InstallQUset_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 56</value>
</metadata>
<metadata name="removeQUSetting_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>1365, 17</value>
<value>568, 54</value>
</metadata>
<metadata name="QuestOptionsButton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>1158, 17</value>
<value>223, 54</value>
</metadata>
<metadata name="ADBWirelessDisable_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>954, 17</value>
<metadata name="btnOpenDownloads_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>764, 17</value>
</metadata>
<metadata name="ADBWirelessEnable_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>754, 17</value>
<metadata name="btnRunAdbCmd_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>581, 17</value>
</metadata>
<metadata name="ADBWirelessToggle_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="UpdateGamesButton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>545, 17</value>
<value>17, 54</value>
</metadata>
<metadata name="listApkButton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>376, 17</value>
</metadata>
<metadata name="speedLabel_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>219, 17</value>
<value>1320, 17</value>
</metadata>
<metadata name="etaLabel_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>178, 56</value>
<value>428, 54</value>
</metadata>
<metadata name="EnterInstallBox_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>514, 95</value>
<metadata name="favoriteGame.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>1021, 91</value>
</metadata>
<metadata name="btnViewToggle_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>215, 17</value>
</metadata>
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>113</value>
</metadata>
</root>

1308
ModernListView.cs Normal file

File diff suppressed because it is too large Load Diff

439
ModernProgessBar.cs Normal file
View File

@@ -0,0 +1,439 @@
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace AndroidSideloader
{
// A modern progress bar with rounded corners, left-to-right gradient fill,
// animated indeterminate mode, and optional status text overlay
[Description("Modern Themed Progress Bar")]
public class ModernProgressBar : Control
{
#region Fields
private float _value;
private float _minimum;
private float _maximum = 100f;
private int _radius = 8;
private bool _isIndeterminate;
private string _statusText = string.Empty;
private string _operationType = string.Empty;
// Indeterminate animation
private readonly Timer _animationTimer;
private float _animationOffset;
private const float AnimationSpeed = 4f;
private const int IndeterminateBlockWidth = 80;
// Colors
private Color _backgroundColor = Color.FromArgb(28, 32, 38);
private Color _progressStartColor = Color.FromArgb(120, 220, 190); // lighter accent
private Color _progressEndColor = Color.FromArgb(50, 160, 130); // darker accent
private Color _indeterminateColor = Color.FromArgb(93, 203, 173); // accent
private Color _textColor = Color.FromArgb(230, 230, 230);
private Color _textShadowColor = Color.FromArgb(90, 0, 0, 0);
#endregion
#region Constructor
public ModernProgressBar()
{
SetStyle(
ControlStyles.AllPaintingInWmPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.ResizeRedraw |
ControlStyles.UserPaint |
ControlStyles.SupportsTransparentBackColor,
true);
BackColor = Color.Transparent;
// Size + Font
Height = 28;
Width = 220;
Font = new Font("Segoe UI", 9f, FontStyle.Bold);
_animationTimer = new Timer { Interval = 16 }; // ~60fps
_animationTimer.Tick += AnimationTimer_Tick;
}
#endregion
#region Properties
[Category("Progress")]
[Description("The current value of the progress bar.")]
public float Value
{
get => _value;
set
{
_value = Math.Max(_minimum, Math.Min(_maximum, value));
Invalidate();
}
}
[Category("Progress")]
[Description("The minimum value of the progress bar.")]
public float Minimum
{
get => _minimum;
set
{
_minimum = value;
if (_value < _minimum) _value = _minimum;
Invalidate();
}
}
[Category("Progress")]
[Description("The maximum value of the progress bar.")]
public float Maximum
{
get => _maximum;
set
{
_maximum = value;
if (_value > _maximum) _value = _maximum;
Invalidate();
}
}
[Category("Appearance")]
[Description("The corner radius of the progress bar.")]
public int Radius
{
get => _radius;
set
{
_radius = Math.Max(0, value);
Invalidate();
}
}
[Category("Progress")]
[Description("Whether the progress bar shows indeterminate (marquee) progress.")]
public bool IsIndeterminate
{
get => _isIndeterminate;
set
{
// If there is no change, do nothing
if (_isIndeterminate == value)
return;
_isIndeterminate = value;
if (_isIndeterminate)
{
_animationOffset = -IndeterminateBlockWidth;
_animationTimer.Start();
}
else
{
_animationTimer.Stop();
}
Invalidate();
}
}
[Category("Appearance")]
[Description("Optional status text to display on the progress bar.")]
public string StatusText
{
get => _statusText;
set
{
_statusText = value ?? string.Empty;
Invalidate();
}
}
[Category("Appearance")]
[Description("Operation type label (e.g., 'Downloading', 'Installing').")]
public string OperationType
{
get => _operationType;
set
{
_operationType = value ?? string.Empty;
Invalidate();
}
}
[Category("Appearance")]
[Description("Background color of the progress bar track.")]
public Color BackgroundColor
{
get => _backgroundColor;
set { _backgroundColor = value; Invalidate(); }
}
[Category("Appearance")]
[Description("Start color of the progress gradient (left side).")]
public Color ProgressStartColor
{
get => _progressStartColor;
set { _progressStartColor = value; Invalidate(); }
}
[Category("Appearance")]
[Description("End color of the progress gradient (right side).")]
public Color ProgressEndColor
{
get => _progressEndColor;
set { _progressEndColor = value; Invalidate(); }
}
[Category("Appearance")]
[Description("Color used for indeterminate animation.")]
public Color IndeterminateColor
{
get => _indeterminateColor;
set { _indeterminateColor = value; Invalidate(); }
}
[Category("Appearance")]
[Description("Text color for status overlay.")]
public Color TextColor
{
get => _textColor;
set { _textColor = value; Invalidate(); }
}
// Gets the progress as a percentage (0-100)
public float ProgressPercent =>
_maximum > _minimum ? (_value - _minimum) / (_maximum - _minimum) * 100f : 0f;
#endregion
#region Painting
protected override void OnPaint(PaintEventArgs e)
{
var g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.Clear(BackColor);
int w = ClientSize.Width;
int h = ClientSize.Height;
if (w <= 0 || h <= 0) return;
var outerRect = new Rectangle(0, 0, w - 1, h - 1);
// Draw background track
using (var path = CreateRoundedPath(outerRect, _radius))
using (var bgBrush = new SolidBrush(_backgroundColor))
{
g.FillPath(bgBrush, path);
}
// Draw progress or indeterminate animation
if (_isIndeterminate)
{
DrawIndeterminate(g, outerRect);
}
else if (_value > _minimum)
{
DrawProgress(g, outerRect);
}
// Draw text overlay
DrawTextOverlay(g, outerRect);
base.OnPaint(e);
}
private void DrawProgress(Graphics g, Rectangle outerRect)
{
float percent = (_maximum > _minimum)
? (_value - _minimum) / (_maximum - _minimum)
: 0f;
if (percent <= 0f) return;
if (percent > 1f) percent = 1f;
int progressWidth = (int)Math.Round(outerRect.Width * percent);
if (progressWidth <= 0) return;
if (progressWidth > outerRect.Width) progressWidth = outerRect.Width;
using (var outerPath = CreateRoundedPath(outerRect, _radius))
{
// Clip to progress area inside rounded track
Rectangle progressRect = new Rectangle(outerRect.X, outerRect.Y, progressWidth, outerRect.Height);
using (var progressClip = new Region(progressRect))
using (var trackRegion = new Region(outerPath))
{
trackRegion.Intersect(progressClip);
Region prevClip = g.Clip;
try
{
g.SetClip(trackRegion, CombineMode.Replace);
// Left-to-right gradient, based on accent color
using (var gradientBrush = new LinearGradientBrush(
progressRect,
_progressStartColor,
_progressEndColor,
LinearGradientMode.Horizontal))
{
g.FillPath(gradientBrush, outerPath);
}
}
finally
{
g.Clip = prevClip;
}
}
}
}
private void DrawIndeterminate(Graphics g, Rectangle outerRect)
{
using (var outerPath = CreateRoundedPath(outerRect, _radius))
{
Region prevClip = g.Clip;
try
{
g.SetClip(outerPath, CombineMode.Replace);
int blockWidth = Math.Min(IndeterminateBlockWidth, outerRect.Width);
int blockX = (int)_animationOffset;
var blockRect = new Rectangle(blockX, outerRect.Y, blockWidth, outerRect.Height);
// Solid bar with slight left-to-right gradient
using (var brush = new LinearGradientBrush(
blockRect,
ControlPaint.Light(_indeterminateColor, 0.1f),
ControlPaint.Dark(_indeterminateColor, 0.1f),
LinearGradientMode.Horizontal))
{
g.FillRectangle(brush, blockRect);
}
}
finally
{
g.Clip = prevClip;
}
}
}
private void DrawTextOverlay(Graphics g, Rectangle outerRect)
{
string displayText = BuildDisplayText();
if (string.IsNullOrEmpty(displayText)) return;
using (var sf = new StringFormat
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center,
Trimming = StringTrimming.EllipsisCharacter
})
{
// Slight shadow for legibility on accent background
var shadowRect = new Rectangle(outerRect.X + 1, outerRect.Y + 1, outerRect.Width, outerRect.Height);
using (var shadowBrush = new SolidBrush(_textShadowColor))
{
g.DrawString(displayText, Font, shadowBrush, shadowRect, sf);
}
using (var textBrush = new SolidBrush(_textColor))
{
g.DrawString(displayText, Font, textBrush, outerRect, sf);
}
}
}
private string BuildDisplayText()
{
if (!string.IsNullOrEmpty(_statusText))
{
return _statusText;
}
if (_isIndeterminate && !string.IsNullOrEmpty(_operationType))
{
// E.g. "Downloading..."
return _operationType + "...";
}
if (!_isIndeterminate && _value > _minimum)
{
// Show one decimal place for sub-percent precision
string percentText = $"{ProgressPercent:0.0}%";
if (!string.IsNullOrEmpty(_operationType))
{
// E.g. "Downloading · 73.5%"
return $"{_operationType} · {percentText}";
}
return percentText;
}
return string.Empty;
}
private GraphicsPath CreateRoundedPath(Rectangle rect, int radius)
{
var path = new GraphicsPath();
if (radius <= 0)
{
path.AddRectangle(rect);
return path;
}
int diameter = radius * 2;
diameter = Math.Min(diameter, Math.Min(rect.Width, rect.Height));
radius = diameter / 2;
var arcRect = new Rectangle(rect.Location, new Size(diameter, diameter));
path.AddArc(arcRect, 180, 90);
arcRect.X = rect.Right - diameter;
path.AddArc(arcRect, 270, 90);
arcRect.Y = rect.Bottom - diameter;
path.AddArc(arcRect, 0, 90);
arcRect.X = rect.Left;
path.AddArc(arcRect, 90, 90);
path.CloseFigure();
return path;
}
#endregion
#region Animation
private void AnimationTimer_Tick(object sender, EventArgs e)
{
_animationOffset += AnimationSpeed;
if (_animationOffset > ClientSize.Width + IndeterminateBlockWidth)
{
_animationOffset = -IndeterminateBlockWidth;
}
Invalidate();
}
#endregion
#region Cleanup
protected override void Dispose(bool disposing)
{
if (disposing)
{
_animationTimer?.Stop();
_animationTimer?.Dispose();
}
base.Dispose(disposing);
}
#endregion
}
}

511
ModernQueuePanel.cs Normal file
View File

@@ -0,0 +1,511 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace AndroidSideloader
{
// Modern download queue panel with drag-reorder, cancel buttons
// and custom scrollbar with auto-scrolling during drag
public sealed class ModernQueuePanel : Control
{
// Layout constants
private const int ItemHeight = 28, ItemMargin = 4, ItemRadius = 5;
private const int XButtonSize = 18, DragHandleWidth = 20, TextPadding = 6;
private const int ScrollbarWidth = 6, ScrollbarWidthHover = 8, ScrollbarMargin = 2;
private const int ScrollbarRadius = 3, MinThumbHeight = 20;
private const int AutoScrollZoneHeight = 30, AutoScrollSpeed = 3;
// Color palette
private static readonly Color BgColor = Color.FromArgb(24, 26, 30);
private static readonly Color ItemBg = Color.FromArgb(32, 36, 44);
private static readonly Color ItemHoverBg = Color.FromArgb(42, 46, 54);
private static readonly Color ItemDragBg = Color.FromArgb(45, 55, 70);
private static readonly Color TextColor = Color.FromArgb(210, 210, 210);
private static readonly Color TextDimColor = Color.FromArgb(140, 140, 140);
private static readonly Color AccentColor = Color.FromArgb(93, 203, 173);
private static readonly Color XButtonBg = Color.FromArgb(55, 60, 70);
private static readonly Color XButtonHoverBg = Color.FromArgb(200, 60, 60);
private static readonly Color GripColor = Color.FromArgb(70, 75, 85);
private static readonly Color ItemDragBorder = Color.FromArgb(55, 65, 80);
private static readonly Color ScrollTrackColor = Color.FromArgb(35, 38, 45);
private static readonly Color ScrollThumbColor = Color.FromArgb(70, 75, 85);
private static readonly Color ScrollThumbHoverColor = Color.FromArgb(90, 95, 105);
private static readonly Color ScrollThumbDragColor = Color.FromArgb(110, 115, 125);
private readonly List<string> _items = new List<string>();
private readonly Timer _autoScrollTimer;
// State tracking
private int _hoveredIndex = -1, _dragIndex = -1, _dropIndex = -1, _scrollOffset;
private bool _hoveringX, _scrollbarHovered, _scrollbarDragging;
private int _scrollDragStartY, _scrollDragStartOffset, _autoScrollDirection;
private Rectangle _scrollThumbRect, _scrollTrackRect;
public event EventHandler<int> ItemRemoved;
public event EventHandler<ReorderEventArgs> ItemReordered;
public ModernQueuePanel()
{
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);
BackColor = BgColor;
_autoScrollTimer = new Timer { Interval = 16 }; // ~60 FPS
_autoScrollTimer.Tick += (s, e) => HandleAutoScroll();
}
public bool IsDownloading { get; set; }
public int Count => _items.Count;
private int ContentHeight => _items.Count * (ItemHeight + ItemMargin) + ItemMargin;
private int MaxScroll => Math.Max(0, ContentHeight - Height);
private bool ScrollbarVisible => ContentHeight > Height;
public void SetItems(IEnumerable<string> items)
{
_items.Clear();
_items.AddRange(items);
ResetState();
}
private void ResetState()
{
_hoveredIndex = _dragIndex = -1;
ClampScroll();
Invalidate();
}
private void ClampScroll() =>
_scrollOffset = Math.Max(0, Math.Min(MaxScroll, _scrollOffset));
// Auto-scroll when dragging near edges
private void HandleAutoScroll()
{
if (_dragIndex < 0 || _autoScrollDirection == 0)
{
_autoScrollTimer.Stop();
return;
}
int oldOffset = _scrollOffset;
_scrollOffset += _autoScrollDirection * AutoScrollSpeed;
ClampScroll();
if (_scrollOffset != oldOffset)
{
UpdateDropIndex(PointToClient(MousePosition).Y);
Invalidate();
}
}
private void UpdateAutoScroll(int mouseY)
{
if (_dragIndex < 0 || MaxScroll <= 0)
{
StopAutoScroll();
return;
}
_autoScrollDirection = mouseY < AutoScrollZoneHeight && _scrollOffset > 0 ? -1 :
mouseY > Height - AutoScrollZoneHeight && _scrollOffset < MaxScroll ? 1 : 0;
if (_autoScrollDirection != 0 && !_autoScrollTimer.Enabled)
_autoScrollTimer.Start();
else if (_autoScrollDirection == 0)
_autoScrollTimer.Stop();
}
private void StopAutoScroll()
{
_autoScrollDirection = 0;
_autoScrollTimer.Stop();
}
private void UpdateDropIndex(int mouseY) =>
_dropIndex = Math.Max(1, Math.Min(_items.Count, (mouseY + _scrollOffset + ItemHeight / 2) / (ItemHeight + ItemMargin)));
protected override void OnPaint(PaintEventArgs e)
{
var g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.Clear(BgColor);
if (_items.Count == 0)
{
DrawEmptyState(g);
return;
}
// Draw visible items
for (int i = 0; i < _items.Count; i++)
{
var rect = GetItemRect(i);
if (rect.Bottom >= 0 && rect.Top <= Height)
DrawItem(g, i, rect);
}
// Draw drop indicator and scrollbar
if (_dragIndex >= 0 && _dropIndex >= 0 && _dropIndex != _dragIndex)
DrawDropIndicator(g);
if (ScrollbarVisible)
DrawScrollbar(g);
}
private void DrawEmptyState(Graphics g)
{
using (var brush = new SolidBrush(TextDimColor))
using (var font = new Font("Segoe UI", 8.5f, FontStyle.Italic))
{
var sf = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
g.DrawString("Queue is empty", font, brush, ClientRectangle, sf);
}
}
private void DrawItem(Graphics g, int index, Rectangle rect)
{
bool isFirst = index == 0;
bool isDragging = index == _dragIndex;
bool isHovered = !isDragging && index == _hoveredIndex;
Color bg = isDragging ? ItemDragBg : isHovered ? ItemHoverBg : ItemBg;
// Draw item background
using (var path = CreateRoundedRect(rect, ItemRadius))
using (var brush = new SolidBrush(bg))
g.FillPath(brush, path);
// Active download (first item) gets gradient accent and border
if (isFirst)
DrawFirstItemAccent(g, rect);
// Dragged items get subtle highlight border
else if (isDragging)
DrawBorder(g, rect, ItemDragBorder, 0.5f);
// Draw drag handle, text, and close button
if (!isFirst)
DrawDragHandle(g, rect);
DrawItemText(g, index, rect, isFirst);
DrawXButton(g, rect, isHovered && _hoveringX);
}
// Draw gradient accent and border for active download (first item)
private void DrawFirstItemAccent(Graphics g, Rectangle rect)
{
using (var path = CreateRoundedRect(rect, ItemRadius))
using (var gradBrush = new LinearGradientBrush(rect,
Color.FromArgb(60, AccentColor), Color.FromArgb(0, AccentColor), LinearGradientMode.Horizontal))
{
var oldClip = g.Clip;
g.SetClip(path);
g.FillRectangle(gradBrush, rect);
g.Clip = oldClip;
}
DrawBorder(g, rect, AccentColor, 1.5f);
}
private void DrawBorder(Graphics g, Rectangle rect, Color color, float width)
{
using (var path = CreateRoundedRect(rect, ItemRadius))
using (var pen = new Pen(color, width))
g.DrawPath(pen, path);
}
private void DrawDragHandle(Graphics g, Rectangle rect)
{
int cx = rect.X + 8, cy = rect.Y + rect.Height / 2;
using (var brush = new SolidBrush(GripColor))
{
for (int row = -1; row <= 1; row++)
for (int col = 0; col < 2; col++)
g.FillEllipse(brush, cx + col * 4, cy + row * 4 - 1, 2, 2);
}
}
private void DrawItemText(Graphics g, int index, Rectangle rect, bool isFirst)
{
int textLeft = isFirst ? rect.X + TextPadding : rect.X + DragHandleWidth;
int rightPad = ScrollbarVisible ? ScrollbarWidthHover + ScrollbarMargin * 2 : 0;
var textRect = new Rectangle(textLeft, rect.Y, rect.Right - XButtonSize - 6 - textLeft - rightPad, rect.Height);
using (var brush = new SolidBrush(TextColor))
using (var font = new Font("Segoe UI", isFirst ? 8.5f : 8f, isFirst ? FontStyle.Bold : FontStyle.Regular))
{
var sf = new StringFormat
{
Alignment = StringAlignment.Near,
LineAlignment = StringAlignment.Center,
Trimming = StringTrimming.EllipsisCharacter,
FormatFlags = StringFormatFlags.NoWrap
};
g.DrawString(_items[index], font, brush, textRect, sf);
}
}
private void DrawXButton(Graphics g, Rectangle itemRect, bool hovered)
{
var xRect = GetXButtonRect(itemRect);
using (var path = CreateRoundedRect(xRect, 3))
using (var brush = new SolidBrush(hovered ? XButtonHoverBg : XButtonBg))
g.FillPath(brush, path);
using (var pen = new Pen(Color.White, 1.4f) { StartCap = LineCap.Round, EndCap = LineCap.Round })
{
int p = 4;
g.DrawLine(pen, xRect.X + p, xRect.Y + p, xRect.Right - p, xRect.Bottom - p);
g.DrawLine(pen, xRect.Right - p, xRect.Y + p, xRect.X + p, xRect.Bottom - p);
}
}
private void DrawDropIndicator(Graphics g)
{
int y = (_dropIndex >= _items.Count ? _items.Count : _dropIndex) * (ItemHeight + ItemMargin) + ItemMargin / 2 - _scrollOffset;
int left = ItemMargin + 2;
int right = Width - ItemMargin - 2 - (ScrollbarVisible ? ScrollbarWidthHover + ScrollbarMargin : 0);
using (var pen = new Pen(AccentColor, 2.5f) { StartCap = LineCap.Round, EndCap = LineCap.Round })
g.DrawLine(pen, left, y, right, y);
}
// Draw custom scrollbar with hover expansion
private void DrawScrollbar(Graphics g)
{
if (MaxScroll <= 0) return;
bool expanded = _scrollbarHovered || _scrollbarDragging;
int sbWidth = expanded ? ScrollbarWidthHover : ScrollbarWidth;
int trackX = Width - ScrollbarWidth - ScrollbarMargin - (expanded ? (ScrollbarWidthHover - ScrollbarWidth) / 2 : 0);
_scrollTrackRect = new Rectangle(trackX, ScrollbarMargin, sbWidth, Height - ScrollbarMargin * 2);
using (var trackBrush = new SolidBrush(Color.FromArgb(40, ScrollTrackColor)))
using (var trackPath = CreateRoundedRect(_scrollTrackRect, ScrollbarRadius))
g.FillPath(trackBrush, trackPath);
// Calculate thumb position and size
int trackHeight = _scrollTrackRect.Height;
int thumbHeight = Math.Max(MinThumbHeight, (int)(trackHeight * ((float)Height / ContentHeight)));
float scrollRatio = MaxScroll > 0 ? (float)_scrollOffset / MaxScroll : 0;
int thumbY = ScrollbarMargin + (int)((trackHeight - thumbHeight) * scrollRatio);
_scrollThumbRect = new Rectangle(trackX, thumbY, sbWidth, thumbHeight);
Color thumbColor = _scrollbarDragging ? ScrollThumbDragColor : _scrollbarHovered ? ScrollThumbHoverColor : ScrollThumbColor;
using (var thumbBrush = new SolidBrush(thumbColor))
using (var thumbPath = CreateRoundedRect(_scrollThumbRect, ScrollbarRadius))
g.FillPath(thumbBrush, thumbPath);
}
private Rectangle GetItemRect(int index)
{
int y = index * (ItemHeight + ItemMargin) + ItemMargin - _scrollOffset;
int w = Width - ItemMargin * 2 - (ScrollbarVisible ? ScrollbarWidthHover + ScrollbarMargin + 2 : 0);
return new Rectangle(ItemMargin, y, w, ItemHeight);
}
private Rectangle GetXButtonRect(Rectangle itemRect) =>
new Rectangle(itemRect.Right - XButtonSize - 3, itemRect.Y + (itemRect.Height - XButtonSize) / 2, XButtonSize, XButtonSize);
private int HitTest(Point pt)
{
for (int i = 0; i < _items.Count; i++)
if (GetItemRect(i).Contains(pt)) return i;
return -1;
}
private bool HitTestScrollbar(Point pt) =>
ScrollbarVisible && new Rectangle(_scrollTrackRect.X - 4, _scrollTrackRect.Y, _scrollTrackRect.Width + 8, _scrollTrackRect.Height).Contains(pt);
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (_scrollbarDragging)
{
HandleScrollbarDrag(e.Y);
return;
}
// Update scrollbar hover state
bool wasHovered = _scrollbarHovered;
_scrollbarHovered = HitTestScrollbar(e.Location);
if (_scrollbarHovered != wasHovered) Invalidate();
if (_scrollbarHovered)
{
Cursor = Cursors.Default;
_hoveredIndex = -1;
_hoveringX = false;
return;
}
// Handle drag operation
if (_dragIndex >= 0)
{
UpdateAutoScroll(e.Y);
int newDrop = Math.Max(1, Math.Min(_items.Count, (e.Y + _scrollOffset + ItemHeight / 2) / (ItemHeight + ItemMargin)));
if (newDrop != _dropIndex) { _dropIndex = newDrop; Invalidate(); }
return;
}
// Update hover state
int hit = HitTest(e.Location);
bool overX = hit >= 0 && GetXButtonRect(GetItemRect(hit)).Contains(e.Location);
if (hit != _hoveredIndex || overX != _hoveringX)
{
_hoveredIndex = hit;
_hoveringX = overX;
Cursor = overX ? Cursors.Hand : hit > 0 ? Cursors.SizeNS : Cursors.Default;
Invalidate();
}
}
private void HandleScrollbarDrag(int mouseY)
{
int trackHeight = Height - ScrollbarMargin * 2;
int thumbHeight = Math.Max(MinThumbHeight, (int)(trackHeight * ((float)Height / ContentHeight)));
int scrollableHeight = trackHeight - thumbHeight;
if (scrollableHeight > 0)
{
float scrollRatio = (float)(mouseY - _scrollDragStartY) / scrollableHeight;
_scrollOffset = _scrollDragStartOffset + (int)(scrollRatio * MaxScroll);
ClampScroll();
Invalidate();
}
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (e.Button != MouseButtons.Left) return;
// Handle scrollbar thumb drag
if (ScrollbarVisible && _scrollThumbRect.Contains(e.Location))
{
_scrollbarDragging = true;
_scrollDragStartY = e.Y;
_scrollDragStartOffset = _scrollOffset;
Capture = true;
return;
}
// Handle scrollbar track click
if (ScrollbarVisible && HitTestScrollbar(e.Location))
{
_scrollOffset += e.Y < _scrollThumbRect.Top ? -Height : Height;
ClampScroll();
Invalidate();
return;
}
int hit = HitTest(e.Location);
if (hit < 0) return;
// Handle close button click
if (GetXButtonRect(GetItemRect(hit)).Contains(e.Location))
{
ItemRemoved?.Invoke(this, hit);
return;
}
// Start drag operation (only for non-first items)
if (hit > 0)
{
_dragIndex = _dropIndex = hit;
Capture = true;
Invalidate();
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
StopAutoScroll();
if (_scrollbarDragging)
{
_scrollbarDragging = false;
Capture = false;
Invalidate();
return;
}
// Complete drag reorder operation
if (_dragIndex > 0 && _dropIndex > 0 && _dropIndex != _dragIndex)
{
int from = _dragIndex;
int to = Math.Max(1, _dropIndex > _dragIndex ? _dropIndex - 1 : _dropIndex);
var item = _items[from];
_items.RemoveAt(from);
_items.Insert(to, item);
ItemReordered?.Invoke(this, new ReorderEventArgs(from, to));
}
_dragIndex = _dropIndex = -1;
Capture = false;
Cursor = Cursors.Default;
Invalidate();
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
if (_dragIndex < 0 && !_scrollbarDragging)
{
_hoveredIndex = -1;
_hoveringX = _scrollbarHovered = false;
Invalidate();
}
}
protected override void OnMouseWheel(MouseEventArgs e)
{
base.OnMouseWheel(e);
if (MaxScroll <= 0) return;
_scrollOffset -= e.Delta / 4;
ClampScroll();
Invalidate();
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
ClampScroll();
Invalidate();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_autoScrollTimer?.Stop();
_autoScrollTimer?.Dispose();
}
base.Dispose(disposing);
}
private static GraphicsPath CreateRoundedRect(Rectangle rect, int radius)
{
var path = new GraphicsPath();
if (radius <= 0 || rect.Width <= 0 || rect.Height <= 0)
{
path.AddRectangle(rect);
return path;
}
int d = Math.Min(radius * 2, Math.Min(rect.Width, rect.Height));
path.AddArc(rect.X, rect.Y, d, d, 180, 90);
path.AddArc(rect.Right - d, rect.Y, d, d, 270, 90);
path.AddArc(rect.Right - d, rect.Bottom - d, d, d, 0, 90);
path.AddArc(rect.X, rect.Bottom - d, d, d, 90, 90);
path.CloseFigure();
return path;
}
}
public class ReorderEventArgs : EventArgs
{
public int FromIndex { get; }
public int ToIndex { get; }
public ReorderEventArgs(int from, int to) { FromIndex = from; ToIndex = to; }
}
}

144
NewApps.Designer.cs generated
View File

@@ -32,31 +32,34 @@ namespace AndroidSideloader
this.NewAppsListView = new System.Windows.Forms.ListView();
this.GameNameIndex = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.PackageNameIndex = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.panel2 = new System.Windows.Forms.Panel();
this.label2 = new System.Windows.Forms.Label();
this.panel1 = new System.Windows.Forms.Panel();
this.NewAppsButton = new AndroidSideloader.RoundButton();
this.label2 = new System.Windows.Forms.Label();
this.titleLabel = new System.Windows.Forms.Label();
this.panel2 = new System.Windows.Forms.Panel();
this.panel1.SuspendLayout();
this.panel2.SuspendLayout();
this.SuspendLayout();
//
// NewAppsListView
//
this.NewAppsListView.AccessibleRole = System.Windows.Forms.AccessibleRole.None;
this.NewAppsListView.BackColor = global::AndroidSideloader.Properties.Settings.Default.BackColor;
this.NewAppsListView.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(28)))), ((int)(((byte)(32)))), ((int)(((byte)(38)))));
this.NewAppsListView.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.NewAppsListView.CausesValidation = false;
this.NewAppsListView.CheckBoxes = true;
this.NewAppsListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.GameNameIndex,
this.PackageNameIndex});
this.NewAppsListView.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "BackColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.NewAppsListView.Dock = System.Windows.Forms.DockStyle.Fill;
this.NewAppsListView.Font = new System.Drawing.Font("Segoe UI", 9.5F);
this.NewAppsListView.ForeColor = System.Drawing.Color.White;
this.NewAppsListView.FullRowSelect = true;
this.NewAppsListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
this.NewAppsListView.HideSelection = false;
this.NewAppsListView.Location = new System.Drawing.Point(3, 5);
this.NewAppsListView.Location = new System.Drawing.Point(1, 1);
this.NewAppsListView.Name = "NewAppsListView";
this.NewAppsListView.RightToLeftLayout = true;
this.NewAppsListView.Size = new System.Drawing.Size(288, 167);
this.NewAppsListView.Size = new System.Drawing.Size(298, 168);
this.NewAppsListView.TabIndex = 1;
this.NewAppsListView.UseCompatibleStateImageBehavior = false;
this.NewAppsListView.View = System.Windows.Forms.View.Details;
@@ -66,82 +69,119 @@ namespace AndroidSideloader
//
// GameNameIndex
//
this.GameNameIndex.Text = "Game Name";
this.GameNameIndex.Width = 284;
this.GameNameIndex.Text = "App Name";
this.GameNameIndex.Width = 280;
//
// PackageNameIndex
//
this.PackageNameIndex.Width = 0;
//
// panel2
// panel1
//
this.panel2.BackColor = global::AndroidSideloader.Properties.Settings.Default.SubButtonColor;
this.panel2.Controls.Add(this.NewAppsListView);
this.panel2.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "SubButtonColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.panel2.Location = new System.Drawing.Point(9, 31);
this.panel2.Name = "panel2";
this.panel2.Size = new System.Drawing.Size(295, 175);
this.panel2.TabIndex = 8;
this.panel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(24)))), ((int)(((byte)(29)))));
this.panel1.Controls.Add(this.NewAppsButton);
this.panel1.Controls.Add(this.label2);
this.panel1.Controls.Add(this.titleLabel);
this.panel1.Controls.Add(this.panel2);
this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.panel1.Location = new System.Drawing.Point(0, 0);
this.panel1.Name = "panel1";
this.panel1.Padding = new System.Windows.Forms.Padding(16);
this.panel1.Size = new System.Drawing.Size(340, 300);
this.panel1.TabIndex = 10;
this.panel1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.label2_MouseDown);
this.panel1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.label2_MouseMove);
this.panel1.MouseUp += new System.Windows.Forms.MouseEventHandler(this.label2_MouseUp);
//
// NewAppsButton
//
this.NewAppsButton.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(140)))), ((int)(((byte)(115)))));
this.NewAppsButton.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(125)))), ((int)(((byte)(105)))));
this.NewAppsButton.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.NewAppsButton.BackColor = System.Drawing.Color.Transparent;
this.NewAppsButton.DialogResult = System.Windows.Forms.DialogResult.OK;
this.NewAppsButton.Disabled1 = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
this.NewAppsButton.Disabled2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(28)))), ((int)(((byte)(35)))));
this.NewAppsButton.DisabledStrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
this.NewAppsButton.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Bold);
this.NewAppsButton.ForeColor = System.Drawing.Color.White;
this.NewAppsButton.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(120)))), ((int)(((byte)(100)))));
this.NewAppsButton.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(35)))), ((int)(((byte)(100)))), ((int)(((byte)(85)))));
this.NewAppsButton.Location = new System.Drawing.Point(20, 252);
this.NewAppsButton.Name = "NewAppsButton";
this.NewAppsButton.Radius = 4;
this.NewAppsButton.Size = new System.Drawing.Size(300, 36);
this.NewAppsButton.Stroke = true;
this.NewAppsButton.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(150)))), ((int)(((byte)(125)))));
this.NewAppsButton.TabIndex = 2;
this.NewAppsButton.Text = "Continue";
this.NewAppsButton.Transparency = false;
this.NewAppsButton.Click += new System.EventHandler(this.DonateButton_Click);
//
// label2
//
this.label2.AutoSize = true;
this.label2.BackColor = System.Drawing.Color.Transparent;
this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 10.25F, System.Drawing.FontStyle.Bold);
this.label2.ForeColor = System.Drawing.SystemColors.ControlLightLight;
this.label2.Location = new System.Drawing.Point(28, 7);
this.label2.Font = new System.Drawing.Font("Segoe UI", 9F);
this.label2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(160)))), ((int)(((byte)(165)))), ((int)(((byte)(175)))));
this.label2.Location = new System.Drawing.Point(20, 42);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(256, 17);
this.label2.Size = new System.Drawing.Size(223, 15);
this.label2.TabIndex = 9;
this.label2.Text = "Check box of all free/non-VR apps";
this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.label2.Text = "Check the box for all free or non-VR apps";
this.label2.MouseDown += new System.Windows.Forms.MouseEventHandler(this.label2_MouseDown);
this.label2.MouseMove += new System.Windows.Forms.MouseEventHandler(this.label2_MouseMove);
this.label2.MouseUp += new System.Windows.Forms.MouseEventHandler(this.label2_MouseUp);
//
// NewAppsButton
// titleLabel
//
this.NewAppsButton.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
this.NewAppsButton.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
this.NewAppsButton.BackColor = System.Drawing.Color.Transparent;
this.NewAppsButton.DialogResult = System.Windows.Forms.DialogResult.OK;
this.NewAppsButton.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F);
this.NewAppsButton.ForeColor = System.Drawing.Color.White;
this.NewAppsButton.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
this.NewAppsButton.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
this.NewAppsButton.Location = new System.Drawing.Point(12, 212);
this.NewAppsButton.Name = "NewAppsButton";
this.NewAppsButton.Radius = 5;
this.NewAppsButton.Size = new System.Drawing.Size(288, 29);
this.NewAppsButton.Stroke = true;
this.NewAppsButton.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(74)))), ((int)(((byte)(74)))), ((int)(((byte)(74)))));
this.NewAppsButton.TabIndex = 2;
this.NewAppsButton.Text = "Accept";
this.NewAppsButton.Transparency = false;
this.NewAppsButton.Click += new System.EventHandler(this.DonateButton_Click);
this.titleLabel.AutoSize = true;
this.titleLabel.BackColor = System.Drawing.Color.Transparent;
this.titleLabel.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Bold);
this.titleLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
this.titleLabel.Location = new System.Drawing.Point(20, 15);
this.titleLabel.Name = "titleLabel";
this.titleLabel.Size = new System.Drawing.Size(129, 20);
this.titleLabel.TabIndex = 11;
this.titleLabel.Text = "New Apps Found";
this.titleLabel.MouseDown += new System.Windows.Forms.MouseEventHandler(this.label2_MouseDown);
this.titleLabel.MouseMove += new System.Windows.Forms.MouseEventHandler(this.label2_MouseMove);
this.titleLabel.MouseUp += new System.Windows.Forms.MouseEventHandler(this.label2_MouseUp);
//
// panel2
//
this.panel2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panel2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(28)))), ((int)(((byte)(32)))), ((int)(((byte)(38)))));
this.panel2.Controls.Add(this.NewAppsListView);
this.panel2.Location = new System.Drawing.Point(20, 70);
this.panel2.Name = "panel2";
this.panel2.Padding = new System.Windows.Forms.Padding(1);
this.panel2.Size = new System.Drawing.Size(300, 170);
this.panel2.TabIndex = 8;
//
// NewApps
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = global::AndroidSideloader.Properties.Settings.Default.BackColor;
this.BackgroundImage = global::AndroidSideloader.Properties.Resources.pattern_cubes_1_1_1_0_0_0_1__000000_212121;
this.ClientSize = new System.Drawing.Size(313, 248);
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(24)))), ((int)(((byte)(29)))));
this.ClientSize = new System.Drawing.Size(340, 300);
this.ControlBox = false;
this.Controls.Add(this.NewAppsButton);
this.Controls.Add(this.label2);
this.Controls.Add(this.panel2);
this.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "BackColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D;
this.Controls.Add(this.panel1);
this.ForeColor = System.Drawing.Color.White;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.Name = "NewApps";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Load += new System.EventHandler(this.NewApps_Load);
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.label2_MouseDown);
this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.label2_MouseMove);
this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.label2_MouseUp);
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
this.panel2.ResumeLayout(false);
this.ResumeLayout(false);
this.PerformLayout();
}
@@ -149,8 +189,10 @@ namespace AndroidSideloader
private System.Windows.Forms.ListView NewAppsListView;
private System.Windows.Forms.ColumnHeader GameNameIndex;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Panel panel2;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label titleLabel;
private System.Windows.Forms.ColumnHeader PackageNameIndex;
private RoundButton NewAppsButton;
}

View File

@@ -1,59 +1,186 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace AndroidSideloader
{
public partial class NewApps : Form
{
private bool mouseDown;
private Point lastLocation;
// Modern theme colors
private static readonly Color BackgroundColor = Color.FromArgb(20, 24, 29);
private static readonly Color BorderColor = Color.FromArgb(70, 80, 100);
// Shadow and corner settings
private const int CS_DROPSHADOW = 0x00020000;
private const int WM_NCLBUTTONDOWN = 0xA1;
private const int HT_CAPTION = 0x2;
private const int SHADOW_SIZE = 2;
private const int CONTENT_RADIUS = 10;
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
private static extern bool ReleaseCapture();
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ClassStyle |= CS_DROPSHADOW;
return cp;
}
}
public NewApps()
{
InitializeComponent();
// Use same icon as the executable
this.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
ApplyModernTheme();
CenterToScreen();
}
private void label2_MouseDown(object sender, MouseEventArgs e)
private void ApplyModernTheme()
{
mouseDown = true;
lastLocation = e.Location;
}
this.FormBorderStyle = FormBorderStyle.None;
this.BackColor = Color.FromArgb(25, 25, 30);
this.Padding = new Padding(5);
private void label2_MouseMove(object sender, MouseEventArgs e)
{
if (mouseDown)
panel1.BackColor = BackgroundColor;
panel1.Location = new Point(6, 6);
panel1.Size = new Size(this.ClientSize.Width - 12, this.ClientSize.Height - 12);
panel1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
this.Paint += Form_Paint;
// Close button
var closeButton = new Button
{
Location = new Point(
Location.X - lastLocation.X + e.X, Location.Y - lastLocation.Y + e.Y);
Text = "✕",
Font = new Font("Segoe UI", 9F),
ForeColor = Color.White,
BackColor = BackgroundColor,
FlatStyle = FlatStyle.Flat,
Size = new Size(30, 28),
Location = new Point(panel1.Width - 35, 5),
Cursor = Cursors.Hand,
TabStop = false
};
closeButton.FlatAppearance.BorderSize = 0;
closeButton.FlatAppearance.MouseOverBackColor = Color.FromArgb(200, 60, 60);
closeButton.Click += (s, e) => Close();
panel1.Controls.Add(closeButton);
closeButton.BringToFront();
Update();
// Enable dragging
panel1.MouseDown += TitleArea_MouseDown;
label2.MouseDown += TitleArea_MouseDown;
titleLabel.MouseDown += TitleArea_MouseDown;
}
private void TitleArea_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
ReleaseCapture();
SendMessage(this.Handle, WM_NCLBUTTONDOWN, (IntPtr)HT_CAPTION, IntPtr.Zero);
}
}
private void label2_MouseUp(object sender, MouseEventArgs e)
private void Form_Paint(object sender, PaintEventArgs e)
{
mouseDown = false;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
int w = this.Width;
int h = this.Height;
// Draw shadow layers
for (int i = SHADOW_SIZE; i >= 1; i--)
{
int alpha = (SHADOW_SIZE - i + 1) * 12;
Rectangle shadowRect = new Rectangle(
SHADOW_SIZE - i,
SHADOW_SIZE - i,
w - (SHADOW_SIZE - i) * 2 - 1,
h - (SHADOW_SIZE - i) * 2 - 1);
using (Pen shadowPen = new Pen(Color.FromArgb(alpha, 0, 0, 0), 1))
using (GraphicsPath shadowPath = CreateRoundedRectPath(shadowRect, CONTENT_RADIUS + i))
{
e.Graphics.DrawPath(shadowPen, shadowPath);
}
}
// Draw content background
Rectangle contentRect = new Rectangle(SHADOW_SIZE, SHADOW_SIZE, w - SHADOW_SIZE * 2, h - SHADOW_SIZE * 2);
using (GraphicsPath contentPath = CreateRoundedRectPath(contentRect, CONTENT_RADIUS))
{
using (SolidBrush bgBrush = new SolidBrush(BackgroundColor))
{
e.Graphics.FillPath(bgBrush, contentPath);
}
using (Pen borderPen = new Pen(BorderColor, 1f))
{
e.Graphics.DrawPath(borderPen, contentPath);
}
}
// Apply rounded region
using (GraphicsPath regionPath = CreateRoundedRectPath(new Rectangle(0, 0, w, h), CONTENT_RADIUS + SHADOW_SIZE))
{
this.Region = new Region(regionPath);
}
}
private GraphicsPath CreateRoundedRectPath(Rectangle rect, int radius)
{
GraphicsPath path = new GraphicsPath();
if (radius <= 0)
{
path.AddRectangle(rect);
return path;
}
int diameter = Math.Min(radius * 2, Math.Min(rect.Width, rect.Height));
radius = diameter / 2;
Rectangle arcRect = new Rectangle(rect.Location, new Size(diameter, diameter));
path.AddArc(arcRect, 180, 90);
arcRect.X = rect.Right - diameter;
path.AddArc(arcRect, 270, 90);
arcRect.Y = rect.Bottom - diameter;
path.AddArc(arcRect, 0, 90);
arcRect.X = rect.Left;
path.AddArc(arcRect, 90, 90);
path.CloseFigure();
return path;
}
private void label2_MouseDown(object sender, MouseEventArgs e) => TitleArea_MouseDown(sender, e);
private void label2_MouseMove(object sender, MouseEventArgs e) { }
private void label2_MouseUp(object sender, MouseEventArgs e) { }
private void DonateButton_Click(object sender, EventArgs e)
{
string HWID = SideloaderUtilities.UUID();
foreach (ListViewItem listItem in NewAppsListView.Items)
{
if (listItem.Checked)
{
Properties.Settings.Default.NonAppPackages += listItem.SubItems[Donors.PackageNameIndex].Text + ";" + HWID + "\n";
Properties.Settings.Default.Save();
}
else
{
Properties.Settings.Default.AppPackages += listItem.SubItems[Donors.PackageNameIndex].Text + "\n";
Properties.Settings.Default.Save();
}
Properties.Settings.Default.Save();
}
MainForm.newpackageupload();
MainForm.newPackageUpload();
Close();
}
@@ -61,20 +188,16 @@ namespace AndroidSideloader
{
NewAppsListView.Items.Clear();
Donors.initNewApps();
List<ListViewItem> NewAppList = new List<ListViewItem>();
var NewAppList = new List<ListViewItem>();
foreach (string[] release in Donors.newApps)
{
ListViewItem NGame = new ListViewItem(release);
if (!NewAppList.Contains(NGame))
{
NewAppList.Add(NGame);
}
}
ListViewItem[] arr = NewAppList.ToArray();
NewAppsListView.BeginUpdate();
NewAppsListView.Items.Clear();
NewAppsListView.Items.AddRange(arr);
NewAppsListView.Items.AddRange(NewAppList.ToArray());
NewAppsListView.EndUpdate();
}
}
}
}

View File

@@ -1,40 +1,110 @@
using System;
using System.IO;
using System.Security.Permissions;
using System.Windows.Forms;
namespace AndroidSideloader
{
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlAppDomain)]
private static void Main()
{
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
form = new MainForm();
Application.Run(form);
//form.Show();
}
public static MainForm form;
private static void MyHandler(object sender, UnhandledExceptionEventArgs args)
{
Exception e = (Exception)args.ExceptionObject;
string date_time = DateTime.Now.ToString("dddd, MMMM dd @ hh:mmtt (UTC)");
File.WriteAllText(Sideloader.CrashLogPath, $"Date/Time of crash: {date_time}\nMessage: {e.Message}\nData: {e.Data}\nSource: {e.Source}\nTargetSite: {e.TargetSite}\n\n\nDebuglog: \n\n\n");
if (File.Exists(Properties.Settings.Default.CurrentLogPath))
{
File.AppendAllText(Sideloader.CrashLogPath, File.ReadAllText($"{Properties.Settings.Default.CurrentLogPath}"));
}
}
}
using AndroidSideloader.Utilities;
using System;
using System.IO;
using System.Security.Permissions;
using System.Windows.Forms;
namespace AndroidSideloader
{
internal static class Program
{
private static SettingsManager settings;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlAppDomain)]
private static void Main()
{
// Handle corrupted user.config files
bool configFixed = false;
Exception configException = null;
try
{
// Force settings initialization to trigger any config errors early
var test = AndroidSideloader.Properties.Settings.Default.FontStyle;
}
catch (Exception ex)
{
configException = ex;
// Delete the corrupted config file and retry
try
{
string configPath = GetUserConfigPath();
if (!string.IsNullOrEmpty(configPath) && File.Exists(configPath))
{
File.Delete(configPath);
configFixed = true;
}
}
catch
{
// If we can't delete it, try to continue anyway
}
}
if (configFixed)
{
// Restart the application after fixing config
Application.Restart();
return;
}
if (configException != null)
{
MessageBox.Show(
"Settings file is corrupted and could not be repaired automatically.\n\n" +
"Please delete this folder and restart the application:\n" +
Path.GetDirectoryName(GetUserConfigPath()),
"Configuration Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
return;
}
settings = SettingsManager.Instance;
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(CrashHandler);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
form = new MainForm();
Application.Run(form);
//form.Show();
}
private static string GetUserConfigPath()
{
try
{
string appData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
string companyName = "Rookie.AndroidSideloader";
string exeName = "AndroidSideloader.exe_Url_dkp0unsd4fjaabhwwafgfxvvbrerf10b";
string version = "2.0.0.0";
return Path.Combine(appData, companyName, exeName, version, "user.config");
}
catch
{
return null;
}
}
public static MainForm form;
private static void CrashHandler(object sender, UnhandledExceptionEventArgs args)
{
// Capture unhandled exceptions and write to file.
Exception e = (Exception)args.ExceptionObject;
string innerExceptionMessage = (e.InnerException != null)
? e.InnerException.Message
: "None";
string date_time = DateTime.Now.ToString("dddd, MMMM dd @ hh:mmtt (UTC)");
File.WriteAllText(Sideloader.CrashLogPath, $"Date/Time of crash: {date_time}\nMessage: {e.Message}\nInner Message: {innerExceptionMessage}\nData: {e.Data}\nSource: {e.Source}\nTargetSite: {e.TargetSite}\nStack Trace: \n{e.StackTrace}\n\n\nDebuglog: \n\n\n");
// If a debuglog exists we append it to the crashlog.
if (settings != null && File.Exists(settings.CurrentLogPath))
{
File.AppendAllText(Sideloader.CrashLogPath, File.ReadAllText($"{settings.CurrentLogPath}"));
}
}
}
}

View File

@@ -8,7 +8,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTitle("AndroidSideloader")]
[assembly: AssemblyDescription("Rookie Sideloader")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Rookie.WTF")]
[assembly: AssemblyCompany("Rookie.AndroidSideloader")]
[assembly: AssemblyProduct("AndroidSideloader")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyTrademark("")]

View File

@@ -19,7 +19,7 @@ namespace AndroidSideloader.Properties {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Resources {
@@ -80,56 +80,6 @@ namespace AndroidSideloader.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
public static System.Drawing.Bitmap battery1 {
get {
object obj = ResourceManager.GetObject("battery1", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
public static System.Drawing.Bitmap battery11 {
get {
object obj = ResourceManager.GetObject("battery11", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
public static System.Drawing.Bitmap Pattern {
get {
object obj = ResourceManager.GetObject("Pattern", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
public static System.Drawing.Bitmap pattern_cubes_1_1_1_0_0_0_1__000000_212121 {
get {
object obj = ResourceManager.GetObject("pattern_cubes-1_1_1_0-0_0_1__000000_212121", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
public static System.Drawing.Bitmap pattern_herringbone_2_1_3_0_0_90_1__000000_1c1c1c {
get {
object obj = ResourceManager.GetObject("pattern_herringbone_2_1_3_0_0_90_1__000000_1c1c1c", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
@@ -139,15 +89,5 @@ namespace AndroidSideloader.Properties {
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
public static System.Drawing.Bitmap splashimage {
get {
object obj = ResourceManager.GetObject("splashimage", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
}
}

View File

@@ -121,28 +121,10 @@
<data name="battery" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\battery.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="battery1" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\battery11.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="pattern_herringbone_2_1_3_0_0_90_1__000000_1c1c1c" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\resources\pattern_herringbone-2_1_3_0-0_90_1__000000_1c1c1c.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="splashimage" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\splashimage.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="battery11" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\battery1.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="ajax-loader" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\ajax-loader.gif;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="SearchGlass" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\SearchGlass.PNG;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Pattern" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Pattern.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="pattern_cubes-1_1_1_0-0_0_1__000000_212121" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\pattern_cubes-1_1_1_0-0_0_1__000000_212121.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

File diff suppressed because it is too large Load Diff

View File

@@ -24,7 +24,7 @@
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="FontStyle" Type="System.Drawing.Font" Scope="User">
<Value Profile="(Default)">Microsoft Sans Serif, 11.25pt</Value>
<Value Profile="(Default)">Microsoft Sans Serif, 10pt</Value>
</Setting>
<Setting Name="BackPicturePath" Type="System.String" Scope="User">
<Value Profile="(Default)" />
@@ -32,9 +32,6 @@
<Setting Name="SpoofGames" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="BandwidthLimit" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="BigFontStyle" Type="System.Drawing.Font" Scope="User">
<Value Profile="(Default)">Microsoft Sans Serif, 14pt</Value>
</Setting>
@@ -50,36 +47,9 @@
<Setting Name="ADBPath" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="QUsett" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="QuChecked" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="QUhz" Type="System.String" Scope="User">
<Value Profile="(Default)">0</Value>
</Setting>
<Setting Name="QUres" Type="System.String" Scope="User">
<Value Profile="(Default)">0</Value>
</Setting>
<Setting Name="QUy" Type="System.String" Scope="User">
<Value Profile="(Default)">0</Value>
</Setting>
<Setting Name="QUx" Type="System.String" Scope="User">
<Value Profile="(Default)">0</Value>
</Setting>
<Setting Name="QUname" Type="System.String" Scope="User">
<Value Profile="(Default)">Change Me</Value>
</Setting>
<Setting Name="QUString" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="MainDir" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="QUStringF" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="delsh" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
@@ -98,9 +68,6 @@
<Setting Name="PackageNameToCB" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="EnterKeyInstall" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="DownUpHeld" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
@@ -116,9 +83,6 @@
<Setting Name="CurrentCrashName" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="QUturnedon" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="adbdebugwarned" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
@@ -168,19 +132,67 @@
<Value Profile="(Default)">25, 25, 25</Value>
</Setting>
<Setting Name="SubButtonColor" Type="System.Drawing.Color" Scope="User">
<Value Profile="(Default)">25, 25, 25</Value>
<Value Profile="(Default)">42, 45, 58</Value>
</Setting>
<Setting Name="TextBoxColor" Type="System.Drawing.Color" Scope="User">
<Value Profile="(Default)">25, 25, 25</Value>
</Setting>
<Setting Name="ButtonColor" Type="System.Drawing.Color" Scope="User">
<Value Profile="(Default)">Black</Value>
<Value Profile="(Default)">32, 35, 45</Value>
</Setting>
<Setting Name="BackColor" Type="System.Drawing.Color" Scope="User">
<Value Profile="(Default)">1, 1, 1</Value>
<Value Profile="(Default)">31, 34, 42</Value>
</Setting>
<Setting Name="AppPackages" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="TrailersOn" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="downloadDir" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="customDownloadDir" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="customBackupDir" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="backupDir" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="singleThreadMode" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="virtualFilesystemCompatibility" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="UpdateSettings" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="UUID" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="createPubMirrorFile" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="useDownloadedFiles" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="bandwidthLimit" Type="System.Single" Scope="User">
<Value Profile="(Default)">0</Value>
</Setting>
<Setting Name="useProxy" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="proxyAddress" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="proxyPort" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="selectedMirror" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
</Settings>
</SettingsFile>

1297
QuestForm.Designer.cs generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
using System;
using AndroidSideloader.Utilities;
using System;
using System.IO;
using System.Windows.Forms;
@@ -6,329 +7,174 @@ namespace AndroidSideloader
{
public partial class QuestForm : Form
{
private static readonly SettingsManager settings = SettingsManager.Instance;
public static int length = 0;
public static string[] result;
public bool settingsexist = false;
public static bool QUSon = false;
public bool delsh = false;
private bool delsh = false;
public QuestForm()
{
InitializeComponent();
// Use same icon as the executable
this.Icon = System.Drawing.Icon.ExtractAssociatedIcon(Application.ExecutablePath);
}
private void button1_Click(object sender, EventArgs e)
private void btnApplyTempSettings_Click(object sender, EventArgs e)
{
bool ChangesMade = false;
//Quest 2 settings, might remove them in the future since some of them are broken
if (RefreshRateComboBox.SelectedIndex != -1)
{
ADB.WakeDevice();
_ = ADB.RunAdbCommandToString($"shell setprop debug.oculus.refreshRate {RefreshRateComboBox.SelectedItem}");
string refreshRate = RefreshRateComboBox.SelectedItem.ToString().Replace(" Hz", "");
_ = ADB.RunAdbCommandToString($"shell setprop debug.oculus.refreshRate {refreshRate}");
_ = ADB.RunAdbCommandToString($"shell settings put global 90hz_global {RefreshRateComboBox.SelectedIndex}");
_ = ADB.RunAdbCommandToString($"shell settings put global 90hzglobal {RefreshRateComboBox.SelectedIndex}");
ChangesMade = true;
}
if (TextureResTextBox.Text.Length > 0)
if (TextureResTextBox.Text.Length > 0 && TextureResTextBox.Text != "0")
{
ADB.WakeDevice();
_ = int.TryParse(TextureResTextBox.Text, out _);
_ = ADB.RunAdbCommandToString($"shell settings put global texture_size_Global {TextureResTextBox.Text}");
_ = ADB.RunAdbCommandToString($"shell setprop debug.oculus.textureWidth {TextureResTextBox.Text}");
_ = ADB.RunAdbCommandToString($"shell setprop debug.oculus.textureHeight {TextureResTextBox.Text}");
ChangesMade = true;
if (int.TryParse(TextureResTextBox.Text, out _))
{
_ = ADB.RunAdbCommandToString($"shell settings put global texture_size_Global {TextureResTextBox.Text}");
_ = ADB.RunAdbCommandToString($"shell setprop debug.oculus.textureWidth {TextureResTextBox.Text}");
_ = ADB.RunAdbCommandToString($"shell setprop debug.oculus.textureHeight {TextureResTextBox.Text}");
ChangesMade = true;
}
}
if (CPUComboBox.SelectedIndex != -1)
{
_ = ADB.RunAdbCommandToString($"shell setprop debug.oculus.cpuLevel {CPUComboBox.SelectedItem}");
_ = ADB.RunAdbCommandToString($"shell setprop debug.oculus.cpuLevel {CPUComboBox.SelectedIndex}");
ChangesMade = true;
}
if (GPUComboBox.SelectedIndex != -1)
{
_ = ADB.RunAdbCommandToString($"shell setprop debug.oculus.gpuLevel {GPUComboBox.SelectedItem}");
_ = ADB.RunAdbCommandToString($"shell setprop debug.oculus.gpuLevel {GPUComboBox.SelectedIndex}");
ChangesMade = true;
}
if (ChangesMade)
{
_ = MessageBox.Show("Settings applied!");
_ = MessageBox.Show("Settings applied!", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
public static void setLength(int value)
{
result = new string[value];
}
public void ResetQU_click(object sender, EventArgs e)
private void toggleDeleteAfterTransfer_CheckedChanged(object sender, EventArgs e)
{
ResBox.Text = "0";
UsrBox.Text = "Change Me";
FOVx.Text = "0";
FOVy.Text = "0";
QURfrRt.SelectedIndex = 0;
delsh = toggleDeleteAfterTransfer.Checked;
}
private void DeleteShots_CheckedChanged(object sender, EventArgs e)
{
delsh = DeleteShots.Checked;
}
private void QUon_CheckedChanged(object sender, EventArgs e)
{
if (QUon.Checked)
{
ResBox.Visible = true;
UsrBox.Visible = true;
FOVx.Visible = true;
FOVy.Visible = true;
QURfrRt.Visible = true;
ResetQU.Visible = true;
QUEnable.Visible = true;
label5.Visible = true;
label6.Visible = true;
label7.Visible = true;
label8.Visible = true;
label9.Visible = true;
label10.Visible = true;
deleteButton.Visible = true;
ResBox.Text = Properties.Settings.Default.QUres;
UsrBox.Text = Properties.Settings.Default.QUname;
FOVy.Text = Properties.Settings.Default.QUy;
FOVx.Text = Properties.Settings.Default.QUx;
QURfrRt.SelectedValue = Properties.Settings.Default.QUhz;
Properties.Settings.Default.QUturnedon = true;
}
else if (!QUon.Checked)
{
ResBox.Visible = false;
UsrBox.Visible = false;
FOVx.Visible = false;
FOVy.Visible = false;
QURfrRt.Visible = false;
ResetQU.Visible = false;
QUEnable.Visible = false;
label5.Visible = false;
label6.Visible = false;
label7.Visible = false;
label8.Visible = false;
label9.Visible = false;
label10.Visible = false;
deleteButton.Visible = false;
Properties.Settings.Default.QUturnedon = false;
_ = MessageBox.Show("Ok, Deleted your custom settings file.\nIf you would like to re-enable return here and apply settings again");
File.Delete($"{Environment.CurrentDirectory}\\Config.Json");
File.Delete($"{Environment.CurrentDirectory}\\delete_settings");
}
}
private static readonly Random random = new Random();
private static readonly object syncLock = new object();
public static int RandomNumber(int min, int max)
{
lock (syncLock)
{ // synchronize
{
return random.Next(min, max);
}
}
private void QUEnable_Click(object sender, EventArgs e)
{
settingsexist = true;
_ = MessageBox.Show("OK, any -QU packages installed will have these settings applied!\nTo delete settings: goto main app window, select a game with top menu, and click \"Remove QU Setting\"");
if (QUon.Checked)
{
Properties.Settings.Default.QUturnedon = true;
Random r = new Random();
int x = r.Next(999999999);
int y = r.Next(9999999);
long sum = (y * (long)1000000000) + x;
int x2 = r.Next(999999999);
int y2 = r.Next(9999999);
long sum2 = (y2 * (long)1000000000) + x2;
QUSon = true;
string selected = QURfrRt.GetItemText(QURfrRt.SelectedItem);
Properties.Settings.Default.QUString = $"\"refresh_rate\":{selected},\"eye_texture_width\":{ResBox.Text},\"fov_x\":{FOVx.Text},\"fov_y\":{FOVy.Text},\"username\":\"{UsrBox.Text}\"}}";
Properties.Settings.Default.QUStringF = $"{{\"user_id\":{sum},\"app_id\":\"{sum2}\",";
Properties.Settings.Default.Save();
File.WriteAllText($"{Properties.Settings.Default.MainDir}\\delete_settings", "");
string boff = Properties.Settings.Default.QUStringF + Properties.Settings.Default.QUString;
File.WriteAllText($"{Properties.Settings.Default.MainDir}\\config.json", boff);
}
else
{
Properties.Settings.Default.QUturnedon = false;
}
}
private void QuestForm_FormClosed(object sender, FormClosedEventArgs e)
{
if (QUon.Checked)
{
Properties.Settings.Default.QUsett = true;
Properties.Settings.Default.Save();
}
if (!QUon.Checked)
{
Properties.Settings.Default.QUsett = false;
Properties.Settings.Default.Save();
}
if (DeleteShots.Checked)
{
Properties.Settings.Default.delsh = true;
Properties.Settings.Default.Save();
}
if (!DeleteShots.Checked)
{
Properties.Settings.Default.delsh = false;
Properties.Settings.Default.Save();
}
settings.Delsh = toggleDeleteAfterTransfer.Checked;
settings.Save();
}
private void QuestForm_Load(object sender, EventArgs e)
{
DeleteShots.Checked = Properties.Settings.Default.delsh;
if (Properties.Settings.Default.QUsett)
{
ResBox.Text = Properties.Settings.Default.QUres;
UsrBox.Text = Properties.Settings.Default.QUname;
FOVy.Text = Properties.Settings.Default.QUy;
FOVx.Text = Properties.Settings.Default.QUx;
QURfrRt.Text = Properties.Settings.Default.QUhz;
QUon.Checked = true;
if (settingsexist)
{
QUSon = true;
}
}
GlobalUsername.Text = Properties.Settings.Default.GlobalUsername;
CenterToParent();
toggleDeleteAfterTransfer.SetCheckedSilent(settings.Delsh);
delsh = settings.Delsh;
GlobalUsername.Text = settings.GlobalUsername;
}
private void ResBox_TextChanged(object sender, EventArgs e)
{
Properties.Settings.Default.QUres = ResBox.Text;
Properties.Settings.Default.Save();
}
private void UsrBox_TextChanged(object sender, EventArgs e)
{
Properties.Settings.Default.QUname = UsrBox.Text;
Properties.Settings.Default.Save();
}
private void FOVx_TextChanged(object sender, EventArgs e)
{
Properties.Settings.Default.QUx = FOVx.Text;
Properties.Settings.Default.Save();
}
private void FOVy_TextChanged(object sender, EventArgs e)
{
Properties.Settings.Default.QUy = FOVy.Text;
Properties.Settings.Default.Save();
}
private void QURfrRt_SelectedIndexChanged(object sender, EventArgs e)
{
string selected = QURfrRt.GetItemText(QURfrRt.SelectedItem);
Properties.Settings.Default.QUhz = selected;
Properties.Settings.Default.Save();
}
private void DeleteButton_Click(object sender, EventArgs e)
{
_ = MessageBox.Show("Ok, Deleted your custom settings file.\nIf you would like to re-enable return here and apply settings again");
File.Delete($"{Properties.Settings.Default.MainDir}\\Config.Json");
}
private void questPics_Click(object sender, EventArgs e)
{
string path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
if (!Directory.Exists($"{path}\\Quest ScreenShots"))
if (!Directory.Exists($"{path}\\Quest Screenshots"))
{
_ = Directory.CreateDirectory($"{path}\\Quest ScreenShots");
_ = Directory.CreateDirectory($"{path}\\Quest Screenshots");
}
_ = MessageBox.Show("Please wait until you get the message that the transfer has finished.");
ADB.WakeDevice();
Program.form.ChangeTitle("Pulling files...");
_ = ADB.RunAdbCommandToString($"pull \"/sdcard/Oculus/Screenshots\" \"{path}\\Quest ScreenShots\"");
_ = MessageBox.Show("Please wait until you get the message that the transfer has finished.",
"Transfer Starting", MessageBoxButtons.OK, MessageBoxIcon.Information);
Program.form.changeTitle("Pulling files...");
_ = ADB.RunAdbCommandToString($"pull \"/sdcard/Oculus/Screenshots\" \"{path}\\Quest Screenshots\"");
if (delsh)
{
DialogResult dialogResult = MessageBox.Show("You have chosen to delete files from headset after transferring, so be sure to move them from your desktop to somewhere safe!", "Warning!", MessageBoxButtons.OKCancel);
DialogResult dialogResult = MessageBox.Show(
"You have chosen to delete files from headset after transferring.\n\nMake sure to move them from your desktop to somewhere safe!",
"Warning", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning);
if (dialogResult == DialogResult.OK)
{
_ = ADB.RunAdbCommandToString("shell rm -r /sdcard/Oculus/Screenshots");
_ = ADB.RunAdbCommandToString("shell mkdir /sdcard/Oculus/Screenshots");
}
}
_ = MessageBox.Show("Transfer finished! ScreenShots can be found in a folder named Quest Screenshots on your desktop!");
Program.form.ChangeTitle("Done!");
_ = MessageBox.Show("Transfer finished!\n\nScreenshots can be found in:\nDesktop\\Quest Screenshots",
"Complete", MessageBoxButtons.OK, MessageBoxIcon.Information);
Program.form.changeTitle("Done!");
}
private void questVids_Click(object sender, EventArgs e)
{
string path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
if (!Directory.Exists($"{path}\\Quest VideoShots"))
if (!Directory.Exists($"{path}\\Quest Recordings"))
{
_ = Directory.CreateDirectory($"{path}\\Quest VideoShots");
_ = Directory.CreateDirectory($"{path}\\Quest Recordings");
}
_ = MessageBox.Show("Please wait until you get the message that the transfer has finished.");
ADB.WakeDevice();
Program.form.ChangeTitle("Pulling files...");
_ = ADB.RunAdbCommandToString($"pull \"/sdcard/Oculus/Videoshots\" \"{path}\\Quest VideoShots\"");
_ = MessageBox.Show("Please wait until you get the message that the transfer has finished.",
"Transfer Starting", MessageBoxButtons.OK, MessageBoxIcon.Information);
Program.form.changeTitle("Pulling files...");
_ = ADB.RunAdbCommandToString($"pull \"/sdcard/Oculus/Videoshots\" \"{path}\\Quest Recordings\"");
if (delsh)
{
DialogResult dialogResult = MessageBox.Show("You have chosen to delete files from headset after transferring, so be sure to move them from your desktop to somewhere safe!", "Warning!", MessageBoxButtons.OKCancel);
DialogResult dialogResult = MessageBox.Show(
"You have chosen to delete files from headset after transferring.\n\nMake sure to move them from your desktop to somewhere safe!",
"Warning", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning);
if (dialogResult == DialogResult.OK)
{
_ = ADB.RunAdbCommandToString("shell rm -r /sdcard/Oculus/Videoshots");
_ = ADB.RunAdbCommandToString("shell mkdir /sdcard/Oculus/Videoshots");
}
}
_ = MessageBox.Show("Transfer finished! VideoShots can be found in a folder named Quest VideoShots on your desktop!");
Program.form.ChangeTitle("Done!");
}
private void button3_Click(object sender, EventArgs e)
{
if (GlobalUsername.Text.Contains(" "))
{
_ = MessageBox.Show("Usernames with a space are not permitted.", "Detected a space in username!");
}
else
{
_ = ADB.RunAdbCommandToString($"shell settings put global username {GlobalUsername.Text}");
_ = MessageBox.Show($"Username set as {GlobalUsername.Text}", "Success");
}
_ = MessageBox.Show("Transfer finished!\n\nRecordings can be found in:\nDesktop\\Quest Recordings",
"Complete", MessageBoxButtons.OK, MessageBoxIcon.Information);
Program.form.changeTitle("Done!");
}
private void Form_KeyDown(object sender, KeyEventArgs e)
private void btnApplyUsername_Click(object sender, EventArgs e)
{
if (e.KeyCode == Keys.Escape)
{
Close();
}
_ = ADB.RunAdbCommandToString($"shell settings put global username {GlobalUsername.Text}");
_ = MessageBox.Show($"Username set to: {GlobalUsername.Text}", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void btnClose_Click(object sender, EventArgs e)
{
Close();
}
protected override bool ProcessDialogKey(Keys keyData)
{
if (Form.ModifierKeys == Keys.None && keyData == Keys.Escape)
@@ -339,33 +185,15 @@ namespace AndroidSideloader
return base.ProcessDialogKey(keyData);
}
private void WifiWake_Click(object sender, EventArgs e)
{
_ = ADB.RunAdbCommandToString("shell settings put global wifi_wakeup_available 1");
_ = ADB.RunAdbCommandToString("shell settings put global wifi_wakeup_enabled 1");
_ = MessageBox.Show("Wake on Wifi enabled!\n\nNOTE: This requires having wireless ADB enabled to work. (Obviously)");
}
private void GlobalUsername_TextChanged(object sender, EventArgs e)
{
button3.Enabled = GlobalUsername.TextLength > 0;
Properties.Settings.Default.GlobalUsername = GlobalUsername.Text;
Properties.Settings.Default.Save();
}
private void RefreshRateComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
}
private void CPUComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
}
private void GPUComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
btnApplyUsername.Enabled = GlobalUsername.TextLength > 0;
btnApplyUsername.ForeColor = System.Drawing.Color.FromArgb(
((int)(((byte)(btnApplyUsername.Enabled ? 30 : 80)))),
((int)(((byte)(btnApplyUsername.Enabled ? 24 : 80)))),
((int)(((byte)(btnApplyUsername.Enabled ? 29 : 80)))));
settings.GlobalUsername = GlobalUsername.Text;
}
}
}
}

211
RCLONE.cs
View File

@@ -1,7 +1,9 @@
using JR.Utils.GUI.Forms;
using AndroidSideloader.Utilities;
using JR.Utils.GUI.Forms;
using System;
using System.Diagnostics;
using System.IO;
using System.Management;
using System.Text;
using System.Windows.Forms;
@@ -9,16 +11,36 @@ namespace AndroidSideloader
{
internal class RCLONE
{
//Kill all rclone, using a static rclone variable doesn't work for some reason #tofix
private static readonly SettingsManager settings = SettingsManager.Instance;
// Kill RCLONE Processes that were started from Rookie by looking for child processes.
public static void killRclone()
{
foreach (var process in Process.GetProcessesByName("rclone"))
var parentProcessId = Process.GetCurrentProcess().Id;
var processes = Process.GetProcessesByName("rclone");
foreach (var process in processes)
{
process.Kill();
try
{
using (ManagementObject obj = new ManagementObject($"win32_process.handle='{process.Id}'"))
{
obj.Get();
var ppid = Convert.ToInt32(obj["ParentProcessId"]);
if (ppid == parentProcessId)
{
process.Kill();
}
}
}
catch (Exception ex)
{
_ = Logger.Log($"Exception occured while attempting to shut down RCLONE with exception message: {ex.Message}", LogLevel.ERROR);
}
}
}
//For custom configs that use a password
// For custom configs that use a password
public static void Init()
{
string PwTxtPath = Path.Combine(Environment.CurrentDirectory, "rclone\\pw.txt");
@@ -28,7 +50,7 @@ namespace AndroidSideloader
}
}
//Change if you want to use a config
// Change if you want to use a config
public static string downloadConfigPath = "vrp.download.config";
public static string uploadConfigPath = "vrp.upload.config";
public static string rclonepw = "";
@@ -36,41 +58,37 @@ namespace AndroidSideloader
private static readonly Process rclone = new Process();
//Run rclone command
public static ProcessOutput runRcloneCommand_DownloadConfig(string command, string BandwidthLimit = "")
// Run an RCLONE Command that accesses the Download Config.
public static ProcessOutput runRcloneCommand_DownloadConfig(string command)
{
if (!MainForm.HasInternet || MainForm.isOffline)
if (MainForm.isOffline)
{
return new ProcessOutput("", "No internet");
}
ProcessOutput prcoutput = new ProcessOutput();
//Rclone output is unicode, else it will show garbage instead of unicode characters
// Rclone output is unicode, else it will show garbage instead of unicode characters
rclone.StartInfo.StandardOutputEncoding = Encoding.UTF8;
string originalCommand = command;
//set bandwidth limit
if (BandwidthLimit.Length > 0)
{
command += $" --bwlimit={BandwidthLimit}";
}
//set configpath if there is any
// set configpath if there is any
if (downloadConfigPath.Length > 0)
{
command += $" --config {downloadConfigPath}";
}
//set rclonepw
command += $" --inplace";
// set rclonepw
if (rclonepw.Length > 0)
{
command += " --ask-password=false";
}
string logcmd = Utilities.StringUtilities.RemoveEverythingBeforeFirst(command, "rclone.exe");
if (logcmd.Contains($"\"{Properties.Settings.Default.CurrentLogPath}\""))
if (logcmd.Contains($"\"{settings.CurrentLogPath}\""))
{
logcmd = logcmd.Replace($"\"{Properties.Settings.Default.CurrentLogPath}\"", $"\"{Properties.Settings.Default.CurrentLogName}\"");
logcmd = logcmd.Replace($"\"{settings.CurrentLogPath}\"", $"\"{settings.CurrentLogName}\"");
}
if (logcmd.Contains(Environment.CurrentDirectory))
@@ -80,23 +98,24 @@ namespace AndroidSideloader
_ = Logger.Log($"Running Rclone command: {logcmd}");
rclone.StartInfo.FileName = Environment.CurrentDirectory + "\\rclone\\rclone.exe";
rclone.StartInfo.FileName = Path.Combine(Environment.CurrentDirectory, "rclone", "rclone.exe");
rclone.StartInfo.Arguments = command;
rclone.StartInfo.RedirectStandardInput = true;
rclone.StartInfo.RedirectStandardError = true;
rclone.StartInfo.RedirectStandardOutput = true;
rclone.StartInfo.WorkingDirectory = Environment.CurrentDirectory + "\\rclone";
rclone.StartInfo.WorkingDirectory = Path.Combine(Environment.CurrentDirectory, "rclone");
rclone.StartInfo.CreateNoWindow = true;
//On debug we want to see when rclone is open
if (MainForm.debugMode == true)
setRcloneProxy();
// Display RCLONE Window if the binary is being run in Debug Mode.
if (MainForm.debugMode)
{
rclone.StartInfo.CreateNoWindow = false;
}
rclone.StartInfo.UseShellExecute = false;
_ = rclone.Start();
rclone.StandardInput.WriteLine(command);
rclone.StandardInput.Flush();
rclone.StandardInput.Close();
string output = rclone.StandardOutput.ReadToEnd();
@@ -105,27 +124,33 @@ namespace AndroidSideloader
if (error.Contains("There is not enough space"))
{
_ = FlexibleMessageBox.Show(Program.form, $"There isn't enough disk space to download this game.\r\nPlease ensure you have at least 200MB more the game size available in {Environment.CurrentDirectory} and try again.",
Program.form.Invoke(() =>
{
_ = FlexibleMessageBox.Show(Program.form, $"There isn't enough disk space to download this game.\r\nPlease ensure you have at least 200MB more the game size available in {settings.DownloadDir} and try again.",
"NOT ENOUGH SPACE",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
});
return new ProcessOutput("Download failed.", "");
}
//if there is one of these errors, we switch the mirrors
// Switch mirror upon matching error output.
if (error.Contains("400 Bad Request") || error.Contains("cannot fetch token") || error.Contains("authError") || error.Contains("quota") || error.Contains("exceeded") || error.Contains("directory not found") || error.Contains("Failed to"))
{
string oldRemote = MainForm.currentRemote;
bool retSM = false;
try
{
Program.form.SwitchMirrors();
retSM = Program.form.SwitchMirrors();
}
catch
{
return new ProcessOutput("All mirrors are on quota or down...", "All mirrors are on quota or down...");
}
prcoutput = runRcloneCommand_DownloadConfig(originalCommand.Replace(oldRemote, MainForm.currentRemote), BandwidthLimit);
if (retSM)
{
prcoutput = runRcloneCommand_DownloadConfig(originalCommand.Replace(oldRemote, MainForm.currentRemote));
}
}
else
{
@@ -137,7 +162,7 @@ namespace AndroidSideloader
{
if (!string.IsNullOrWhiteSpace(error))
{
_ = Logger.Log($"Rclone error: {error}\n");
_ = Logger.Log($"Rclone error: {error}\n", LogLevel.ERROR);
}
if (!string.IsNullOrWhiteSpace(output))
@@ -148,33 +173,22 @@ namespace AndroidSideloader
return prcoutput;
}
public static ProcessOutput runRcloneCommand_UploadConfig(string command, string BandwidthLimit = "")
public static ProcessOutput runRcloneCommand_UploadConfig(string command)
{
if (!MainForm.HasInternet || MainForm.isOffline)
{
return new ProcessOutput("", "No internet");
}
ProcessOutput prcoutput = new ProcessOutput();
//Rclone output is unicode, else it will show garbage instead of unicode characters
// Rclone output is unicode, else it will show garbage instead of unicode characters
rclone.StartInfo.StandardOutputEncoding = Encoding.UTF8;
//set bandwidth limit
if (BandwidthLimit.Length > 0)
{
command += $" --bwlimit={BandwidthLimit}";
}
//set configpath if there is any
// set configpath if there is any
if (uploadConfigPath.Length > 0)
{
command += $" --config {uploadConfigPath}";
}
string logcmd = Utilities.StringUtilities.RemoveEverythingBeforeFirst(command, "rclone.exe");
if (logcmd.Contains($"\"{Properties.Settings.Default.CurrentLogPath}\""))
if (logcmd.Contains($"\"{settings.CurrentLogPath}\""))
{
logcmd = logcmd.Replace($"\"{Properties.Settings.Default.CurrentLogPath}\"", $"\"{Properties.Settings.Default.CurrentLogName}\"");
logcmd = logcmd.Replace($"\"{settings.CurrentLogPath}\"", $"\"{settings.CurrentLogName}\"");
}
if (logcmd.Contains(Environment.CurrentDirectory))
@@ -184,21 +198,23 @@ namespace AndroidSideloader
_ = Logger.Log($"Running Rclone command: {logcmd}");
command += " --checkers 0 --no-check-dest --retries 1";
command += " --checkers 1 --retries 2 --inplace";
rclone.StartInfo.FileName = Environment.CurrentDirectory + "\\rclone\\rclone.exe";
rclone.StartInfo.FileName = Path.Combine(Environment.CurrentDirectory, "rclone", "rclone.exe");
rclone.StartInfo.Arguments = command;
rclone.StartInfo.RedirectStandardInput = true;
rclone.StartInfo.RedirectStandardError = true;
rclone.StartInfo.RedirectStandardOutput = true;
rclone.StartInfo.WorkingDirectory = Environment.CurrentDirectory + "\\rclone";
rclone.StartInfo.WorkingDirectory = Path.Combine(Environment.CurrentDirectory, "rclone");
rclone.StartInfo.CreateNoWindow = true;
//On debug we want to see when rclone is open
if (MainForm.debugMode == true)
setRcloneProxy();
// Display RCLONE Window if the binary is being run in Debug Mode.
if (MainForm.debugMode)
{
rclone.StartInfo.CreateNoWindow = false;
}
rclone.StartInfo.UseShellExecute = false;
_ = rclone.Start();
rclone.StandardInput.WriteLine(command);
@@ -209,10 +225,10 @@ namespace AndroidSideloader
string error = rclone.StandardError.ReadToEnd();
rclone.WaitForExit();
//if there is one of these errors, we switch the mirrors
// if there is one of these errors, we switch the mirrors
if (error.Contains("400 Bad Request") || error.Contains("cannot fetch token") || error.Contains("authError") || error.Contains("quota") || error.Contains("exceeded") || error.Contains("directory not found") || error.Contains("Failed to"))
{
_ = Logger.Log(error);
_ = Logger.Log(error, LogLevel.ERROR);
return new ProcessOutput("Upload Failed.", "Upload failed.");
}
else
@@ -225,7 +241,7 @@ namespace AndroidSideloader
{
if (!string.IsNullOrWhiteSpace(error))
{
_ = Logger.Log($"Rclone error: {error}\n");
_ = Logger.Log($"Rclone error: {error}\n", LogLevel.ERROR);
}
if (!string.IsNullOrWhiteSpace(output))
@@ -236,27 +252,21 @@ namespace AndroidSideloader
return prcoutput;
}
public static ProcessOutput runRcloneCommand_PublicConfig(string command, string BandwidthLimit = "")
public static ProcessOutput runRcloneCommand_PublicConfig(string command)
{
if (!MainForm.HasInternet || MainForm.isOffline)
if (MainForm.isOffline)
{
return new ProcessOutput("", "No internet");
}
ProcessOutput prcoutput = new ProcessOutput();
//Rclone output is unicode, else it will show garbage instead of unicode characters
// Rclone output is unicode, else it will show garbage instead of unicode characters
rclone.StartInfo.StandardOutputEncoding = Encoding.UTF8;
//set bandwidth limit
if (BandwidthLimit.Length > 0)
{
command += $" --bwlimit={BandwidthLimit}";
}
string logcmd = Utilities.StringUtilities.RemoveEverythingBeforeFirst(command, "rclone.exe");
if (logcmd.Contains($"\"{Properties.Settings.Default.CurrentLogPath}\""))
if (logcmd.Contains($"\"{settings.CurrentLogPath}\""))
{
logcmd = logcmd.Replace($"\"{Properties.Settings.Default.CurrentLogPath}\"", $"\"{Properties.Settings.Default.CurrentLogName}\"");
logcmd = logcmd.Replace($"\"{settings.CurrentLogPath}\"", $"\"{settings.CurrentLogName}\"");
}
if (logcmd.Contains(Environment.CurrentDirectory))
@@ -264,25 +274,27 @@ namespace AndroidSideloader
logcmd = logcmd.Replace($"{Environment.CurrentDirectory}", $"CurrentDirectory");
}
command += $" --inplace";
_ = Logger.Log($"Running Rclone command: {logcmd}");
//set http source & args
command += $" --http-url {MainForm.PublicConfigFile.BaseUri} {MainForm.PublicMirrorExtraArgs}";
rclone.StartInfo.FileName = Environment.CurrentDirectory + "\\rclone\\rclone.exe";
rclone.StartInfo.FileName = Path.Combine(Environment.CurrentDirectory, "rclone", "rclone.exe");
rclone.StartInfo.Arguments = command;
rclone.StartInfo.RedirectStandardInput = true;
rclone.StartInfo.RedirectStandardError = true;
rclone.StartInfo.RedirectStandardOutput = true;
rclone.StartInfo.WorkingDirectory = Environment.CurrentDirectory + "\\rclone";
rclone.StartInfo.WorkingDirectory = Path.Combine(Environment.CurrentDirectory, "rclone");
rclone.StartInfo.CreateNoWindow = true;
//On debug we want to see when rclone is open
if (MainForm.debugMode == true)
setRcloneProxy();
// Display RCLONE Window if the binary is being run in Debug Mode.
if (MainForm.debugMode)
{
rclone.StartInfo.CreateNoWindow = false;
}
rclone.StartInfo.UseShellExecute = false;
_ = rclone.Start();
rclone.StandardInput.WriteLine(command);
@@ -295,14 +307,22 @@ namespace AndroidSideloader
if (error.Contains("There is not enough space"))
{
_ = FlexibleMessageBox.Show(Program.form, $"There isn't enough disk space to download this game.\r\nPlease ensure you have at least 2x the game size available in {Environment.CurrentDirectory} and try again.",
Program.form.Invoke(() =>
{
_ = FlexibleMessageBox.Show(Program.form, $"There isn't enough disk space to download this game.\r\nPlease ensure you have at least 2x the game size available in {settings.DownloadDir} and try again.",
"NOT ENOUGH SPACE",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
return new ProcessOutput("Download failed.", "");
});
return new ProcessOutput("Download failed.", string.Empty);
}
if (error.Contains("400 Bad Request")
if (error.Contains("Only one usage of each socket address (protocol/network address/port) is normally permitted"))
{
_ = Logger.Log(error, LogLevel.ERROR);
return new ProcessOutput("Failed to fetch from public mirror.", "Failed to fetch from public mirror.\nYou may have a running RCLONE Task!\nCheck your Task Manager, Sort by Network Usage, and kill the process Rsync for Cloud Storage/Rclone");
}
else if (error.Contains("400 Bad Request")
|| error.Contains("cannot fetch token")
|| error.Contains("authError")
|| error.Contains("quota")
@@ -310,7 +330,7 @@ namespace AndroidSideloader
|| error.Contains("directory not found")
|| error.Contains("Failed to"))
{
_ = Logger.Log(error);
_ = Logger.Log(error, LogLevel.ERROR);
return new ProcessOutput("Failed to fetch from public mirror.", "Failed to fetch from public mirror.");
}
else
@@ -323,7 +343,7 @@ namespace AndroidSideloader
{
if (!string.IsNullOrWhiteSpace(error))
{
_ = Logger.Log($"Rclone error: {error}\n");
_ = Logger.Log($"Rclone error: {error}\n", LogLevel.ERROR);
}
if (!string.IsNullOrWhiteSpace(output))
@@ -334,5 +354,38 @@ namespace AndroidSideloader
return prcoutput;
}
private static void setRcloneProxy()
{
// Use the user's proxy settings if set, otherwise fallback to DNS fallback proxy if active
string proxyUrl = DnsHelper.ProxyUrl;
if (settings.useProxy)
{
// Use user's configured proxy
var url = $"http://{settings.ProxyAddress}:{settings.ProxyPort}";
rclone.StartInfo.EnvironmentVariables["HTTP_PROXY"] = url;
rclone.StartInfo.EnvironmentVariables["HTTPS_PROXY"] = url;
rclone.StartInfo.EnvironmentVariables["http_proxy"] = url;
rclone.StartInfo.EnvironmentVariables["https_proxy"] = url;
}
else if (!string.IsNullOrEmpty(proxyUrl))
{
// Use our DNS-resolving proxy
rclone.StartInfo.EnvironmentVariables["HTTP_PROXY"] = proxyUrl;
rclone.StartInfo.EnvironmentVariables["HTTPS_PROXY"] = proxyUrl;
rclone.StartInfo.EnvironmentVariables["http_proxy"] = proxyUrl;
rclone.StartInfo.EnvironmentVariables["https_proxy"] = proxyUrl;
}
else
{
// No proxy
rclone.StartInfo.EnvironmentVariables.Remove("HTTP_PROXY");
rclone.StartInfo.EnvironmentVariables.Remove("HTTPS_PROXY");
rclone.StartInfo.EnvironmentVariables.Remove("http_proxy");
rclone.StartInfo.EnvironmentVariables.Remove("https_proxy");
}
}
}
}

View File

@@ -1,15 +1,33 @@
# androidsideloader
![GitHub last commit](https://img.shields.io/github/last-commit/nerdunit/androidsideloader)
![GitHub release (latest by date)](https://img.shields.io/github/v/release/nerdunit/androidsideloader)
[![Downloads](https://img.shields.io/github/downloads/nerdunit/androidsideloader/total.svg)](https://github.com/nerdunit/androidsideloader/releases)
![Issues](https://img.shields.io/github/issues/nerdunit/androidsideloader)
# AndroidSideloader
androidsideloader uses the GPL license, any forks of it must have their source code made public on the internet.
![GitHub last commit](https://img.shields.io/github/last-commit/VRPirates/rookie)
![GitHub release (latest by date)](https://img.shields.io/github/v/release/VRPirates/rookie)
[![Downloads](https://img.shields.io/github/downloads/VRPirates/rookie/total.svg)](https://github.com/VRPirates/rookie/releases)
![Issues](https://img.shields.io/github/issues/VRPirates/rookie)
## !IMPORTANT!
## Disclaimer
This application might get flagged as malware by some antivirus software; however, both the Sideloader and the Sideloader Launcher are open source.
This app may be buggy and have problems.
To run properly, Rookie must be extracted to a non-Protected folder on your drive. We recommend running Rookie from C:\RSL\Rookie
Do Not use folders such as- C:\Users; C:\Users\Desktop; C:\Program Files; OneDrive; Google Drive; etc...
Rookie will cleanup its own folder. We are not responsible if you run Rookie from a folder containing other files as Rookie may delete them.
### This app might get detected as malware, however both the sideloader and the sideloader launcher are open source. And obviously, it isn't.
See:
https://www.virustotal.com/gui/file/977105693610cf360fc29339b918e224180ba393ba05a64b6255af3845cbf376/relations
## Support
For any assistance or questions, please utilize our support channels available at [Live Chats](https://vrpirates.wiki/en/general_information/live-chats).
## Build Instructions
This project is developed using C# with WinForms targeting the .NET Framework 4.5.2. To build the project successfully in Visual Studio 2022, follow these steps:
1. Clone this repository to your local machine.
2. Ensure you have the .NET Framework 4.5.2 installed on your machine.
3. Open the solution file (`*.sln`) in Visual Studio 2022.
4. Sometimes the building process can fail due to the packages.config, you should migrate it to PackageReference, do this by right clicking on References in the Solution Explorer, and pressing "Migrate packages.config to PackageReference"
5. Build the solution by selecting "Build" > "Build Solution" from the Visual Studio menu or pressing `Ctrl + Shift + B`. (or right click the solution in the solution explorer, then press Build)
6. Run the application.
## Contributing
We welcome contributions from the community. If you would like to contribute, please fork the repository, branch from the newest beta branch from this repository, make your changes, and submit a pull request.
## License
AndroidSideloader is distributed under the GPL license, meaning any forks of it must have their source code made public on the internet. See the [LICENSE](LICENSE) file for details.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 476 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -36,6 +36,39 @@ namespace AndroidSideloader
Invalidate();
}
}
private Color disabled1, disabled2;
private Color disabledStrokeColor;
public Color Disabled1
{
get => disabled1;
set
{
disabled1 = value;
Invalidate();
}
}
public Color Disabled2
{
get => disabled2;
set
{
disabled2 = value;
Invalidate();
}
}
public Color DisabledStrokeColor
{
get => disabledStrokeColor;
set
{
disabledStrokeColor = value;
Invalidate();
}
}
#endregion
#region RoundButton
public RoundButton()
@@ -48,7 +81,9 @@ namespace AndroidSideloader
inactive2 = Color.FromArgb(33, 167, 188);
active1 = Color.FromArgb(64, 168, 183);
active2 = Color.FromArgb(36, 164, 183);
disabled1 = Color.FromArgb(32, 35, 45);
disabled2 = Color.FromArgb(25, 28, 35);
disabledStrokeColor = Color.FromArgb(50, 55, 65);
radius = 10;
roundedRect = new RoundedRectangleF(Width, Height, radius);
@@ -74,7 +109,7 @@ namespace AndroidSideloader
#endregion
#region Drawing
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
roundedRect = new RoundedRectangleF(Width, Height, radius);
e.Graphics.FillRectangle(Brushes.Transparent, ClientRectangle);
@@ -123,12 +158,18 @@ namespace AndroidSideloader
}
else
{
Color linear1 = Color.FromArgb(190, 190, 190);
Color linear2 = Color.FromArgb(210, 210, 210);
using (LinearGradientBrush inactiveGB = new LinearGradientBrush(rect, linear1, linear2, 90f))
using (LinearGradientBrush disabledGB = new LinearGradientBrush(rect, disabled1, disabled2, 90f))
{
e.Graphics.FillPath(inactiveGB, roundedRect.Path);
e.Graphics.DrawPath(new Pen(inactiveGB), roundedRect.Path);
e.Graphics.FillPath(disabledGB, roundedRect.Path);
}
if (stroke)
{
using (Pen pen = new Pen(disabledStrokeColor, 1))
using (GraphicsPath path = new RoundedRectangleF(Width - (radius > 0 ? 0 : 1), Height - (radius > 0 ? 0 : 1), radius).Path)
{
e.Graphics.DrawPath(pen, path);
}
}
}

28
Settings.cs Normal file
View File

@@ -0,0 +1,28 @@
namespace AndroidSideloader.Properties {
// This class allows you to handle specific events on the settings class:
// The SettingChanging event is raised before a setting's value is changed.
// The PropertyChanged event is raised after a setting's value is changed.
// The SettingsLoaded event is raised after the setting values are loaded.
// The SettingsSaving event is raised before the setting values are saved.
internal sealed partial class Settings {
public Settings() {
// // To add event handlers for saving and changing settings, uncomment the lines below:
//
// this.SettingChanging += this.SettingChangingEventHandler;
//
// this.SettingsSaving += this.SettingsSavingEventHandler;
//
}
private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) {
// Add code to handle the SettingChangingEvent event here.
}
private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) {
// Add code to handle the SettingsSaving event here.
}
}
}

1329
SettingsForm.Designer.cs generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,195 +1,400 @@
using JR.Utils.GUI.Forms;
using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
namespace AndroidSideloader
{
public partial class SettingsForm : Form
{
public SettingsForm()
{
InitializeComponent();
}
private void SettingsForm_Load(object sender, EventArgs e)
{
CenterToParent();
intSettings();
intToolTips();
}
//Init form objects with values from settings
private void intSettings()
{
checkForUpdatesCheckBox.Checked = Properties.Settings.Default.checkForUpdates;
enableMessageBoxesCheckBox.Checked = Properties.Settings.Default.enableMessageBoxes;
deleteAfterInstallCheckBox.Checked = Properties.Settings.Default.deleteAllAfterInstall;
updateConfigCheckBox.Checked = Properties.Settings.Default.autoUpdateConfig;
userJsonOnGameInstall.Checked = Properties.Settings.Default.userJsonOnGameInstall;
nodevicemodeBox.Checked = Properties.Settings.Default.nodevicemode;
bmbfBox.Checked = Properties.Settings.Default.BMBFchecked;
AutoReinstBox.Checked = Properties.Settings.Default.AutoReinstall;
if (Properties.Settings.Default.BandwidthLimit.Length > 1)
{
txtBandwidth.Text = Properties.Settings.Default.BandwidthLimit.Remove(Properties.Settings.Default.BandwidthLimit.Length - 1);
}
}
private void intToolTips()
{
ToolTip checkForUpdatesToolTip = new ToolTip();
checkForUpdatesToolTip.SetToolTip(checkForUpdatesCheckBox, "If this is checked, the software will check for available updates");
ToolTip enableMessageBoxesToolTip = new ToolTip();
enableMessageBoxesToolTip.SetToolTip(enableMessageBoxesCheckBox, "If this is checked, the software will display message boxes after every completed task");
ToolTip deleteAfterInstallToolTip = new ToolTip();
deleteAfterInstallToolTip.SetToolTip(deleteAfterInstallCheckBox, "If this is checked, the software will delete all game files after downloading and installing a game from a remote server");
}
public void btnUploadDebug_click(object sender, EventArgs e)
{
if (File.Exists($"{Properties.Settings.Default.CurrentLogPath}"))
{
string UUID = SideloaderUtilities.UUID();
string debugLogPath = $"{Environment.CurrentDirectory}\\{UUID}.log";
System.IO.File.Copy("debuglog.txt", debugLogPath);
Clipboard.SetText(UUID);
_ = RCLONE.runRcloneCommand_UploadConfig($"copy \"{debugLogPath}\" RSL-gameuploads:DebugLogs");
_ = MessageBox.Show($"Your debug log has been copied to the server. ID: {UUID}");
}
}
public void btnResetDebug_click(object sender, EventArgs e)
{
if (File.Exists($"{Properties.Settings.Default.CurrentLogPath}"))
{
File.Delete($"{Properties.Settings.Default.CurrentLogPath}");
}
if (File.Exists($"{Environment.CurrentDirectory}\\debuglog.txt"))
{
File.Delete($"{Environment.CurrentDirectory}\\debuglog.txt");
}
}
//Apply settings
private void applyButton_Click(object sender, EventArgs e)
{
Properties.Settings.Default.BandwidthLimit = txtBandwidth.Text.Length > 0 && txtBandwidth.Text != "0" ? $"{txtBandwidth.Text.Replace(" ", "")}M" : "";
Properties.Settings.Default.Save();
_ = FlexibleMessageBox.Show(this, "Settings applied!");
}
private void checkForUpdatesCheckBox_CheckedChanged(object sender, EventArgs e)
{
Properties.Settings.Default.checkForUpdates = checkForUpdatesCheckBox.Checked;
}
private void enableMessageBoxesCheckBox_CheckedChanged(object sender, EventArgs e)
{
Properties.Settings.Default.enableMessageBoxes = enableMessageBoxesCheckBox.Checked;
}
private void resetSettingsButton_Click(object sender, EventArgs e)
{
Properties.Settings.Default.Reset();
intSettings();
}
private void deleteAfterInstallCheckBox_CheckedChanged(object sender, EventArgs e)
{
Properties.Settings.Default.deleteAllAfterInstall = deleteAfterInstallCheckBox.Checked;
}
private void updateConfigCheckBox_CheckedChanged(object sender, EventArgs e)
{
Properties.Settings.Default.autoUpdateConfig = updateConfigCheckBox.Checked;
}
private void userJsonOnGameInstall_CheckedChanged(object sender, EventArgs e)
{
Properties.Settings.Default.userJsonOnGameInstall = userJsonOnGameInstall.Checked;
}
private void SettingsForm_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Escape)
{
Close();
}
}
private void SettingsForm_Leave(object sender, EventArgs e)
{
Close();
}
private void Form_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Escape)
{
Close();
}
}
protected override bool ProcessDialogKey(Keys keyData)
{
if (Form.ModifierKeys == Keys.None && keyData == Keys.Escape)
{
Close();
return true;
}
return base.ProcessDialogKey(keyData);
}
private void nodevicemodeBox_CheckedChanged(object sender, EventArgs e)
{
Properties.Settings.Default.nodevicemode = nodevicemodeBox.Checked;
Properties.Settings.Default.Save();
}
private void bmbfBox_CheckedChanged(object sender, EventArgs e)
{
Properties.Settings.Default.BMBFchecked = bmbfBox.Checked;
Properties.Settings.Default.Save();
}
private void AutoReinstBox_CheckedChanged(object sender, EventArgs e)
{
Properties.Settings.Default.AutoReinstall = AutoReinstBox.Checked;
Properties.Settings.Default.Save();
}
private void AutoReinstBox_Click(object sender, EventArgs e)
{
if (AutoReinstBox.Checked)
{
DialogResult dialogResult = FlexibleMessageBox.Show(this, "WARNING: This box enables automatic reinstall when installs fail,\ndue to some games not allowing " +
"access to their save data (less than 5%) this\noption can lead to losing your progress." +
" However with this option\nchecked when installs fail you won't have to agree to a prompt to preform\nthe reinstall. " +
"(ideal when installing from a queue).\n\nNOTE: If your usb/wireless adb connection is extremely slow this option can\ncause larger" +
"apk file installations to fail. Enable anyway?", "WARNING", MessageBoxButtons.OKCancel);
if (dialogResult == DialogResult.Cancel)
{
AutoReinstBox.Checked = false;
}
}
}
private void btnOpenDebug_Click(object sender, EventArgs e)
{
if (File.Exists($"{Environment.CurrentDirectory}\\debuglog.txt"))
{
_ = Process.Start($"{Environment.CurrentDirectory}\\debuglog.txt");
}
}
}
}
using AndroidSideloader.Properties;
using AndroidSideloader.Utilities;
using JR.Utils.GUI.Forms;
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text.RegularExpressions;
using System.Windows.Forms;
namespace AndroidSideloader
{
public partial class SettingsForm : Form
{
private static readonly SettingsManager _settings = SettingsManager.Instance;
public SettingsForm()
{
InitializeComponent();
// Use same icon as the executable
this.Icon = System.Drawing.Icon.ExtractAssociatedIcon(Application.ExecutablePath);
}
private void SettingsForm_Load(object sender, EventArgs e)
{
CenterToParent();
initSettings();
initToolTips();
}
private void initSettings()
{
// Use SetCheckedSilent to avoid triggering events during initialization
toggleCheckForUpdates.SetCheckedSilent(_settings.CheckForUpdates);
toggleMessageBoxes.SetCheckedSilent(_settings.EnableMessageBoxes);
toggleDeleteAfterInstall.SetCheckedSilent(_settings.DeleteAllAfterInstall);
toggleUpdateConfig.SetCheckedSilent(_settings.AutoUpdateConfig);
toggleUserJson.SetCheckedSilent(_settings.UserJsonOnGameInstall);
toggleNoDeviceMode.SetCheckedSilent(_settings.NodeviceMode);
toggleBMBF.SetCheckedSilent(_settings.BMBFChecked);
toggleAutoReinstall.SetCheckedSilent(_settings.AutoReinstall);
toggleSingleThread.SetCheckedSilent(_settings.SingleThreadMode);
toggleVirtualFilesystem.SetCheckedSilent(_settings.VirtualFilesystemCompatibility);
toggleUseDownloadedFiles.SetCheckedSilent(_settings.UseDownloadedFiles);
toggleTrailers.SetCheckedSilent(_settings.TrailersEnabled);
bandwidthLimitTextBox.Text = _settings.BandwidthLimit.ToString();
// Handle no device mode disabling delete after install
if (toggleNoDeviceMode.Checked)
{
toggleDeleteAfterInstall.SetCheckedSilent(false);
toggleDeleteAfterInstall.Enabled = false;
lblDeleteAfterInstall.ForeColor = System.Drawing.Color.FromArgb(100, 100, 100);
}
toggleProxy.Checked = _settings.useProxy;
proxyAddressTextBox.Text = _settings.ProxyAddress;
proxyPortTextBox.Text = _settings.ProxyPort;
}
private void initToolTips()
{
ToolTip toolTip = new ToolTip();
toolTip.SetToolTip(toggleCheckForUpdates, "Check for available application updates on startup");
toolTip.SetToolTip(lblCheckForUpdates, "Check for available application updates on startup");
toolTip.SetToolTip(toggleMessageBoxes, "Show message boxes after every completed task");
toolTip.SetToolTip(lblMessageBoxes, "Show message boxes after every completed task");
toolTip.SetToolTip(toggleDeleteAfterInstall, "Delete game files after downloading and installing");
toolTip.SetToolTip(lblDeleteAfterInstall, "Delete game files after downloading and installing");
toolTip.SetToolTip(toggleUseDownloadedFiles, "Always install downloaded files without prompting to re-download");
toolTip.SetToolTip(lblUseDownloadedFiles, "Always install downloaded files without prompting to re-download");
toolTip.SetToolTip(toggleTrailers, "Show game trailers when selecting a game");
toolTip.SetToolTip(lblTrailers, "Show game trailers when selecting a game");
}
private void SaveAllSettings()
{
string input = bandwidthLimitTextBox.Text;
Regex regex = new Regex(@"^\d+(\.\d+)?$");
if (regex.IsMatch(input) && float.TryParse(input, out float bandwidthLimit))
{
_settings.BandwidthLimit = bandwidthLimit;
}
_settings.CheckForUpdates = toggleCheckForUpdates.Checked;
_settings.EnableMessageBoxes = toggleMessageBoxes.Checked;
_settings.DeleteAllAfterInstall = toggleDeleteAfterInstall.Checked;
_settings.AutoUpdateConfig = toggleUpdateConfig.Checked;
_settings.UserJsonOnGameInstall = toggleUserJson.Checked;
_settings.NodeviceMode = toggleNoDeviceMode.Checked;
_settings.BMBFChecked = toggleBMBF.Checked;
_settings.AutoReinstall = toggleAutoReinstall.Checked;
_settings.SingleThreadMode = toggleSingleThread.Checked;
_settings.VirtualFilesystemCompatibility = toggleVirtualFilesystem.Checked;
_settings.UseDownloadedFiles = toggleUseDownloadedFiles.Checked;
_settings.TrailersEnabled = toggleTrailers.Checked;
_settings.useProxy = toggleProxy.Checked;
if (Program.form != null)
{
Program.form.SetTrailerVisibility(toggleTrailers.Checked);
Program.form.UpdateSideloadingUI();
}
if (_settings.AutoUpdateConfig)
{
_settings.CreatePubMirrorFile = true;
}
_settings.Save();
}
public void btnUploadDebug_click(object sender, EventArgs e)
{
if (File.Exists($"{_settings.CurrentLogPath}"))
{
string UUID = SideloaderUtilities.UUID();
string debugLogPath = $"{Environment.CurrentDirectory}\\{UUID}.log";
System.IO.File.Copy("debuglog.txt", debugLogPath);
Clipboard.SetText(UUID);
_ = RCLONE.runRcloneCommand_UploadConfig($"copy \"{debugLogPath}\" RSL-gameuploads:DebugLogs");
_ = MessageBox.Show($"Your debug log has been copied to the server. ID: {UUID}");
}
}
public void btnResetDebug_click(object sender, EventArgs e)
{
if (File.Exists($"{_settings.CurrentLogPath}"))
{
File.Delete($"{_settings.CurrentLogPath}");
}
if (File.Exists($"{Environment.CurrentDirectory}\\debuglog.txt"))
{
File.Delete($"{Environment.CurrentDirectory}\\debuglog.txt");
}
}
private void applyButton_Click(object sender, EventArgs e)
{
// Parse bandwidth value
var bandwidthInput = bandwidthLimitTextBox.Text;
Regex regex = new Regex(@"^\d+(\.\d+)?$");
if (regex.IsMatch(bandwidthInput) && float.TryParse(bandwidthInput, out float bandwidthLimit))
{
_settings.BandwidthLimit = bandwidthLimit;
}
else
{
MessageBox.Show("Please enter a valid number for the bandwidth limit.");
return;
}
// Parse proxy values if proxy is enabled
if (toggleProxy.Checked)
{
// Parse proxy address
var proxyAddressInput = proxyAddressTextBox.Text;
if (proxyAddressInput.StartsWith("http://"))
{
proxyAddressInput = proxyAddressInput.Substring("http://".Length);
}
else if (proxyAddressInput.StartsWith("https://"))
{
proxyAddressInput = proxyAddressInput.Substring("https://".Length);
}
if (proxyAddressInput.Equals("localhost", StringComparison.OrdinalIgnoreCase) ||
IPAddress.TryParse(proxyAddressInput, out _))
{
_settings.ProxyAddress = proxyAddressInput;
}
else
{
MessageBox.Show("Please enter a valid address for the proxy.");
}
// Parse proxy port
var proxyPortInput = proxyPortTextBox.Text;
if (ushort.TryParse(proxyPortInput, out _))
{
_settings.ProxyPort = proxyPortInput;
}
else
{
MessageBox.Show("Please enter a valid port for the proxy.");
}
}
SaveAllSettings();
this.Close();
}
private void toggleCheckForUpdates_CheckedChanged(object sender, EventArgs e)
{
// Settings saved on form close
}
private void toggleUseDownloadedFiles_CheckedChanged(object sender, EventArgs e)
{
// Settings saved on form close
}
private void toggleMessageBoxes_CheckedChanged(object sender, EventArgs e)
{
// Settings saved on form close
}
private void toggleTrailers_CheckedChanged(object sender, EventArgs e)
{
// Settings saved on form close
}
private void resetSettingsButton_Click(object sender, EventArgs e)
{
this.Close();
}
private void toggleDeleteAfterInstall_CheckedChanged(object sender, EventArgs e)
{
// Settings saved on form close
}
private void toggleUpdateConfig_CheckedChanged(object sender, EventArgs e)
{
// Settings saved on form close
}
private void toggleUserJson_CheckedChanged(object sender, EventArgs e)
{
// Settings saved on form close
}
private void SettingsForm_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Escape)
{
Close();
}
}
private void SettingsForm_Leave(object sender, EventArgs e)
{
Close();
}
private void Form_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Escape)
{
Close();
}
}
protected override bool ProcessDialogKey(Keys keyData)
{
if (Form.ModifierKeys == Keys.None && keyData == Keys.Escape)
{
Close();
return true;
}
return base.ProcessDialogKey(keyData);
}
private void toggleNoDeviceMode_CheckedChanged(object sender, EventArgs e)
{
// Update UI state only - settings saved on form close
if (!toggleNoDeviceMode.Checked)
{
toggleDeleteAfterInstall.Checked = true;
toggleDeleteAfterInstall.Enabled = true;
lblDeleteAfterInstall.ForeColor = System.Drawing.Color.White;
}
else
{
toggleDeleteAfterInstall.SetCheckedSilent(false);
toggleDeleteAfterInstall.Enabled = false;
lblDeleteAfterInstall.ForeColor = System.Drawing.Color.FromArgb(100, 100, 100);
}
}
private void toggleBMBF_CheckedChanged(object sender, EventArgs e)
{
// Settings saved on form close
}
private void toggleAutoReinstall_CheckedChanged(object sender, EventArgs e)
{
// Settings saved on form close
}
private void toggleAutoReinstall_Click(object sender, EventArgs e)
{
if (toggleAutoReinstall.Checked)
{
DialogResult dialogResult = FlexibleMessageBox.Show(this,
"WARNING: This enables automatic reinstall when installs fail.\n\n" +
"Some games (less than 5%) don't allow access to their save data, " +
"which can lead to losing your progress.\n\n" +
"However, with this option enabled, you won't have to confirm reinstalls manually " +
"(ideal for queue installations).\n\n" +
"NOTE: If your USB/wireless ADB connection is slow, this may cause " +
"larger APK installations to fail.\n\nEnable anyway?",
"WARNING", MessageBoxButtons.OKCancel);
if (dialogResult == DialogResult.Cancel)
{
toggleAutoReinstall.SetCheckedSilent(false);
}
}
}
private void btnOpenDebug_Click(object sender, EventArgs e)
{
if (File.Exists($"{Environment.CurrentDirectory}\\debuglog.txt"))
{
_ = Process.Start($"{Environment.CurrentDirectory}\\debuglog.txt");
}
}
private void setDownloadDirectory_Click(object sender, EventArgs e)
{
var dialog = new FolderSelectDialog
{
Title = "Select Download Folder",
InitialDirectory = _settings.CustomDownloadDir && Directory.Exists(_settings.DownloadDir)
? _settings.DownloadDir
: Environment.CurrentDirectory
};
if (dialog.Show(this.Handle))
{
_settings.CustomDownloadDir = true;
_settings.DownloadDir = dialog.FileName;
}
}
private void setBackupDirectory_Click(object sender, EventArgs e)
{
var dialog = new FolderSelectDialog
{
Title = "Select Backup Folder",
InitialDirectory = _settings.GetEffectiveBackupDir()
};
if (dialog.Show(this.Handle))
{
_settings.CustomBackupDir = true;
_settings.BackupDir = dialog.FileName;
}
}
private void toggleSingleThread_CheckedChanged(object sender, EventArgs e)
{
// Settings saved on form close
}
private void toggleVirtualFilesystem_CheckedChanged(object sender, EventArgs e)
{
// Settings saved on form close
}
private void openDownloadDirectory_Click(object sender, EventArgs e)
{
string pathToOpen = _settings.CustomDownloadDir ? _settings.DownloadDir : Environment.CurrentDirectory;
MainForm.OpenDirectory(pathToOpen);
}
private void openBackupDirectory_Click(object sender, EventArgs e)
{
string pathToOpen = _settings.GetEffectiveBackupDir();
MainForm.OpenDirectory(pathToOpen);
}
private void bandwidthLimitTextBox_KeyPress(object sender, KeyPressEventArgs e)
{
if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) && (e.KeyChar != '.'))
{
e.Handled = true;
}
if ((e.KeyChar == '.') && ((sender as TextBox).Text.IndexOf('.') > -1))
{
e.Handled = true;
}
}
private void toggleProxy_CheckedChanged(object sender, EventArgs e)
{
// Settings saved on form close
}
}
}

View File

@@ -117,4 +117,13 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="downloadDirectorySetter.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="backupDirectorySetter.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>186, 17</value>
</metadata>
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>25</value>
</metadata>
</root>

Binary file not shown.

View File

@@ -1,27 +1,26 @@
using AndroidSideloader.Utilities;
using JR.Utils.GUI.Forms;
using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Management;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AndroidSideloader
{
internal class Sideloader
{
private static readonly SettingsManager settings = SettingsManager.Instance;
public static string TempFolder = Path.Combine(Environment.CurrentDirectory, "temp");
public static string CrashLogPath = "crashlog.txt";
public static string SpooferWarning = @"Please make sure you have installed:
- APKTool
- Java JDK
- aapt
And all of them added to PATH, without ANY of them, the spoofer won't work!";
//push user.json to device (required for some devices like oculus quest)
public static void PushUserJsons()
{
ADB.WakeDevice();
foreach (string userJson in UsernameForm.userJsons)
{
UsernameForm.createUserJsonByName(Utilities.GeneralUtilities.randomString(16), userJson);
@@ -34,19 +33,21 @@ And all of them added to PATH, without ANY of them, the spoofer won't work!";
//public static List<string> InstalledPackageNames = new List<string>(); //Remove folder from device
public static ProcessOutput RemoveFolder(string path)
{
ADB.WakeDevice();
return ADB.RunAdbCommandToString($"shell rm -r {path}");
if (path == "/sdcard/Android/obb/" || path == "sdcard/Android/data/")
{
return null;
}
return ADB.RunAdbCommandToString($"shell rm -r \"{path}\"");
}
public static ProcessOutput RemoveFile(string path)
{
ADB.WakeDevice();
return ADB.RunAdbCommandToString($"shell rm -f {path}");
return ADB.RunAdbCommandToString($"shell rm -f \"{path}\"");
}
//For games that require manual install, like having another folder that isnt an obb
public static ProcessOutput RunADBCommandsFromFile(string path)
{
ADB.WakeDevice();
ProcessOutput output = new ProcessOutput();
string[] commands = File.ReadAllLines(path);
string currfolder = Path.GetDirectoryName(path);
@@ -62,11 +63,11 @@ And all of them added to PATH, without ANY of them, the spoofer won't work!";
string replacement = "";
string pattern = "adb";
replacement = ADB.DeviceID.Length > 1
? $"{Properties.Settings.Default.ADBPath} -s {ADB.DeviceID}"
: $"{Properties.Settings.Default.ADBPath}";
? $"{settings.ADBPath} -s {ADB.DeviceID}"
: $"{settings.ADBPath}";
Regex rgx = new Regex(pattern);
string result = rgx.Replace(cmd, replacement);
Program.form.ChangeTitle($"Running {result}");
Program.form.changeTitle($"Running {result}");
_ = Logger.Log($"Logging command: {result} from file: {path}");
output += ADB.RunAdbCommandToStringWOADB(result, path);
if (output.Error.Contains("mkdir"))
@@ -84,13 +85,12 @@ And all of them added to PATH, without ANY of them, the spoofer won't work!";
return output;
}
//Recursive sideload any apk fileD
//Recursive sideload any apk file
public static ProcessOutput RecursiveOutput = new ProcessOutput();
public static void RecursiveSideload(string FolderPath)
public static async Task RecursiveSideloadAsync(
string FolderPath,
Action<float, TimeSpan?> progressCallback = null,
Action<string> statusCallback = null)
{
try
{
@@ -98,60 +98,75 @@ And all of them added to PATH, without ANY of them, the spoofer won't work!";
{
if (Path.GetExtension(f) == ".apk")
{
RecursiveOutput += ADB.Sideload(f);
string gameName = Path.GetFileNameWithoutExtension(f);
statusCallback?.Invoke(gameName);
RecursiveOutput += await ADB.SideloadWithProgressAsync(f, progressCallback, statusCallback, "", gameName);
}
}
foreach (string d in Directory.GetDirectories(FolderPath))
{
RecursiveSideload(d);
await RecursiveSideloadAsync(d, progressCallback, statusCallback);
}
}
catch (Exception ex) { _ = Logger.Log(ex.Message); }
catch (Exception ex) { _ = Logger.Log(ex.Message, LogLevel.ERROR); }
}
//Recursive copy any obb folder
public static void RecursiveCopyOBB(string FolderPath)
public static async Task RecursiveCopyOBBAsync(
string FolderPath,
Action<float, TimeSpan?> progressCallback = null,
Action<string> statusCallback = null)
{
try
{
foreach (string f in Directory.GetFiles(FolderPath))
{
RecursiveOutput += ADB.CopyOBB(f);
}
foreach (string d in Directory.GetDirectories(FolderPath))
{
RecursiveCopyOBB(d);
string folderName = Path.GetFileName(d);
if (folderName.Contains("."))
{
statusCallback?.Invoke(folderName);
RecursiveOutput += await ADB.CopyOBBWithProgressAsync(d, progressCallback, statusCallback, folderName);
}
else
{
await RecursiveCopyOBBAsync(d, progressCallback, statusCallback);
}
}
}
catch (Exception ex) { _ = Logger.Log(ex.Message); }
catch (Exception ex) { _ = Logger.Log(ex.Message, LogLevel.ERROR); }
}
public static string BackupFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), $"Rookie Backups");
//uninstalls an app
// Removes the game package and its OBB + Data Folders.
public static ProcessOutput UninstallGame(string packagename)
{
ADB.WakeDevice();
Program.form.ChangeTitle("Attempting to backup any savedata to Documents\\Rookie Backups...");
_ = new ProcessOutput("", "");
string date_str = DateTime.Today.ToString("yyyy.MM.dd");
string CurrBackups = Path.Combine(BackupFolder, date_str);
if (!Directory.Exists(CurrBackups))
{
_ = Directory.CreateDirectory(CurrBackups);
}
_ = ADB.RunAdbCommandToString($"pull \"/sdcard/Android/data/{packagename}\" \"{CurrBackups}\"");
ProcessOutput output = ADB.UninstallPackage(packagename);
Program.form.ChangeTitle("");
Program.form.changeTitle("");
_ = Sideloader.RemoveFolder("/sdcard/Android/obb/" + packagename);
_ = Sideloader.RemoveFolder("/sdcard/Android/data/" + packagename);
return output;
}
public static void BackupGame(string packagename)
{
MainForm.backupFolder = settings.GetEffectiveBackupDir();
if (!Directory.Exists(MainForm.backupFolder))
{
_ = Directory.CreateDirectory(MainForm.backupFolder);
}
Program.form.changeTitle($"Attempting to backup any savedata to {MainForm.backupFolder}\\Rookie Backups...");
_ = new ProcessOutput("", "");
string date_str = DateTime.Today.ToString("yyyy.MM.dd");
string CurrBackups = Path.Combine(MainForm.backupFolder, date_str);
if (!Directory.Exists(CurrBackups))
{
_ = Directory.CreateDirectory(CurrBackups);
}
_ = ADB.RunAdbCommandToString($"pull \"/sdcard/Android/data/{packagename}\" \"{CurrBackups}\"");
}
public static ProcessOutput DeleteFile(string GameName)
{
ADB.WakeDevice();
ProcessOutput output = new ProcessOutput("", "");
string packageName = Sideloader.gameNameToPackageName(GameName);
@@ -170,8 +185,6 @@ And all of them added to PATH, without ANY of them, the spoofer won't work!";
//Extracts apk from device, saves it by package name to sideloader folder
public static ProcessOutput getApk(string GameName)
{
ADB.WakeDevice();
_ = new ProcessOutput("", "");
string packageName = Sideloader.gameNameToPackageName(GameName);
@@ -183,26 +196,26 @@ And all of them added to PATH, without ANY of them, the spoofer won't work!";
apkPath = apkPath.Remove(apkPath.Length - 1);
apkPath = apkPath.Remove(0, 8); //remove package:
apkPath = apkPath.Remove(apkPath.Length - 1);
if (File.Exists($"{Properties.Settings.Default.ADBFolder}\\base.apk"))
if (File.Exists($"{settings.ADBFolder}\\base.apk"))
{
File.Delete($"{Properties.Settings.Default.ADBFolder}\\base.apk");
File.Delete($"{settings.ADBFolder}\\base.apk");
}
if (File.Exists($"{Properties.Settings.Default.MainDir}\\{packageName}\\{packageName}.apk"))
if (File.Exists($"{settings.MainDir}\\{packageName}\\{packageName}.apk"))
{
File.Delete($"{Properties.Settings.Default.MainDir}\\{packageName}\\{packageName}.apk");
File.Delete($"{settings.MainDir}\\{packageName}\\{packageName}.apk");
}
output += ADB.RunAdbCommandToString("pull " + apkPath); //pull apk
if (Directory.Exists($"{Properties.Settings.Default.MainDir}\\{packageName}"))
if (Directory.Exists($"{settings.MainDir}\\{packageName}"))
{
Directory.Delete($"{Properties.Settings.Default.MainDir}\\{packageName}", true);
FileSystemUtilities.TryDeleteDirectory($"{settings.MainDir}\\{packageName}");
}
_ = Directory.CreateDirectory($"{Properties.Settings.Default.MainDir}\\{packageName}");
_ = Directory.CreateDirectory($"{settings.MainDir}\\{packageName}");
File.Move($"{Properties.Settings.Default.ADBFolder}\\base.apk", $"{Properties.Settings.Default.MainDir}\\{packageName}\\{packageName}.apk");
File.Move($"{settings.ADBFolder}\\base.apk", $"{settings.MainDir}\\{packageName}\\{packageName}.apk");
return output;
}
@@ -210,148 +223,50 @@ And all of them added to PATH, without ANY of them, the spoofer won't work!";
{
foreach (string[] game in SideloaderRCLONE.games)
{
if (gameName.Equals(game[SideloaderRCLONE.GameNameIndex]))
{
if (gameName.Equals(game[SideloaderRCLONE.GameNameIndex]) || gameName.Equals(game[SideloaderRCLONE.ReleaseNameIndex]))
return game[SideloaderRCLONE.PackageNameIndex];
}
if (gameName.Equals(game[SideloaderRCLONE.ReleaseNameIndex]))
{
return game[SideloaderRCLONE.PackageNameIndex];
}
}
return gameName;
}
public static string PackageNametoGameName(string gameName)
public static string gameNameToVersionCode(string gameName)
{
foreach (string[] game in SideloaderRCLONE.games)
{
if (gameName.Equals(game[SideloaderRCLONE.PackageNameIndex]))
{
return game[SideloaderRCLONE.ReleaseNameIndex];
}
if (gameName.Equals(game[SideloaderRCLONE.GameNameIndex]) || gameName.Equals(game[SideloaderRCLONE.ReleaseNameIndex]))
return game[SideloaderRCLONE.VersionCodeIndex];
}
return gameName;
}
public static string PackageNametoGameName(string packageName)
{
foreach (string[] game in SideloaderRCLONE.games)
{
if (packageName.Equals(game[SideloaderRCLONE.PackageNameIndex]))
return game[SideloaderRCLONE.ReleaseNameIndex];
}
return packageName;
}
public static string gameNameToSimpleName(string gameName)
{
foreach (string[] game in SideloaderRCLONE.games)
{
if (gameName.Equals(game[SideloaderRCLONE.GameNameIndex]))
{
if (gameName.Equals(game[SideloaderRCLONE.GameNameIndex]) || gameName.Equals(game[SideloaderRCLONE.ReleaseNameIndex]))
return game[SideloaderRCLONE.GameNameIndex];
}
if (gameName.Equals(game[SideloaderRCLONE.ReleaseNameIndex]))
{
return game[SideloaderRCLONE.GameNameIndex];
}
}
return gameName;
}
public static string PackageNameToSimpleName(string gameName)
public static string PackageNameToSimpleName(string packageName)
{
foreach (string[] game in SideloaderRCLONE.games)
{
if (gameName.Contains(game[SideloaderRCLONE.PackageNameIndex]))
{
if (packageName.Contains(game[SideloaderRCLONE.PackageNameIndex]))
return game[SideloaderRCLONE.GameNameIndex];
}
}
return gameName;
}
//Downloads the required files
public static void downloadFiles()
{
WebClient client = new WebClient();
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var currentAccessedWebsite = "";
try
{
if (!File.Exists("Sideloader Launcher.exe"))
{
currentAccessedWebsite = "github";
client.DownloadFile("https://github.com/nerdunit/androidsideloader/raw/master/Sideloader%20Launcher.exe", "Sideloader Launcher.exe");
}
if (!File.Exists("Rookie Offline.cmd"))
{
currentAccessedWebsite = "github";
client.DownloadFile("https://github.com/nerdunit/androidsideloader/raw/master/Rookie%20Offline.cmd", "Rookie Offline.cmd");
}
if (!File.Exists("C:\\RSL\\platform-tools\\aug2021.txt") || !File.Exists("C:\\RSL\\platform-tools\\adb.exe")) //if adb is not updated, download and auto extract
{
if (Directory.Exists($"C:\\RSL\\2.8.2"))
{
Directory.Delete("C:\\RSL\\2.8.2", true);
}
if (Directory.Exists($"{Properties.Settings.Default.MainDir}\\adb"))
{
Directory.Delete($"{Properties.Settings.Default.MainDir}\\adb", true);
}
if (!Directory.Exists("C:\\RSL\\platform-tools"))
{
_ = Directory.CreateDirectory("C:\\RSL\\platform-tools");
}
currentAccessedWebsite = "github";
client.DownloadFile("https://github.com/nerdunit/androidsideloader/raw/master/adb2.zip", "Ad.7z");
Utilities.Zip.ExtractFile(Environment.CurrentDirectory + "\\Ad.7z", "C:\\RSL\\platform-tools");
File.Delete("Ad.7z");
}
if (!Directory.Exists(Environment.CurrentDirectory + "\\rclone"))
{
currentAccessedWebsite = "rclone";
string url = Environment.Is64BitOperatingSystem
? "https://downloads.rclone.org/v1.55.1/rclone-v1.55.1-windows-amd64.zip"
: "https://downloads.rclone.org/v1.55.1/rclone-v1.55.1-windows-386.zip";
//Since sideloader is build for x86, it should work on both x86 and x64 so we download the according rclone version
client.DownloadFile(url, "rclone.zip");
Utilities.Zip.ExtractFile(Environment.CurrentDirectory + "\\rclone.zip", Environment.CurrentDirectory);
File.Delete("rclone.zip");
string[] folders = Directory.GetDirectories(Environment.CurrentDirectory);
foreach (string folder in folders)
{
if (folder.Contains("rclone"))
{
Directory.Move(folder, "rclone");
break; //only 1 rclone folder
}
}
}
}
catch (Exception ex)
{
if (currentAccessedWebsite == "github")
{
_ = FlexibleMessageBox.Show($"You are unable to access the github page with the Exception: {ex.Message}\n Some files may be missing (ADB, Offline Script, Launcher)");
_ = FlexibleMessageBox.Show("These required files were unable to be downloaded\nRookie will now close, please use Offline Mode for manual sideloading if needed");
Application.Exit();
}
if (currentAccessedWebsite == "rclone")
{
_ = FlexibleMessageBox.Show($"You are unable to access the rclone page with the Exception: {ex.Message}\n Some files may be missing (RCLONE)");
_ = FlexibleMessageBox.Show("Rclone was unable to be downloaded\nRookie will now close, please use Offline Mode for manual sideloading if needed");
Application.Exit();
}
}
return packageName;
}
}
}

View File

@@ -0,0 +1,333 @@
using AndroidSideloader.Utilities;
using JR.Utils.GUI.Forms;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AndroidSideloader
{
internal class GetDependencies
{
public static void updatePublicConfig()
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12
| SecurityProtocolType.Ssl3;
_ = Logger.Log("Attempting to update public config from main.");
string configUrl = "https://raw.githubusercontent.com/vrpyou/quest/main/vrp-public.json";
string fallbackUrl = "https://vrpirates.wiki/downloads/vrp-public.json";
try
{
string resultString;
// Try fetching raw JSON data from the provided link
HttpWebRequest getUrl = DnsHelper.CreateWebRequest(configUrl);
using (StreamReader responseReader = new StreamReader(getUrl.GetResponse().GetResponseStream()))
{
resultString = responseReader.ReadToEnd();
_ = Logger.Log($"Retrieved updated config from main: {configUrl}.");
File.WriteAllText(Path.Combine(Environment.CurrentDirectory, "vrp-public.json"), resultString);
_ = Logger.Log("Public config updated successfully from main.");
}
}
catch (Exception mainException)
{
_ = Logger.Log($"Failed to update public config from main: {mainException.Message}, trying fallback.", LogLevel.ERROR);
try
{
HttpWebRequest getUrl = DnsHelper.CreateWebRequest(fallbackUrl);
using (StreamReader responseReader = new StreamReader(getUrl.GetResponse().GetResponseStream()))
{
string resultString = responseReader.ReadToEnd();
_ = Logger.Log($"Retrieved updated config from fallback: {fallbackUrl}.");
File.WriteAllText(Path.Combine(Environment.CurrentDirectory, "vrp-public.json"), resultString);
_ = Logger.Log("Public config updated successfully from fallback.");
}
}
catch (Exception fallbackException)
{
_ = Logger.Log($"Failed to update public config from fallback: {fallbackException.Message}.", LogLevel.ERROR);
}
}
}
// Download required dependencies
public static void downloadFiles()
{
// Initialize DNS helper early to detect and configure fallback if needed
DnsHelper.Initialize();
WebClient client = new WebClient();
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var currentAccessedWebsite = "";
try
{
if (!File.Exists("Sideloader Launcher.exe"))
{
currentAccessedWebsite = "github";
_ = Logger.Log($"Missing 'Sideloader Launcher.exe'. Attempting to download from {currentAccessedWebsite}");
DownloadFileWithDnsFallback(client, "https://github.com/VRPirates/rookie/raw/master/Sideloader%20Launcher.exe", "Sideloader Launcher.exe");
_ = Logger.Log($"'Sideloader Launcher.exe' download successful");
}
if (!File.Exists("Rookie Offline.cmd"))
{
currentAccessedWebsite = "github";
_ = Logger.Log($"Missing 'Rookie Offline.cmd'. Attempting to download from {currentAccessedWebsite}");
DownloadFileWithDnsFallback(client, "https://github.com/VRPirates/rookie/raw/master/Rookie%20Offline.cmd", "Rookie Offline.cmd");
_ = Logger.Log($"'Rookie Offline.cmd' download successful");
}
if (!File.Exists("CleanupInstall.cmd"))
{
currentAccessedWebsite = "github";
_ = Logger.Log($"Missing 'CleanupInstall.cmd'. Attempting to download from {currentAccessedWebsite}");
DownloadFileWithDnsFallback(client, "https://github.com/VRPirates/rookie/raw/master/CleanupInstall.cmd", "CleanupInstall.cmd");
_ = Logger.Log($"'CleanupInstall.cmd' download successful");
}
if (!File.Exists("AddDefenderExceptions.ps1"))
{
currentAccessedWebsite = "github";
_ = Logger.Log($"Missing 'AddDefenderExceptions.ps1'. Attempting to download from {currentAccessedWebsite}");
DownloadFileWithDnsFallback(client, "https://github.com/VRPirates/rookie/raw/master/AddDefenderExceptions.ps1", "AddDefenderExceptions.ps1");
_ = Logger.Log($"'AddDefenderExceptions.ps1' download successful");
}
}
catch (Exception ex)
{
_ = FlexibleMessageBox.Show(Program.form, $"You are unable to access raw.githubusercontent.com with the Exception:\n{ex.Message}\n\nSome files may be missing (Offline/Cleanup Script, Launcher)");
}
string adbPath = Path.Combine(Environment.CurrentDirectory, "platform-tools", "adb.exe");
string platformToolsDir = Path.Combine(Environment.CurrentDirectory, "platform-tools");
try
{
if (!File.Exists(adbPath)) //if adb is not updated, download and auto extract
{
if (!Directory.Exists(platformToolsDir))
{
_ = Directory.CreateDirectory(platformToolsDir);
}
currentAccessedWebsite = "github";
_ = Logger.Log($"Missing adb within {platformToolsDir}. Attempting to download from {currentAccessedWebsite}");
DownloadFileWithDnsFallback(client, "https://github.com/VRPirates/rookie/raw/master/dependencies.7z", "dependencies.7z");
Utilities.Zip.ExtractFile(Path.Combine(Environment.CurrentDirectory, "dependencies.7z"), platformToolsDir);
File.Delete("dependencies.7z");
_ = Logger.Log($"adb download successful");
}
}
catch (Exception ex)
{
_ = FlexibleMessageBox.Show(Program.form, $"You are unable to access raw.githubusercontent.com page with the Exception:\n{ex.Message}\n\nSome files may be missing (ADB)");
_ = FlexibleMessageBox.Show(Program.form, "ADB was unable to be downloaded\nRookie will now close.");
Application.Exit();
}
string wantedRcloneVersion = "1.72.1";
bool rcloneSuccess = false;
rcloneSuccess = downloadRclone(wantedRcloneVersion, false);
if (!rcloneSuccess)
{
rcloneSuccess = downloadRclone(wantedRcloneVersion, true);
}
if (!rcloneSuccess)
{
_ = Logger.Log($"Unable to download rclone", LogLevel.ERROR);
_ = FlexibleMessageBox.Show(Program.form, "Rclone was unable to be downloaded\nRookie will now close, please use Offline Mode for manual sideloading if needed");
Application.Exit();
}
// Download WebView2 runtime if needed
downloadWebView2Runtime();
}
// Downloads a file using the DNS fallback proxy if active
public static void DownloadFileWithDnsFallback(WebClient client, string url, string localPath)
{
try
{
// Use DNS fallback proxy if active
if (DnsHelper.UseFallbackDns && !string.IsNullOrEmpty(DnsHelper.ProxyUrl))
{
client.Proxy = new WebProxy(DnsHelper.ProxyUrl);
}
client.DownloadFile(url, localPath);
}
catch (Exception ex)
{
_ = Logger.Log($"Download failed for {url}: {ex.Message}", LogLevel.ERROR);
throw;
}
finally
{
// Reset proxy to avoid affecting other operations
client.Proxy = null;
}
}
// Overload that creates its own WebClient for convenience
public static void DownloadFileWithDnsFallback(string url, string localPath)
{
using (var client = new WebClient())
{
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
DownloadFileWithDnsFallback(client, url, localPath);
}
}
// Downloads WebView2 runtime if not present
private static void downloadWebView2Runtime()
{
string runtimesPath = Path.Combine(Environment.CurrentDirectory, "runtimes");
string webView2LoaderArm64 = Path.Combine(runtimesPath, "win-arm64", "native", "WebView2Loader.dll");
string webView2LoaderX86 = Path.Combine(runtimesPath, "win-x86", "native", "WebView2Loader.dll");
string webView2LoaderX64 = Path.Combine(runtimesPath, "win-x64", "native", "WebView2Loader.dll");
bool runtimeExists = File.Exists(webView2LoaderX86) || File.Exists(webView2LoaderX64) || File.Exists(webView2LoaderArm64);
if (!runtimeExists)
{
try
{
_ = Logger.Log("Missing WebView2 runtime. Attempting to download...");
string archivePath = Path.Combine(Environment.CurrentDirectory, "runtimes.7z");
DownloadFileWithDnsFallback("https://vrpirates.wiki/downloads/runtimes.7z", archivePath);
_ = Logger.Log("Extracting WebView2 runtime...");
Utilities.Zip.ExtractFile(archivePath, Environment.CurrentDirectory);
File.Delete(archivePath);
_ = Logger.Log("WebView2 runtime download successful");
}
catch (Exception ex)
{
_ = Logger.Log($"Failed to download WebView2 runtime: {ex.Message}", LogLevel.ERROR);
// Don't show message box here - let CreateEnvironment handle the UI feedback
}
}
}
public static bool downloadRclone(string wantedRcloneVersion, bool useFallback = false)
{
try
{
bool updateRclone = false;
string currentRcloneVersion = "0.0.0";
WebClient client = new WebClient();
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
_ = Logger.Log($"Checking for Local rclone...");
string dirRclone = Path.Combine(Environment.CurrentDirectory, "rclone");
string pathToRclone = Path.Combine(dirRclone, "rclone.exe");
if (File.Exists(pathToRclone))
{
var versionInfo = FileVersionInfo.GetVersionInfo(pathToRclone);
currentRcloneVersion = versionInfo.ProductVersion;
Logger.Log($"Current RCLONE Version {currentRcloneVersion}");
if (!MainForm.noRcloneUpdating)
{
if (currentRcloneVersion != wantedRcloneVersion)
{
updateRclone = true;
_ = Logger.Log($"RCLONE Version does not match ({currentRcloneVersion})! Downloading required version ({wantedRcloneVersion})");
}
}
}
else
{
updateRclone = true;
_ = Logger.Log($"RCLONE exe does not exist, attempting to download");
}
if (!Directory.Exists(dirRclone))
{
updateRclone = true;
_ = Logger.Log($"Missing RCLONE Folder, attempting to download");
Directory.CreateDirectory(dirRclone);
}
if (updateRclone == true)
{
// Preserve vrp.download.config if it exists
string configPath = Path.Combine(dirRclone, "vrp.download.config");
string tempConfigPath = Path.Combine(Environment.CurrentDirectory, "vrp.download.config.bak");
bool hasConfig = false;
if (File.Exists(configPath))
{
_ = Logger.Log("Preserving vrp.download.config before update");
File.Copy(configPath, tempConfigPath, true);
hasConfig = true;
}
string architecture = Environment.Is64BitOperatingSystem ? "amd64" : "386";
string url = $"https://downloads.rclone.org/v{wantedRcloneVersion}/rclone-v{wantedRcloneVersion}-windows-{architecture}.zip";
if (useFallback == true)
{
_ = Logger.Log($"Using git fallback for rclone download");
url = $"https://raw.githubusercontent.com/VRPirates/rookie/master/dep/rclone-v{wantedRcloneVersion}-windows-{architecture}.zip";
}
_ = Logger.Log($"Downloading rclone from {url}");
_ = Logger.Log("Begin download rclone");
DownloadFileWithDnsFallback(client, url, "rclone.zip");
_ = Logger.Log("Complete download rclone");
_ = Logger.Log($"Extract {Environment.CurrentDirectory}\\rclone.zip");
Utilities.Zip.ExtractFile(Path.Combine(Environment.CurrentDirectory, "rclone.zip"), Environment.CurrentDirectory);
string dirExtractedRclone = Path.Combine(Environment.CurrentDirectory, $"rclone-v{wantedRcloneVersion}-windows-{architecture}");
File.Delete("rclone.zip");
_ = Logger.Log("rclone extracted. Moving files");
foreach (string file in Directory.GetFiles(dirExtractedRclone))
{
string fileName = Path.GetFileName(file);
string destFile = Path.Combine(dirRclone, fileName);
if (File.Exists(destFile))
{
File.Delete(destFile);
}
File.Move(file, destFile);
}
FileSystemUtilities.TryDeleteDirectory(dirExtractedRclone);
// Restore vrp.download.config if it was backed up
if (hasConfig && File.Exists(tempConfigPath))
{
_ = Logger.Log("Restoring vrp.download.config after update");
File.Move(tempConfigPath, configPath);
}
_ = Logger.Log($"rclone download successful");
}
return true;
}
catch (Exception ex)
{
_ = Logger.Log($"Unable to download rclone: {ex}", LogLevel.ERROR);
return false;
}
}
}
}

View File

@@ -1,35 +1,27 @@
using AndroidSideloader.Utilities;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
namespace AndroidSideloader
{
internal class rcloneFolder
{
public string Path { get; set; }
public string Name { get; set; }
public string Size { get; set; }
public string ModTime { get; set; }
}
internal class SideloaderRCLONE
{
public static List<string> RemotesList = new List<string>();
public static string RcloneGamesFolder = "Quest Games";
//This shit sucks but i'll switch to programatically adding indexes from the gamelist txt sometimes maybe
public static int GameNameIndex = 0;
public static int ReleaseNameIndex = 1;
public static int PackageNameIndex = 2;
public static int VersionCodeIndex = 3;
public static int ReleaseAPKPathIndex = 4;
public static int VersionNameIndex = 5;
public static int DownloadsIndex = 6;
public static int InstalledVersion = 7;
public static List<string> gameProperties = new List<string>();
/* Game Name
@@ -41,9 +33,9 @@ namespace AndroidSideloader
*/
public static List<string[]> games = new List<string[]>();
public static string Nouns = Environment.CurrentDirectory + "\\nouns";
public static string ThumbnailsFolder = Environment.CurrentDirectory + "\\thumbnails";
public static string NotesFolder = Environment.CurrentDirectory + "\\notes";
public static string Nouns = Path.Combine(Environment.CurrentDirectory, "nouns");
public static string ThumbnailsFolder = Path.Combine(Environment.CurrentDirectory, "thumbnails");
public static string NotesFolder = Path.Combine(Environment.CurrentDirectory, "notes");
public static void UpdateNouns(string remote)
{
@@ -54,7 +46,7 @@ namespace AndroidSideloader
public static void UpdateGamePhotos(string remote)
{
_ = Logger.Log($"Updating Thumbnails");
_ = RCLONE.runRcloneCommand_DownloadConfig($"sync \"{remote}:{RcloneGamesFolder}/.meta/thumbnails\" \"{ThumbnailsFolder}\"");
_ = RCLONE.runRcloneCommand_DownloadConfig($"sync \"{remote}:{RcloneGamesFolder}/.meta/thumbnails\" \"{ThumbnailsFolder}\" --transfers 10");
}
public static void UpdateGameNotes(string remote)
@@ -75,46 +67,91 @@ namespace AndroidSideloader
{
try
{
_ = Logger.Log($"Extracting Metadata");
Zip.ExtractFile($"{Environment.CurrentDirectory}\\meta.7z", $"{Environment.CurrentDirectory}\\meta",
MainForm.PublicConfigFile.Password);
var sw = Stopwatch.StartNew();
_ = Logger.Log($"Updating Metadata");
string currentDir = Environment.CurrentDirectory;
string metaRoot = Path.Combine(currentDir, "meta");
string metaArchive = Path.Combine(currentDir, "meta.7z");
string metaDotMeta = Path.Combine(metaRoot, ".meta");
if (Directory.Exists(Nouns))
// Check if archive exists and is newer than existing metadata
if (!File.Exists(metaArchive))
{
Directory.Delete(Nouns, true);
Logger.Log("meta.7z not found, skipping extraction", LogLevel.WARNING);
return;
}
if (Directory.Exists(ThumbnailsFolder))
// Skip extraction if metadata is already up-to-date (based on file timestamps)
string gameListPath = Path.Combine(metaRoot, "VRP-GameList.txt");
if (File.Exists(gameListPath))
{
Directory.Delete(ThumbnailsFolder, true);
}
var archiveTime = File.GetLastWriteTimeUtc(metaArchive);
var gameListTime = File.GetLastWriteTimeUtc(gameListPath);
if (Directory.Exists(NotesFolder))
{
Directory.Delete(NotesFolder, true);
}
Directory.Move($"{Environment.CurrentDirectory}\\meta\\.meta\\nouns", Nouns);
Directory.Move($"{Environment.CurrentDirectory}\\meta\\.meta\\thumbnails", ThumbnailsFolder);
Directory.Move($"{Environment.CurrentDirectory}\\meta\\.meta\\notes", NotesFolder);
_ = Logger.Log($"Initializing Games List");
string gameList = File.ReadAllText($"{Environment.CurrentDirectory}\\meta\\VRP-GameList.txt");
string[] splitList = gameList.Split('\n');
splitList = splitList.Skip(1).ToArray();
foreach (string game in splitList)
{
if (game.Length > 1)
// If game list is newer than archive, skip extraction
if (gameListTime > archiveTime && games.Count > 0)
{
string[] splitGame = game.Split(';');
games.Add(splitGame);
Logger.Log($"Metadata already up-to-date, skipping extraction");
return;
}
}
Directory.Delete($"{Environment.CurrentDirectory}\\meta", true);
_ = Logger.Log($"Extracting Metadata");
Zip.ExtractFile(metaArchive, metaRoot, MainForm.PublicConfigFile.Password);
Logger.Log($"Extraction completed in {sw.ElapsedMilliseconds}ms");
sw.Restart();
_ = Logger.Log($"Updating Metadata");
// Use Parallel.Invoke for independent directory operations
System.Threading.Tasks.Parallel.Invoke(
() => SafeDeleteDirectory(Nouns),
() => SafeDeleteDirectory(ThumbnailsFolder),
() => SafeDeleteDirectory(NotesFolder)
);
Logger.Log($"Directory cleanup in {sw.ElapsedMilliseconds}ms");
sw.Restart();
// Move directories
MoveIfExists(Path.Combine(metaDotMeta, "nouns"), Nouns);
MoveIfExists(Path.Combine(metaDotMeta, "thumbnails"), ThumbnailsFolder);
MoveIfExists(Path.Combine(metaDotMeta, "notes"), NotesFolder);
Logger.Log($"Directory moves in {sw.ElapsedMilliseconds}ms");
sw.Restart();
_ = Logger.Log($"Initializing Games List");
gameListPath = Path.Combine(metaRoot, "VRP-GameList.txt");
if (File.Exists(gameListPath))
{
// Read all lines at once - faster for files that fit in memory
var lines = File.ReadAllLines(gameListPath);
var newGames = new List<string[]>(lines.Length);
for (int i = 1; i < lines.Length; i++) // Skip header
{
var line = lines[i];
if (string.IsNullOrWhiteSpace(line))
continue;
var splitGame = line.Split(';');
if (splitGame.Length > 1)
{
newGames.Add(splitGame);
}
}
// Atomic swap
games.Clear();
games.AddRange(newGames);
Logger.Log($"Parsed {games.Count} games in {sw.ElapsedMilliseconds}ms");
}
else
{
_ = Logger.Log("VRP-GameList.txt not found in extracted metadata.", LogLevel.WARNING);
}
SafeDeleteDirectory(metaRoot);
}
catch (Exception e)
{
@@ -123,122 +160,49 @@ namespace AndroidSideloader
}
}
public static void RefreshRemotes()
{
_ = Logger.Log($"Refresh / List Remotes");
RemotesList.Clear();
string[] remotes = RCLONE.runRcloneCommand_DownloadConfig("listremotes").Output.Split('\n');
_ = Logger.Log("Loaded following remotes: ");
foreach (string r in remotes)
{
if (r.Length > 1)
{
string remote = r.Remove(r.Length - 1);
if (remote.Contains("mirror"))
{
_ = Logger.Log(remote);
RemotesList.Add(remote);
}
}
}
}
public static void initGames(string remote)
{
_ = Logger.Log($"Initializing Games List");
gameProperties.Clear();
games.Clear();
// Fetch once, then process as lines
string tempGameList = RCLONE.runRcloneCommand_DownloadConfig($"cat \"{remote}:{RcloneGamesFolder}/VRP-GameList.txt\"").Output;
if (MainForm.debugMode)
{
File.WriteAllText("VRP-GamesList.txt", tempGameList);
}
if (!tempGameList.Equals(""))
{
string[] gameListSplited = tempGameList.Split(new[] { '\n' });
gameListSplited = gameListSplited.Skip(1).ToArray();
foreach (string game in gameListSplited)
// Avoid redundant disk I/O: write only if non-empty
if (!string.IsNullOrEmpty(tempGameList))
{
if (game.Length > 1)
File.WriteAllText("VRP-GamesList.txt", tempGameList);
}
}
if (!string.IsNullOrEmpty(tempGameList))
{
bool isFirstLine = true;
foreach (var line in SplitLines(tempGameList))
{
if (isFirstLine)
{
isFirstLine = false; // skip header
continue;
}
if (string.IsNullOrWhiteSpace(line))
{
continue;
}
var splitGame = line.Split(new[] { ';' }, StringSplitOptions.None);
if (splitGame.Length > 1)
{
string[] splitGame = game.Split(';');
games.Add(splitGame);
}
}
}
}
public static void updateDownloadConfig()
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12
| SecurityProtocolType.Ssl3;
_ = Logger.Log($"Attempting to Update Download Config");
try
{
string configUrl = "https://wiki.vrpirates.club/downloads/vrp.download.config";
HttpWebRequest getUrl = (HttpWebRequest)WebRequest.Create(configUrl);
using (StreamReader responseReader = new StreamReader(getUrl.GetResponse().GetResponseStream()))
{
string resultString = responseReader.ReadToEnd();
_ = Logger.Log($"Retrived updated config from: {configUrl}");
if (File.Exists(Environment.CurrentDirectory + "\\rclone\\vrp.download.config_new"))
{
File.Delete(Environment.CurrentDirectory + "\\rclone\\vrp.download.config_new");
}
File.Create(Environment.CurrentDirectory + "\\rclone\\vrp.download.config_new").Close();
File.WriteAllText(Environment.CurrentDirectory + "\\rclone\\vrp.download.config_new", resultString);
if (!File.Exists(Environment.CurrentDirectory + "\\rclone\\hash.txt"))
{
File.Create(Environment.CurrentDirectory + "\\rclone\\hash.txt").Close();
}
string newConfig = CalculateMD5(Environment.CurrentDirectory + "\\rclone\\vrp.download.config_new");
string oldConfig = File.ReadAllText(Environment.CurrentDirectory + "\\rclone\\hash.txt");
if (!File.Exists(Environment.CurrentDirectory + "\\rclone\\vrp.download.config"))
{
oldConfig = "Config Doesnt Exist!";
}
_ = Logger.Log($"Online Config Hash: {newConfig}; Local Config Hash: {oldConfig}");
if (newConfig != oldConfig)
{
_ = Logger.Log($"Updated Config Hash is different than the current Config. Updating Configuration File.");
if (File.Exists(Environment.CurrentDirectory + "\\rclone\\vrp.download.config"))
{
File.Delete(Environment.CurrentDirectory + "\\rclone\\vrp.download.config");
}
File.Move(Environment.CurrentDirectory + "\\rclone\\vrp.download.config_new", Environment.CurrentDirectory + "\\rclone\\vrp.download.config");
File.WriteAllText(Environment.CurrentDirectory + "\\rclone\\hash.txt", string.Empty);
File.WriteAllText(Environment.CurrentDirectory + "\\rclone\\hash.txt", newConfig);
}
else
{
_ = Logger.Log($"Updated Config Hash matches last download. Not updating.");
if (File.Exists(Environment.CurrentDirectory + "\\rclone\\vrp.download.config_new"))
{
File.Delete(Environment.CurrentDirectory + "\\rclone\\vrp.download.config_new");
}
}
}
}
catch { }
}
public static void updateUploadConfig()
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
@@ -248,63 +212,119 @@ namespace AndroidSideloader
_ = Logger.Log($"Attempting to Update Upload Config");
try
{
string configUrl = "https://wiki.vrpirates.club/downloads/vrp.upload.config";
string configUrl = "https://vrpirates.wiki/downloads/vrp.upload.config";
HttpWebRequest getUrl = (HttpWebRequest)WebRequest.Create(configUrl);
using (StreamReader responseReader = new StreamReader(getUrl.GetResponse().GetResponseStream()))
// Use DnsHelper for fallback DNS support
var getUrl = DnsHelper.CreateWebRequest(configUrl);
using (var response = getUrl.GetResponse())
using (var stream = response.GetResponseStream())
using (var responseReader = new StreamReader(stream))
{
string resultString = responseReader.ReadToEnd();
_ = Logger.Log($"Retrived updated config from: {configUrl}");
_ = Logger.Log($"Retrieved updated config from: {configUrl}");
File.WriteAllText(Environment.CurrentDirectory + "\\rclone\\vrp.upload.config", resultString);
// Avoid multiple combines; write once
string uploadConfigPath = Path.Combine(Environment.CurrentDirectory, "rclone", "vrp.upload.config");
File.WriteAllText(uploadConfigPath, resultString);
_ = Logger.Log("Upload config updated successfully.");
}
}
catch (Exception e)
{
_ = Logger.Log($"Failed to update Upload config: {e.Message}");
_ = Logger.Log($"Failed to update Upload config: {e.Message}", LogLevel.ERROR);
}
}
public static void updatePublicConfig()
// Fast directory delete using Windows cmd - faster than .NET's Directory.Delete
// for large directories with many files (e.g., thumbnails folder with 1000+ images)
private static void SafeDeleteDirectory(string path)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12
| SecurityProtocolType.Ssl3;
_ = Logger.Log($"Attempting to Update Public Config");
// Avoid exceptions when directory is missing
if (!Directory.Exists(path))
return;
try
{
string configUrl = "https://wiki.vrpirates.club/downloads/vrp-public.json";
HttpWebRequest getUrl = (HttpWebRequest)WebRequest.Create(configUrl);
using (StreamReader responseReader = new StreamReader(getUrl.GetResponse().GetResponseStream()))
// Use Windows rd command which is ~10x faster than .NET's recursive delete
var psi = new ProcessStartInfo
{
string resultString = responseReader.ReadToEnd();
FileName = "cmd.exe",
Arguments = $"/c rd /s /q \"{path}\"",
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true
};
_ = Logger.Log($"Retrived updated config from: {configUrl}");
File.WriteAllText(Environment.CurrentDirectory + "\\vrp-public.json", resultString);
_ = Logger.Log("Public config updated successfully.");
using (var process = Process.Start(psi))
{
// Wait with timeout to prevent hanging
if (!process.WaitForExit(30000)) // 30 second timeout
{
try { process.Kill(); } catch { }
Logger.Log($"Directory delete timed out for: {path}", LogLevel.WARNING);
// Fallback to .NET delete
FallbackDelete(path);
}
else if (process.ExitCode != 0 && Directory.Exists(path))
{
// Command failed, try fallback
FallbackDelete(path);
}
}
}
catch (Exception e)
catch (Exception ex)
{
_ = Logger.Log($"Failed to update Public config: {e.Message}");
Logger.Log($"Fast delete failed for {path}: {ex.Message}", LogLevel.WARNING);
// Fallback to standard .NET delete
FallbackDelete(path);
}
}
private static string CalculateMD5(string filename)
// Fallback delete method using standard .NET
private static void FallbackDelete(string path)
{
using (MD5 md5 = MD5.Create())
try
{
using (FileStream stream = File.OpenRead(filename))
if (Directory.Exists(path))
{
byte[] hash = md5.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
FileSystemUtilities.TryDeleteDirectory(path);
}
}
catch (Exception ex)
{
Logger.Log($"Fallback delete also failed for {path}: {ex.Message}", LogLevel.ERROR);
}
}
// Move directory only if source exists
private static void MoveIfExists(string sourceDir, string destDir)
{
if (Directory.Exists(sourceDir))
{
// Ensure destination does not exist to prevent IOException
// Use fast delete method
SafeDeleteDirectory(destDir);
Directory.Move(sourceDir, destDir);
}
else
{
_ = Logger.Log($"Source directory not found: {sourceDir}", LogLevel.WARNING);
}
}
// Efficient, cross-platform line splitting for string buffers
private static IEnumerable<string> SplitLines(string s)
{
// Handle both \r\n and \n without allocating intermediate arrays
using (var reader = new StringReader(s))
{
string line;
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using AndroidSideloader.Utilities;
using System;
using System.IO;
using System.Management;
using System.Security.Cryptography;
@@ -8,6 +9,7 @@ namespace AndroidSideloader
{
internal class SideloaderUtilities
{
private static readonly SettingsManager settings = SettingsManager.Instance;
public static bool CheckFolderIsObb(string path)
{
string[] files = Directory.GetFiles(path);
@@ -26,49 +28,23 @@ namespace AndroidSideloader
private static string uuid = null;
public static string UUID()
{
if (uuid != null)
uuid = settings.UUID;
if (string.IsNullOrEmpty(uuid) != true)
{
return uuid;
}
StringBuilder sb = new StringBuilder();
ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_Processor");
foreach (ManagementObject queryObj in searcher.Get())
var bytes = new byte[16];
using (var rng = new RNGCryptoServiceProvider())
{
_ = sb.Append(queryObj["NumberOfCores"]);
_ = sb.Append(queryObj["ProcessorId"]);
_ = sb.Append(queryObj["Name"]);
_ = sb.Append(queryObj["SocketDesignation"]);
rng.GetBytes(bytes);
}
searcher = new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_BIOS");
uuid = BitConverter.ToString(bytes).Replace("-", "");
foreach (ManagementObject queryObj in searcher.Get())
{
_ = sb.Append(queryObj["Manufacturer"]);
_ = sb.Append(queryObj["Name"]);
_ = sb.Append(queryObj["Version"]);
settings.UUID = uuid;
settings.Save();
}
searcher = new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_BaseBoard");
foreach (ManagementObject queryObj in searcher.Get())
{
_ = sb.Append(queryObj["Product"]);
}
byte[] bytes = Encoding.ASCII.GetBytes(sb.ToString());
SHA256Managed sha = new SHA256Managed();
byte[] hash = sha.ComputeHash(bytes);
uuid = BitConverter.ToString(hash).Replace("-", "");
return uuid;
}

58
Splash.Designer.cs generated
View File

@@ -1,58 +0,0 @@
namespace AndroidSideloader
{
partial class Splash
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Splash));
this.SuspendLayout();
//
// Splash
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("$this.BackgroundImage")));
this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
this.CausesValidation = false;
this.ClientSize = new System.Drawing.Size(427, 250);
this.ControlBox = false;
this.DoubleBuffered = true;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.Margin = new System.Windows.Forms.Padding(2);
this.Name = "Splash";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Splash";
this.ResumeLayout(false);
}
#endregion
}
}

View File

@@ -1,12 +0,0 @@
using System.Windows.Forms;
namespace AndroidSideloader
{
public partial class Splash : Form
{
public Splash()
{
InitializeComponent();
}
}
}

View File

@@ -1,413 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
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
</value>
</data>
</root>

127
SpoofForm.Designer.cs generated
View File

@@ -1,127 +0,0 @@
namespace AndroidSideloader
{
partial class SpoofForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.PackageNameTextBox = new System.Windows.Forms.TextBox();
this.progressBar1 = new System.Windows.Forms.ProgressBar();
this.SpoofButton = new AndroidSideloader.RoundButton();
this.RandomizeButton = new AndroidSideloader.RoundButton();
this.SuspendLayout();
//
// PackageNameTextBox
//
this.PackageNameTextBox.BackColor = global::AndroidSideloader.Properties.Settings.Default.TextBoxColor;
this.PackageNameTextBox.DataBindings.Add(new System.Windows.Forms.Binding("ForeColor", global::AndroidSideloader.Properties.Settings.Default, "FontColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.PackageNameTextBox.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "TextBoxColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.PackageNameTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Font", global::AndroidSideloader.Properties.Settings.Default, "FontStyle", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.PackageNameTextBox.Font = global::AndroidSideloader.Properties.Settings.Default.FontStyle;
this.PackageNameTextBox.ForeColor = global::AndroidSideloader.Properties.Settings.Default.FontColor;
this.PackageNameTextBox.Location = new System.Drawing.Point(13, 13);
this.PackageNameTextBox.Name = "PackageNameTextBox";
this.PackageNameTextBox.Size = new System.Drawing.Size(273, 24);
this.PackageNameTextBox.TabIndex = 1;
//
// progressBar1
//
this.progressBar1.Location = new System.Drawing.Point(13, 43);
this.progressBar1.Name = "progressBar1";
this.progressBar1.Size = new System.Drawing.Size(273, 23);
this.progressBar1.TabIndex = 3;
//
// SpoofButton
//
this.SpoofButton.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
this.SpoofButton.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
this.SpoofButton.BackColor = System.Drawing.Color.Transparent;
this.SpoofButton.DialogResult = System.Windows.Forms.DialogResult.OK;
this.SpoofButton.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F);
this.SpoofButton.ForeColor = System.Drawing.Color.White;
this.SpoofButton.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
this.SpoofButton.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
this.SpoofButton.Location = new System.Drawing.Point(176, 72);
this.SpoofButton.Name = "SpoofButton";
this.SpoofButton.Radius = 5;
this.SpoofButton.Size = new System.Drawing.Size(110, 42);
this.SpoofButton.Stroke = true;
this.SpoofButton.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(74)))), ((int)(((byte)(74)))), ((int)(((byte)(74)))));
this.SpoofButton.TabIndex = 5;
this.SpoofButton.Text = "Spoof!";
this.SpoofButton.Transparency = false;
this.SpoofButton.Click += new System.EventHandler(this.SpoofButton_Click);
//
// RandomizeButton
//
this.RandomizeButton.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
this.RandomizeButton.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
this.RandomizeButton.BackColor = System.Drawing.Color.Transparent;
this.RandomizeButton.DialogResult = System.Windows.Forms.DialogResult.OK;
this.RandomizeButton.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F);
this.RandomizeButton.ForeColor = System.Drawing.Color.White;
this.RandomizeButton.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
this.RandomizeButton.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
this.RandomizeButton.Location = new System.Drawing.Point(12, 72);
this.RandomizeButton.Name = "RandomizeButton";
this.RandomizeButton.Radius = 5;
this.RandomizeButton.Size = new System.Drawing.Size(110, 42);
this.RandomizeButton.Stroke = true;
this.RandomizeButton.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(74)))), ((int)(((byte)(74)))), ((int)(((byte)(74)))));
this.RandomizeButton.TabIndex = 6;
this.RandomizeButton.Text = "Randomize";
this.RandomizeButton.Transparency = false;
this.RandomizeButton.Click += new System.EventHandler(this.RandomizeButton_Click);
//
// SpoofForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = global::AndroidSideloader.Properties.Settings.Default.BackColor;
this.BackgroundImage = global::AndroidSideloader.Properties.Resources.pattern_cubes_1_1_1_0_0_0_1__000000_212121;
this.ClientSize = new System.Drawing.Size(300, 131);
this.Controls.Add(this.RandomizeButton);
this.Controls.Add(this.SpoofButton);
this.Controls.Add(this.progressBar1);
this.Controls.Add(this.PackageNameTextBox);
this.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "BackColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.MaximumSize = new System.Drawing.Size(316, 170);
this.MinimumSize = new System.Drawing.Size(316, 170);
this.Name = "SpoofForm";
this.Text = "SpoofForm";
this.Load += new System.EventHandler(this.SpoofForm_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox PackageNameTextBox;
private System.Windows.Forms.ProgressBar progressBar1;
private RoundButton SpoofButton;
private RoundButton RandomizeButton;
}
}

View File

@@ -1,79 +0,0 @@
using JR.Utils.GUI.Forms;
using Spoofer;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AndroidSideloader
{
public partial class SpoofForm : Form
{
public SpoofForm()
{
InitializeComponent();
}
private async void SpoofButton_Click(object sender, EventArgs e)
{
if (!spoofer.HasDependencies())
{
_ = MessageBox.Show("You are missing the dependencies... Cannot spoof games");
return;
}
string NewPackageName = PackageNameTextBox.Text;
string path;
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
openFileDialog.Filter = "Android apps (*.apk)|*.apk";
openFileDialog.FilterIndex = 2;
openFileDialog.RestoreDirectory = true;
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
path = openFileDialog.FileName;
}
else
{
return;
}
}
progressBar1.Style = ProgressBarStyle.Marquee;
string output = "";
//Spawn spoofer in a new thread so the ui isn't blocked
Thread t1 = new Thread(() =>
{
spoofer.Init();
output += spoofer.SpoofApk(path, NewPackageName);
})
{
IsBackground = true
};
t1.Start();
while (t1.IsAlive)
{
await Task.Delay(100);
}
progressBar1.Style = ProgressBarStyle.Continuous;
_ = output.Contains("is not recognized as an internal or external command")
? FlexibleMessageBox.Show(Program.form, Sideloader.SpooferWarning)
: FlexibleMessageBox.Show(Program.form, $"App spoofed from {spoofer.originalPackageName} to {NewPackageName}");
}
private void SpoofForm_Load(object sender, EventArgs e)
{
PackageNameTextBox.Text = Utilities.GeneralUtilities.RandomPackageName();
}
private void RandomizeButton_Click(object sender, EventArgs e)
{
PackageNameTextBox.Text = Utilities.GeneralUtilities.RandomPackageName();
}
}
}

View File

@@ -1,120 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

264
ToggleSwitch.cs Normal file
View File

@@ -0,0 +1,264 @@
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace AndroidSideloader
{
/// <summary>
/// An iOS-style toggle switch control with smooth animation.
/// </summary>
public class ToggleSwitch : Control
{
private bool _checked;
private bool _isHovered;
private float _animationProgress; // 0 = off, 1 = on
private Timer _animationTimer;
private const int AnimationDuration = 80; // ms
private const int AnimationInterval = 8; // ~120fps
private float _animationStep;
// Colors
private Color _onColor = Color.FromArgb(93, 203, 173);
private Color _offColor = Color.FromArgb(60, 65, 75);
private Color _thumbColor = Color.White;
private Color _onHoverColor = Color.FromArgb(110, 215, 190);
private Color _offHoverColor = Color.FromArgb(75, 80, 90);
public event EventHandler CheckedChanged;
public ToggleSwitch()
{
SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.ResizeRedraw |
ControlStyles.SupportsTransparentBackColor |
ControlStyles.StandardClick |
ControlStyles.StandardDoubleClick, true);
// Disable double-click so rapid clicks are treated as separate clicks
SetStyle(ControlStyles.StandardDoubleClick, false);
Size = new Size(44, 24);
Cursor = Cursors.Hand;
BackColor = Color.Transparent;
_animationTimer = new Timer { Interval = AnimationInterval };
_animationTimer.Tick += AnimationTimer_Tick;
_animationStep = (float)AnimationInterval / AnimationDuration;
}
[Category("Appearance")]
[Description("Gets or sets whether the toggle is in the 'on' state.")]
[DefaultValue(false)]
public bool Checked
{
get => _checked;
set
{
if (_checked != value)
{
_checked = value;
StartAnimation();
OnCheckedChanged(EventArgs.Empty);
}
}
}
/// <summary>
/// Sets the checked state without triggering animation or events.
/// Using this for initial state setup.
/// </summary>
public void SetCheckedSilent(bool value)
{
_checked = value;
_animationProgress = value ? 1f : 0f;
_animationTimer.Stop();
Invalidate();
}
[Category("Appearance")]
[Description("The color of the toggle when it is on.")]
public Color OnColor
{
get => _onColor;
set { _onColor = value; Invalidate(); }
}
[Category("Appearance")]
[Description("The color of the toggle when it is off.")]
public Color OffColor
{
get => _offColor;
set { _offColor = value; Invalidate(); }
}
[Category("Appearance")]
[Description("The color of the thumb (circle).")]
public Color ThumbColor
{
get => _thumbColor;
set { _thumbColor = value; Invalidate(); }
}
protected virtual void OnCheckedChanged(EventArgs e)
{
CheckedChanged?.Invoke(this, e);
}
private void StartAnimation()
{
if (!_animationTimer.Enabled)
{
_animationTimer.Start();
}
}
private void AnimationTimer_Tick(object sender, EventArgs e)
{
float target = _checked ? 1f : 0f;
if (_animationProgress < target)
{
_animationProgress += _animationStep;
if (_animationProgress >= target)
{
_animationProgress = target;
_animationTimer.Stop();
}
}
else if (_animationProgress > target)
{
_animationProgress -= _animationStep;
if (_animationProgress <= target)
{
_animationProgress = target;
_animationTimer.Stop();
}
}
else
{
_animationTimer.Stop();
}
Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
int width = Width;
int height = Height;
int padding = 2;
int thumbDiameter = height - (padding * 2);
int trackRadius = height / 2;
Color trackColor;
if (_isHovered)
{
trackColor = InterpolateColor(_offHoverColor, _onHoverColor, _animationProgress);
}
else
{
trackColor = InterpolateColor(_offColor, _onColor, _animationProgress);
}
Rectangle trackRect = new Rectangle(0, 0, width, height);
using (GraphicsPath trackPath = CreateRoundedRectPath(trackRect, trackRadius))
using (SolidBrush trackBrush = new SolidBrush(trackColor))
{
g.FillPath(trackBrush, trackPath);
}
int thumbMinX = padding;
int thumbMaxX = width - thumbDiameter - padding;
float easedProgress = EaseOutQuad(_animationProgress);
int thumbX = (int)(thumbMinX + (thumbMaxX - thumbMinX) * easedProgress);
int thumbY = padding;
Rectangle shadowRect = new Rectangle(thumbX + 1, thumbY + 1, thumbDiameter, thumbDiameter);
using (SolidBrush shadowBrush = new SolidBrush(Color.FromArgb(40, 0, 0, 0)))
{
g.FillEllipse(shadowBrush, shadowRect);
}
Rectangle thumbRect = new Rectangle(thumbX, thumbY, thumbDiameter, thumbDiameter);
using (SolidBrush thumbBrush = new SolidBrush(_thumbColor))
{
g.FillEllipse(thumbBrush, thumbRect);
}
}
private float EaseOutQuad(float t)
{
return t * (2 - t);
}
private Color InterpolateColor(Color from, Color to, float progress)
{
int r = (int)(from.R + (to.R - from.R) * progress);
int g = (int)(from.G + (to.G - from.G) * progress);
int b = (int)(from.B + (to.B - from.B) * progress);
int a = (int)(from.A + (to.A - from.A) * progress);
return Color.FromArgb(a, r, g, b);
}
private GraphicsPath CreateRoundedRectPath(Rectangle rect, int radius)
{
GraphicsPath path = new GraphicsPath();
int diameter = radius * 2;
path.AddArc(rect.X, rect.Y, diameter, diameter, 180, 90);
path.AddArc(rect.Right - diameter, rect.Y, diameter, diameter, 270, 90);
path.AddArc(rect.Right - diameter, rect.Bottom - diameter, diameter, diameter, 0, 90);
path.AddArc(rect.X, rect.Bottom - diameter, diameter, diameter, 90, 90);
path.CloseFigure();
return path;
}
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
_isHovered = true;
Invalidate();
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
_isHovered = false;
Invalidate();
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (e.Button == MouseButtons.Left)
{
// Toggle immediately on mouse down for responsive feel
_checked = !_checked;
StartAnimation();
OnCheckedChanged(EventArgs.Empty);
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_animationTimer?.Stop();
_animationTimer?.Dispose();
}
base.Dispose(disposing);
}
}
}

120
UpdateForm.Designer.cs generated
View File

@@ -1,5 +1,4 @@

namespace AndroidSideloader
namespace AndroidSideloader
{
partial class UpdateForm
{
@@ -30,57 +29,85 @@ namespace AndroidSideloader
private void InitializeComponent()
{
this.panel1 = new System.Windows.Forms.Panel();
this.YesUpdate = new AndroidSideloader.RoundButton();
this.panel3 = new System.Windows.Forms.Panel();
this.UpdateTextBox = new System.Windows.Forms.RichTextBox();
this.UpdateVerLabel = new System.Windows.Forms.Label();
this.CurVerLabel = new System.Windows.Forms.Label();
this.SkipUpdate = new System.Windows.Forms.Label();
this.YesUpdate = new AndroidSideloader.RoundButton();
this.panel1.SuspendLayout();
this.panel3.SuspendLayout();
this.SuspendLayout();
//
// panel1
//
this.panel1.BackColor = global::AndroidSideloader.Properties.Settings.Default.BackColor;
this.panel1.BackgroundImage = global::AndroidSideloader.Properties.Resources.pattern_cubes_1_1_1_0_0_0_1__000000_212121;
this.panel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(24)))), ((int)(((byte)(29)))));
this.panel1.Controls.Add(this.YesUpdate);
this.panel1.Controls.Add(this.panel3);
this.panel1.Controls.Add(this.UpdateVerLabel);
this.panel1.Controls.Add(this.CurVerLabel);
this.panel1.Controls.Add(this.SkipUpdate);
this.panel1.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "BackColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.panel1.Location = new System.Drawing.Point(-6, -6);
this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.panel1.Location = new System.Drawing.Point(0, 0);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(474, 305);
this.panel1.Padding = new System.Windows.Forms.Padding(20, 50, 20, 20);
this.panel1.Size = new System.Drawing.Size(480, 320);
this.panel1.TabIndex = 5;
this.panel1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.UpdateForm_MouseDown);
this.panel1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.UpdateForm_MouseMove);
this.panel1.MouseUp += new System.Windows.Forms.MouseEventHandler(this.UpdateForm_MouseUp);
//
// YesUpdate
//
this.YesUpdate.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(113)))), ((int)(((byte)(223)))), ((int)(((byte)(193)))));
this.YesUpdate.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
this.YesUpdate.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.YesUpdate.BackColor = System.Drawing.Color.Transparent;
this.YesUpdate.DialogResult = System.Windows.Forms.DialogResult.OK;
this.YesUpdate.Disabled1 = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
this.YesUpdate.Disabled2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(28)))), ((int)(((byte)(35)))));
this.YesUpdate.DisabledStrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
this.YesUpdate.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Bold);
this.YesUpdate.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
this.YesUpdate.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
this.YesUpdate.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(73)))), ((int)(((byte)(183)))), ((int)(((byte)(153)))));
this.YesUpdate.Location = new System.Drawing.Point(340, 259);
this.YesUpdate.Name = "YesUpdate";
this.YesUpdate.Radius = 6;
this.YesUpdate.Size = new System.Drawing.Size(120, 36);
this.YesUpdate.Stroke = false;
this.YesUpdate.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(74)))), ((int)(((byte)(74)))), ((int)(((byte)(74)))));
this.YesUpdate.TabIndex = 2;
this.YesUpdate.Text = "Update Now";
this.YesUpdate.Transparency = false;
this.YesUpdate.Click += new System.EventHandler(this.YesUpdate_Click);
//
// panel3
//
this.panel3.BackColor = global::AndroidSideloader.Properties.Settings.Default.SubButtonColor;
this.panel3.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panel3.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(28)))), ((int)(((byte)(32)))), ((int)(((byte)(38)))));
this.panel3.Controls.Add(this.UpdateTextBox);
this.panel3.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "SubButtonColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.panel3.Location = new System.Drawing.Point(21, 19);
this.panel3.Location = new System.Drawing.Point(20, 50);
this.panel3.Name = "panel3";
this.panel3.Size = new System.Drawing.Size(432, 218);
this.panel3.Padding = new System.Windows.Forms.Padding(12, 10, 12, 10);
this.panel3.Size = new System.Drawing.Size(440, 200);
this.panel3.TabIndex = 0;
//
// UpdateTextBox
//
this.UpdateTextBox.BackColor = global::AndroidSideloader.Properties.Settings.Default.ComboBoxColor;
this.UpdateTextBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(28)))), ((int)(((byte)(32)))), ((int)(((byte)(38)))));
this.UpdateTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.UpdateTextBox.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "ComboBoxColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.UpdateTextBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.25F);
this.UpdateTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.UpdateTextBox.Font = new System.Drawing.Font("Segoe UI", 9.5F);
this.UpdateTextBox.ForeColor = System.Drawing.Color.White;
this.UpdateTextBox.Location = new System.Drawing.Point(12, 8);
this.UpdateTextBox.Location = new System.Drawing.Point(12, 10);
this.UpdateTextBox.Margin = new System.Windows.Forms.Padding(6);
this.UpdateTextBox.Name = "UpdateTextBox";
this.UpdateTextBox.ReadOnly = true;
this.UpdateTextBox.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.Vertical;
this.UpdateTextBox.Size = new System.Drawing.Size(408, 200);
this.UpdateTextBox.Size = new System.Drawing.Size(416, 180);
this.UpdateTextBox.TabIndex = 1;
this.UpdateTextBox.Text = "";
this.UpdateTextBox.TextChanged += new System.EventHandler(this.UpdateTextBox_TextChanged);
@@ -90,73 +117,54 @@ namespace AndroidSideloader
//
// UpdateVerLabel
//
this.UpdateVerLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.UpdateVerLabel.AutoSize = true;
this.UpdateVerLabel.BackColor = System.Drawing.Color.Transparent;
this.UpdateVerLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F);
this.UpdateVerLabel.ForeColor = System.Drawing.SystemColors.Control;
this.UpdateVerLabel.Location = new System.Drawing.Point(21, 261);
this.UpdateVerLabel.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold);
this.UpdateVerLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
this.UpdateVerLabel.Location = new System.Drawing.Point(20, 285);
this.UpdateVerLabel.Name = "UpdateVerLabel";
this.UpdateVerLabel.Size = new System.Drawing.Size(94, 15);
this.UpdateVerLabel.Size = new System.Drawing.Size(95, 15);
this.UpdateVerLabel.TabIndex = 3;
this.UpdateVerLabel.Text = "Update Version:";
//
// CurVerLabel
//
this.CurVerLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.CurVerLabel.AutoSize = true;
this.CurVerLabel.BackColor = System.Drawing.Color.Transparent;
this.CurVerLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F);
this.CurVerLabel.ForeColor = System.Drawing.SystemColors.Control;
this.CurVerLabel.Location = new System.Drawing.Point(21, 245);
this.CurVerLabel.Font = new System.Drawing.Font("Segoe UI", 9F);
this.CurVerLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(160)))), ((int)(((byte)(165)))), ((int)(((byte)(175)))));
this.CurVerLabel.Location = new System.Drawing.Point(20, 266);
this.CurVerLabel.Name = "CurVerLabel";
this.CurVerLabel.Size = new System.Drawing.Size(94, 15);
this.CurVerLabel.Size = new System.Drawing.Size(91, 15);
this.CurVerLabel.TabIndex = 2;
this.CurVerLabel.Text = "Current Version:";
//
// SkipUpdate
//
this.SkipUpdate.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.SkipUpdate.AutoSize = true;
this.SkipUpdate.BackColor = System.Drawing.Color.Transparent;
this.SkipUpdate.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.SkipUpdate.Font = new System.Drawing.Font("Microsoft Sans Serif", 8F);
this.SkipUpdate.ForeColor = System.Drawing.Color.Silver;
this.SkipUpdate.Location = new System.Drawing.Point(374, 279);
this.SkipUpdate.Cursor = System.Windows.Forms.Cursors.Hand;
this.SkipUpdate.Font = new System.Drawing.Font("Segoe UI", 8.5F);
this.SkipUpdate.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(160)))), ((int)(((byte)(165)))), ((int)(((byte)(175)))));
this.SkipUpdate.Location = new System.Drawing.Point(380, 297);
this.SkipUpdate.Name = "SkipUpdate";
this.SkipUpdate.Size = new System.Drawing.Size(76, 13);
this.SkipUpdate.Size = new System.Drawing.Size(73, 15);
this.SkipUpdate.TabIndex = 4;
this.SkipUpdate.Text = "𝖲𝖪𝖨𝖯 𝖥𝖮𝖱 𝖭𝖮𝖶";
this.SkipUpdate.Text = "Skip for now";
this.SkipUpdate.Click += new System.EventHandler(this.SkipUpdate_Click);
//
// YesUpdate
//
this.YesUpdate.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
this.YesUpdate.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
this.YesUpdate.BackColor = System.Drawing.Color.Transparent;
this.YesUpdate.DialogResult = System.Windows.Forms.DialogResult.OK;
this.YesUpdate.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F);
this.YesUpdate.ForeColor = System.Drawing.Color.White;
this.YesUpdate.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
this.YesUpdate.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
this.YesUpdate.Location = new System.Drawing.Point(339, 245);
this.YesUpdate.Name = "YesUpdate";
this.YesUpdate.Radius = 5;
this.YesUpdate.Size = new System.Drawing.Size(111, 31);
this.YesUpdate.Stroke = true;
this.YesUpdate.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(74)))), ((int)(((byte)(74)))), ((int)(((byte)(74)))));
this.YesUpdate.TabIndex = 2;
this.YesUpdate.Text = "Update Now";
this.YesUpdate.Transparency = false;
this.YesUpdate.Click += new System.EventHandler(this.YesUpdate_Click);
//
// UpdateForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoValidate = System.Windows.Forms.AutoValidate.EnablePreventFocusChange;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(29)))), ((int)(((byte)(29)))), ((int)(((byte)(29)))));
this.ClientSize = new System.Drawing.Size(462, 291);
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(24)))), ((int)(((byte)(29)))));
this.ClientSize = new System.Drawing.Size(480, 320);
this.ControlBox = false;
this.Controls.Add(this.panel1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.Name = "UpdateForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.UpdateForm_MouseDown);

View File

@@ -1,5 +1,6 @@
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace AndroidSideloader
@@ -9,15 +10,207 @@ namespace AndroidSideloader
private bool mouseDown;
private Point lastLocation;
// Modern theme colors
private static readonly Color BackgroundColor = Color.FromArgb(20, 24, 29);
private static readonly Color PanelColor = Color.FromArgb(28, 32, 38);
private static readonly Color TextColor = Color.White;
private static readonly Color SecondaryTextColor = Color.FromArgb(160, 165, 175);
private static readonly Color BorderColor = Color.FromArgb(60, 65, 75);
public UpdateForm()
{
InitializeComponent();
// Use same icon as the executable
this.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
ApplyModernTheme();
CenterToScreen();
CurVerLabel.Text += " " + Updater.LocalVersion;
UpdateVerLabel.Text += " " + Updater.currentVersion;
CurVerLabel.Text = $"Current Version: {Updater.LocalVersion}";
UpdateVerLabel.Text = $"Update Version: {Updater.currentVersion}";
UpdateTextBox.Text = Updater.changelog;
}
private void ApplyModernTheme()
{
// Form settings
this.FormBorderStyle = FormBorderStyle.None;
this.BackColor = BackgroundColor;
this.DoubleBuffered = true;
// Enable double buffering on panels for smooth rounded corners
EnableDoubleBuffering(panel1);
EnableDoubleBuffering(panel3);
// Add custom paint handler for rounded panel1 (main container)
panel1.Paint += Panel1_Paint;
panel1.BackColor = Color.Transparent;
// Add custom paint handler for rounded panel3 (changelog container)
panel3.Paint += Panel3_Paint;
panel3.BackColor = Color.Transparent;
// Update textbox to have matching background
UpdateTextBox.BackColor = PanelColor;
// Add title label
var titleLabel = new Label
{
Text = "Update Available",
Font = new Font("Segoe UI", 12F, FontStyle.Bold),
ForeColor = TextColor,
BackColor = Color.Transparent,
AutoSize = true,
Location = new Point(20, 15)
};
panel1.Controls.Add(titleLabel);
titleLabel.BringToFront();
// Add close button
var closeButton = new Label
{
Text = "✕",
Font = new Font("Segoe UI", 10F),
ForeColor = SecondaryTextColor,
BackColor = Color.Transparent,
AutoSize = true,
Cursor = Cursors.Hand,
Location = new Point(this.ClientSize.Width - 30, 10),
Anchor = AnchorStyles.Top | AnchorStyles.Right
};
closeButton.Click += (s, e) => Close();
closeButton.MouseEnter += (s, e) => closeButton.ForeColor = Color.FromArgb(220, 80, 80);
closeButton.MouseLeave += (s, e) => closeButton.ForeColor = SecondaryTextColor;
panel1.Controls.Add(closeButton);
closeButton.BringToFront();
// Apply custom painting for form rounded corners and border
this.Paint += UpdateForm_Paint;
}
private void EnableDoubleBuffering(Panel panel)
{
typeof(Panel).InvokeMember("DoubleBuffered",
System.Reflection.BindingFlags.SetProperty | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic,
null, panel, new object[] { true });
}
private void Panel1_Paint(object sender, PaintEventArgs e)
{
var panel = sender as Panel;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
int radius = 12;
var rect = new Rectangle(0, 0, panel.Width - 1, panel.Height - 1);
using (var path = CreateRoundedRectPath(rect, radius))
{
// Fill background
using (var brush = new SolidBrush(BackgroundColor))
{
e.Graphics.FillPath(brush, path);
}
// Draw border
using (var pen = new Pen(BorderColor, 1f))
{
e.Graphics.DrawPath(pen, path);
}
}
// Apply rounded region to clip children
using (var regionPath = CreateRoundedRectPath(new Rectangle(0, 0, panel.Width, panel.Height), radius))
{
panel.Region = new Region(regionPath);
}
}
private void Panel3_Paint(object sender, PaintEventArgs e)
{
var panel = sender as Panel;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
int radius = 10;
var rect = new Rectangle(0, 0, panel.Width - 1, panel.Height - 1);
using (var path = CreateRoundedRectPath(rect, radius))
{
// Fill background
using (var brush = new SolidBrush(PanelColor))
{
e.Graphics.FillPath(brush, path);
}
// Draw border
using (var pen = new Pen(BorderColor, 1f))
{
e.Graphics.DrawPath(pen, path);
}
}
// Apply rounded region to clip children
using (var regionPath = CreateRoundedRectPath(new Rectangle(0, 0, panel.Width, panel.Height), radius))
{
panel.Region = new Region(regionPath);
}
}
private void UpdateForm_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
int w = this.ClientSize.Width;
int h = this.ClientSize.Height;
int radius = 12;
// Draw border
using (var borderPen = new Pen(BorderColor, 1f))
using (var path = CreateRoundedRectPath(new Rectangle(0, 0, w - 1, h - 1), radius))
{
e.Graphics.DrawPath(borderPen, path);
}
// Apply rounded region
using (var regionPath = CreateRoundedRectPath(new Rectangle(0, 0, w, h), radius))
{
this.Region = new Region(regionPath);
}
}
private GraphicsPath CreateRoundedRectPath(Rectangle rect, int radius)
{
GraphicsPath path = new GraphicsPath();
if (radius <= 0)
{
path.AddRectangle(rect);
return path;
}
int diameter = radius * 2;
diameter = Math.Min(diameter, Math.Min(rect.Width, rect.Height));
radius = diameter / 2;
Rectangle arcRect = new Rectangle(rect.Location, new Size(diameter, diameter));
// Top left arc
path.AddArc(arcRect, 180, 90);
// Top right arc
arcRect.X = rect.Right - diameter;
path.AddArc(arcRect, 270, 90);
// Bottom right arc
arcRect.Y = rect.Bottom - diameter;
path.AddArc(arcRect, 0, 90);
// Bottom left arc
arcRect.X = rect.Left;
path.AddArc(arcRect, 90, 90);
path.CloseFigure();
return path;
}
private void YesUpdate_Click(object sender, EventArgs e)
{
Updater.doUpdate();
@@ -31,7 +224,6 @@ namespace AndroidSideloader
private void UpdateTextBox_TextChanged(object sender, EventArgs e)
{
}
private void UpdateForm_MouseDown(object sender, MouseEventArgs e)
@@ -46,7 +238,6 @@ namespace AndroidSideloader
{
Location = new Point(
Location.X - lastLocation.X + e.X, Location.Y - lastLocation.Y + e.Y);
Update();
}
}
@@ -56,4 +247,4 @@ namespace AndroidSideloader
mouseDown = false;
}
}
}
}

View File

@@ -1,67 +1,110 @@
using System.Diagnostics;
using System;
using System.Diagnostics;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace AndroidSideloader
{
internal class Updater
{
public static string AppName { get; set; }
public static string Repostory { get; set; }
private static string RawGitHubUrl;
private static string GitHubUrl;
public static string Repository { get; set; }
private static readonly string RawGitHubUrl = "https://raw.githubusercontent.com/VRPirates/rookie";
public static readonly string GitHubUrl = "https://github.com/VRPirates/rookie";
public static readonly string LocalVersion = "2.18";
public static readonly string LocalVersion = "3.0.1";
public static string currentVersion = string.Empty;
public static string changelog = string.Empty;
//Check if there is a new version of the sideloader
private static bool IsUpdateAvailable()
// Check if there is a new version of the sideloader
private static async Task<bool> IsUpdateAvailableAsync()
{
using (HttpClient client = new HttpClient())
{
try
{
currentVersion = await client.GetStringAsync($"{RawGitHubUrl}/master/version");
changelog = await client.GetStringAsync($"{RawGitHubUrl}/master/changelog.txt");
currentVersion = currentVersion.Trim();
}
catch (HttpRequestException)
{
return false;
}
}
// Compare versions - only return true if server version is greater than local version
return CompareVersions(currentVersion, LocalVersion.Trim()) > 0;
}
// Compares two semantic version strings (e.g., "2.35")
// returns: 1 if version1 > version2, -1 if version1 < version2, 0 if equal
private static int CompareVersions(string version1, string version2)
{
HttpClient client = new HttpClient();
try
{
currentVersion = client.GetStringAsync($"{RawGitHubUrl}/master/version").Result;
changelog = client.GetStringAsync($"{RawGitHubUrl}/master/changelog.txt").Result;
client.Dispose();
currentVersion = currentVersion.Trim();
// Parse versions into parts
string[] parts1 = version1.Split('.');
string[] parts2 = version2.Split('.');
// Compare each part
int maxLength = Math.Max(parts1.Length, parts2.Length);
for (int i = 0; i < maxLength; i++)
{
int v1 = i < parts1.Length && int.TryParse(parts1[i], out int p1) ? p1 : 0;
int v2 = i < parts2.Length && int.TryParse(parts2[i], out int p2) ? p2 : 0;
if (v1 > v2) return 1;
if (v1 < v2) return -1;
}
return 0; // Versions are equal
}
catch
{
// Fallback to string comparison if parsing fails
return string.Compare(version1, version2, StringComparison.Ordinal);
}
catch { return false; }
return LocalVersion.Trim() != currentVersion;
}
//Call this to ask the user if they want to update
public static void Update()
// Ask the user if they want to update
public static async Task Update()
{
RawGitHubUrl = $"https://raw.githubusercontent.com/nerdunit/androidsideloader";
GitHubUrl = $"https://github.com/nerdunit/androidsideloader";
if (IsUpdateAvailable())
if (await IsUpdateAvailableAsync())
{
UpdateForm upForm = new UpdateForm();
_ = upForm.ShowDialog(); ;
_ = upForm.ShowDialog();
}
}
//If the user wants to update
// If the user wants to update
public static void doUpdate()
{
try
{
_ = ADB.RunAdbCommandToString("kill-server");
WebClient fileClient = new WebClient();
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
_ = Logger.Log($"Downloading update from {GitHubUrl}/releases/download/v{currentVersion}/{AppName}.exe to {AppName} v{currentVersion}.exe");
fileClient.DownloadFile($"{GitHubUrl}/releases/download/v{currentVersion}/{AppName}.exe", $"{AppName} v{currentVersion}.exe");
fileClient.Dispose();
_ = Logger.Log($"Starting {AppName} v{currentVersion}.exe");
_ = Process.Start($"{AppName} v{currentVersion}.exe");
//Delete current version
ADB.RunAdbCommandToString("kill-server");
using (WebClient fileClient = new WebClient())
{
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
Logger.Log($"Downloading update from {GitHubUrl}/releases/download/v{currentVersion}/{AppName}.exe to {AppName} v{currentVersion}.exe");
fileClient.DownloadFile($"{GitHubUrl}/releases/download/v{currentVersion}/{AppName}.exe", $"{AppName} v{currentVersion}.exe");
Logger.Log($"Starting {AppName} v{currentVersion}.exe");
Process.Start($"{AppName} v{currentVersion}.exe");
}
// Delete current version
AndroidSideloader.Utilities.GeneralUtilities.Melt();
}
catch { }
catch (Exception ex)
{
// Handle specific exceptions that might occur during the update process
Logger.Log($"Update failed: {ex.Message}");
}
}
}
}
}

190
UsernameForm.Designer.cs generated
View File

@@ -1,96 +1,94 @@
namespace AndroidSideloader
{
partial class UsernameForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.textBox1 = new System.Windows.Forms.TextBox();
this.button1 = new AndroidSideloader.RoundButton();
this.SuspendLayout();
//
// textBox1
//
this.textBox1.BackColor = global::AndroidSideloader.Properties.Settings.Default.TextBoxColor;
this.textBox1.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "TextBoxColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.textBox1.DataBindings.Add(new System.Windows.Forms.Binding("Font", global::AndroidSideloader.Properties.Settings.Default, "FontStyle", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.textBox1.Font = global::AndroidSideloader.Properties.Settings.Default.FontStyle;
this.textBox1.ForeColor = System.Drawing.Color.White;
this.textBox1.Location = new System.Drawing.Point(13, 13);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(418, 24);
this.textBox1.TabIndex = 0;
this.textBox1.Text = "Enter your username here";
//
// button1
//
this.button1.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(45)))), ((int)(((byte)(45)))));
this.button1.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(45)))), ((int)(((byte)(45)))));
this.button1.BackColor = System.Drawing.Color.Transparent;
this.button1.DialogResult = System.Windows.Forms.DialogResult.OK;
this.button1.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F);
this.button1.ForeColor = System.Drawing.Color.White;
this.button1.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
this.button1.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
this.button1.Location = new System.Drawing.Point(13, 51);
this.button1.Name = "button1";
this.button1.Radius = 5;
this.button1.Size = new System.Drawing.Size(418, 34);
this.button1.Stroke = true;
this.button1.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(74)))), ((int)(((byte)(74)))), ((int)(((byte)(74)))));
this.button1.TabIndex = 2;
this.button1.Text = "Create User.Json";
this.button1.Transparency = false;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// UsernameForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = global::AndroidSideloader.Properties.Settings.Default.BackColor;
this.BackgroundImage = global::AndroidSideloader.Properties.Resources.pattern_cubes_1_1_1_0_0_0_1__000000_212121;
this.ClientSize = new System.Drawing.Size(443, 100);
this.Controls.Add(this.button1);
this.Controls.Add(this.textBox1);
this.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "BackColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.ForeColor = System.Drawing.Color.White;
this.MaximumSize = new System.Drawing.Size(459, 139);
this.MinimumSize = new System.Drawing.Size(459, 139);
this.Name = "UsernameForm";
this.ShowIcon = false;
this.Text = "USER.JSON";
this.Load += new System.EventHandler(this.usernameForm_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox textBox1;
private RoundButton button1;
}
}
namespace AndroidSideloader
{
partial class UsernameForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.textBox1 = new System.Windows.Forms.TextBox();
this.button1 = new AndroidSideloader.RoundButton();
this.SuspendLayout();
//
// textBox1
//
this.textBox1.BackColor = global::AndroidSideloader.Properties.Settings.Default.TextBoxColor;
this.textBox1.Font = global::AndroidSideloader.Properties.Settings.Default.FontStyle;
this.textBox1.ForeColor = System.Drawing.Color.White;
this.textBox1.Location = new System.Drawing.Point(13, 13);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(418, 23);
this.textBox1.TabIndex = 0;
this.textBox1.Text = "Enter your username here";
//
// button1
//
this.button1.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(45)))), ((int)(((byte)(45)))));
this.button1.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(45)))), ((int)(((byte)(45)))));
this.button1.BackColor = System.Drawing.Color.Transparent;
this.button1.DialogResult = System.Windows.Forms.DialogResult.OK;
this.button1.Disabled1 = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
this.button1.Disabled2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(28)))), ((int)(((byte)(35)))));
this.button1.DisabledStrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
this.button1.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F);
this.button1.ForeColor = System.Drawing.Color.White;
this.button1.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
this.button1.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
this.button1.Location = new System.Drawing.Point(13, 51);
this.button1.Name = "button1";
this.button1.Radius = 5;
this.button1.Size = new System.Drawing.Size(418, 34);
this.button1.Stroke = true;
this.button1.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(74)))), ((int)(((byte)(74)))), ((int)(((byte)(74)))));
this.button1.TabIndex = 2;
this.button1.Text = "Create User.Json";
this.button1.Transparency = false;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// UsernameForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = global::AndroidSideloader.Properties.Settings.Default.BackColor;
this.ClientSize = new System.Drawing.Size(443, 100);
this.Controls.Add(this.button1);
this.Controls.Add(this.textBox1);
this.ForeColor = System.Drawing.Color.White;
this.MaximumSize = new System.Drawing.Size(459, 139);
this.MinimumSize = new System.Drawing.Size(459, 139);
this.Name = "UsernameForm";
this.Text = "USER.JSON";
this.Load += new System.EventHandler(this.usernameForm_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox textBox1;
private RoundButton button1;
}
}

View File

@@ -12,6 +12,9 @@ namespace AndroidSideloader
public UsernameForm()
{
InitializeComponent();
// Use same icon as the executable
this.Icon = System.Drawing.Icon.ExtractAssociatedIcon(Application.ExecutablePath);
}
private string defaultText;
@@ -53,7 +56,7 @@ namespace AndroidSideloader
public static void createUserJson(string username)
{
_ = ADB.RunAdbCommandToString($"shell settings put global username {username}");
_ = ADB.RunAdbCommandToString($"shell settings put global username \"{username}\"");
foreach (string jsonFileName in userJsons)
{
createUserJsonByName(username, jsonFileName);

554
Utilities/DnsHelper.cs Normal file
View File

@@ -0,0 +1,554 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace AndroidSideloader.Utilities
{
// Provides DNS fallback functionality using Cloudflare DNS (1.1.1.1, 1.0.0.1) if system DNS fails to resolve critical hostnames
// Also provides a proxy for rclone that handles DNS resolution
public static class DnsHelper
{
private static readonly string[] FallbackDnsServers = { "1.1.1.1", "1.0.0.1" };
private static readonly string[] CriticalHostnames =
{
"raw.githubusercontent.com",
"downloads.rclone.org",
"vrpirates.wiki",
"github.com"
};
private static readonly ConcurrentDictionary<string, IPAddress> _dnsCache =
new ConcurrentDictionary<string, IPAddress>(StringComparer.OrdinalIgnoreCase);
private static readonly object _lock = new object();
// Local proxy for rclone
private static TcpListener _proxyListener;
private static CancellationTokenSource _proxyCts;
private static int _proxyPort;
private static bool _initialized;
private static bool _proxyRunning;
public static bool UseFallbackDns { get; private set; }
// Gets the proxy URL for rclone to use, or empty string if not needed
public static string ProxyUrl => _proxyRunning ? $"http://127.0.0.1:{_proxyPort}" : string.Empty;
// Called after vrp-public.json is created/updated to test the hostname
// Enable fallback DNS if the hostname fails on system DNS but works with fallback DNS
public static void TestPublicConfigDns()
{
string hostname = GetPublicConfigHostname();
if (string.IsNullOrEmpty(hostname))
return;
lock (_lock)
{
// If already using fallback, just pre-resolve the new hostname
if (UseFallbackDns)
{
var ip = ResolveWithFallbackDns(hostname);
if (ip != null)
{
_dnsCache[hostname] = ip;
Logger.Log($"Pre-resolved public config hostname {hostname} -> {ip}");
}
return;
}
// Test if system DNS can resolve the public config hostname
bool systemDnsWorks = TestHostnameWithSystemDns(hostname);
if (!systemDnsWorks)
{
Logger.Log($"System DNS failed for {hostname}. Testing fallback...", LogLevel.WARNING);
// Test if fallback DNS works for this hostname
var ip = ResolveWithFallbackDns(hostname);
if (ip != null)
{
UseFallbackDns = true;
_dnsCache[hostname] = ip;
Logger.Log($"Enabled fallback DNS for {hostname} -> {ip}", LogLevel.INFO);
ServicePointManager.DnsRefreshTimeout = 0;
// Pre-resolve base hostnames too
PreResolveHostnames(CriticalHostnames);
// Start proxy if not already running
if (!_proxyRunning)
{
StartProxy();
}
}
else
{
Logger.Log($"Both system and fallback DNS failed for {hostname}", LogLevel.ERROR);
}
}
else
{
Logger.Log($"System DNS works for public config hostname: {hostname}");
}
}
}
private static string GetPublicConfigHostname()
{
try
{
string configPath = Path.Combine(Environment.CurrentDirectory, "vrp-public.json");
if (!File.Exists(configPath))
return null;
var config = JsonConvert.DeserializeObject<Dictionary<string, string>>(File.ReadAllText(configPath));
if (config != null && config.TryGetValue("baseUri", out string baseUri))
{
return ExtractHostname(baseUri);
}
}
catch (Exception ex)
{
Logger.Log($"Failed to get hostname from vrp-public.json: {ex.Message}", LogLevel.WARNING);
}
return null;
}
private static string[] GetCriticalHostnames()
{
var hostnames = new List<string>(CriticalHostnames);
string host = GetPublicConfigHostname();
if (!string.IsNullOrWhiteSpace(host) && !hostnames.Contains(host))
{
hostnames.Add(host);
Logger.Log($"Added {host} from vrp-public.json to critical hostnames");
}
return hostnames.ToArray();
}
private static string ExtractHostname(string uriString)
{
if (string.IsNullOrWhiteSpace(uriString)) return null;
if (!uriString.StartsWith("http://", StringComparison.OrdinalIgnoreCase) &&
!uriString.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
{
uriString = "https://" + uriString;
}
if (Uri.TryCreate(uriString, UriKind.Absolute, out Uri uri))
return uri.Host;
// Fallback: manual extraction
string hostname = uriString.Replace("https://", "").Replace("http://", "");
int idx = hostname.IndexOfAny(new[] { '/', ':' });
return idx > 0 ? hostname.Substring(0, idx) : hostname;
}
public static void Initialize()
{
lock (_lock)
{
if (_initialized) return;
Logger.Log("Testing DNS resolution for critical hostnames...");
var hostnames = GetCriticalHostnames();
if (TestDns(hostnames, useSystem: true))
{
Logger.Log("System DNS is working correctly.");
}
else
{
Logger.Log("System DNS failed. Testing Cloudflare DNS fallback...", LogLevel.WARNING);
if (TestDns(hostnames, useSystem: false))
{
UseFallbackDns = true;
Logger.Log("Using Cloudflare DNS fallback.", LogLevel.INFO);
PreResolveHostnames(hostnames);
ServicePointManager.DnsRefreshTimeout = 0;
StartProxy();
}
else
{
Logger.Log("Both system and fallback DNS failed.", LogLevel.ERROR);
}
}
_initialized = true;
}
}
public static void Cleanup() => StopProxy();
private static bool TestHostnameWithSystemDns(string hostname)
{
try
{
var addresses = Dns.GetHostAddresses(hostname);
return addresses?.Length > 0;
}
catch
{
return false;
}
}
private static bool TestDns(string[] hostnames, bool useSystem)
{
if (useSystem)
{
return hostnames.All(h =>
{
try { return Dns.GetHostAddresses(h)?.Length > 0; }
catch { return false; }
});
}
return FallbackDnsServers.Any(server =>
{
try { return ResolveWithDns(hostnames[0], server)?.Count > 0; }
catch { return false; }
});
}
private static void PreResolveHostnames(string[] hostnames)
{
foreach (string hostname in hostnames)
{
var ip = ResolveWithFallbackDns(hostname);
if (ip != null)
{
_dnsCache[hostname] = ip;
Logger.Log($"Pre-resolved {hostname} -> {ip}");
}
}
}
private static IPAddress ResolveWithFallbackDns(string hostname)
{
foreach (string server in FallbackDnsServers)
{
try
{
var addresses = ResolveWithDns(hostname, server);
if (addresses?.Count > 0) return addresses[0];
}
catch { }
}
return null;
}
private static List<IPAddress> ResolveWithDns(string hostname, string dnsServer, int timeoutMs = 5000)
{
using (var udp = new UdpClient { Client = { ReceiveTimeout = timeoutMs, SendTimeout = timeoutMs } })
{
byte[] query = BuildDnsQuery(hostname);
udp.Send(query, query.Length, new IPEndPoint(IPAddress.Parse(dnsServer), 53));
IPEndPoint remoteEp = null;
return ParseDnsResponse(udp.Receive(ref remoteEp));
}
}
private static byte[] BuildDnsQuery(string hostname)
{
using (var ms = new MemoryStream())
using (var writer = new BinaryWriter(ms))
{
writer.Write(IPAddress.HostToNetworkOrder((short)new Random().Next(0, ushort.MaxValue)));
writer.Write(IPAddress.HostToNetworkOrder((short)0x0100)); // Flags
writer.Write(IPAddress.HostToNetworkOrder((short)1)); // Questions
writer.Write(IPAddress.HostToNetworkOrder((short)0)); // Answer RRs
writer.Write(IPAddress.HostToNetworkOrder((short)0)); // Authority RRs
writer.Write(IPAddress.HostToNetworkOrder((short)0)); // Additional RRs
foreach (string label in hostname.Split('.'))
{
writer.Write((byte)label.Length);
writer.Write(Encoding.ASCII.GetBytes(label));
}
writer.Write((byte)0);
writer.Write(IPAddress.HostToNetworkOrder((short)1)); // Type A
writer.Write(IPAddress.HostToNetworkOrder((short)1)); // Class IN
return ms.ToArray();
}
}
private static List<IPAddress> ParseDnsResponse(byte[] response)
{
var addresses = new List<IPAddress>();
if (response.Length < 12) return addresses;
int pos = 12;
while (pos < response.Length && response[pos] != 0) pos += response[pos] + 1;
pos += 5;
int answerCount = (response[6] << 8) | response[7];
for (int i = 0; i < answerCount && pos + 12 <= response.Length; i++)
{
pos += (response[pos] & 0xC0) == 0xC0 ? 2 : SkipName(response, pos);
if (pos + 10 > response.Length) break;
ushort type = (ushort)((response[pos] << 8) | response[pos + 1]);
ushort rdLength = (ushort)((response[pos + 8] << 8) | response[pos + 9]);
pos += 10;
if (pos + rdLength > response.Length) break;
if (type == 1 && rdLength == 4)
addresses.Add(new IPAddress(new[] { response[pos], response[pos + 1], response[pos + 2], response[pos + 3] }));
pos += rdLength;
}
return addresses;
}
private static int SkipName(byte[] data, int pos)
{
int start = pos;
while (pos < data.Length && data[pos] != 0) pos += data[pos] + 1;
return pos - start + 1;
}
public static IPAddress ResolveHostname(string hostname, bool alwaysTryFallback = false)
{
if (_dnsCache.TryGetValue(hostname, out IPAddress cached))
return cached;
try
{
var addresses = Dns.GetHostAddresses(hostname);
if (addresses?.Length > 0)
{
_dnsCache[hostname] = addresses[0];
return addresses[0];
}
}
catch { }
if (alwaysTryFallback || UseFallbackDns || !_initialized)
{
var ip = ResolveWithFallbackDns(hostname);
if (ip != null)
{
_dnsCache[hostname] = ip;
return ip;
}
}
return null;
}
public static HttpWebRequest CreateWebRequest(string url)
{
var uri = new Uri(url);
if (!UseFallbackDns)
{
try
{
Dns.GetHostAddresses(uri.Host);
return (HttpWebRequest)WebRequest.Create(url);
}
catch
{
if (!_initialized) Initialize();
}
}
if (UseFallbackDns)
{
var ip = ResolveHostname(uri.Host, alwaysTryFallback: true);
if (ip != null)
{
var builder = new UriBuilder(uri) { Host = ip.ToString() };
var request = (HttpWebRequest)WebRequest.Create(builder.Uri);
request.Host = uri.Host;
return request;
}
}
return (HttpWebRequest)WebRequest.Create(url);
}
private static void StartProxy()
{
try
{
// Find an available port
_proxyListener = new TcpListener(IPAddress.Loopback, 0);
_proxyListener.Start();
_proxyPort = ((IPEndPoint)_proxyListener.LocalEndpoint).Port;
_proxyCts = new CancellationTokenSource();
_proxyRunning = true;
Logger.Log($"Started DNS proxy on port {_proxyPort}");
// Accept connections in background
Task.Run(() => ProxyAcceptLoop(_proxyCts.Token));
}
catch (Exception ex)
{
Logger.Log($"Failed to start DNS proxy: {ex.Message}", LogLevel.WARNING);
_proxyRunning = false;
}
}
private static void StopProxy()
{
_proxyRunning = false;
_proxyCts?.Cancel();
try { _proxyListener?.Stop(); } catch { }
}
private static async Task ProxyAcceptLoop(CancellationToken ct)
{
while (!ct.IsCancellationRequested && _proxyRunning)
{
try
{
var client = await _proxyListener.AcceptTcpClientAsync();
_ = HandleProxyClient(client, ct);
}
catch (ObjectDisposedException) { break; }
catch (Exception ex)
{
if (!ct.IsCancellationRequested)
Logger.Log($"Proxy accept error: {ex.Message}", LogLevel.WARNING);
}
}
}
private static async Task HandleProxyClient(TcpClient client, CancellationToken ct)
{
try
{
using (client)
using (var stream = client.GetStream())
{
client.ReceiveTimeout = client.SendTimeout = 30000;
var buffer = new byte[8192];
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, ct);
if (bytesRead == 0) return;
string request = Encoding.ASCII.GetString(buffer, 0, bytesRead);
string[] lines = request.Split(new[] { "\r\n" }, StringSplitOptions.None);
if (lines.Length == 0) return;
string[] requestLine = lines[0].Split(' ');
if (requestLine.Length < 2) return;
if (requestLine[0] == "CONNECT")
// HTTPS proxy - tunnel mode
await HandleConnectRequest(stream, requestLine[1], ct);
else
// HTTP proxy - forward mode
await HandleHttpRequest(stream, request, requestLine[1], ct);
}
}
catch (Exception ex)
{
if (!ct.IsCancellationRequested)
Logger.Log($"Proxy client error: {ex.Message}", LogLevel.WARNING);
}
}
private static async Task HandleConnectRequest(NetworkStream clientStream, string target, CancellationToken ct)
{
// Parse host:port
string[] parts = target.Split(':');
string host = parts[0];
int port = parts.Length > 1 && int.TryParse(parts[1], out int p) ? p : 443;
// Resolve hostname
IPAddress ip = ResolveHostname(host, alwaysTryFallback: true);
if (ip == null)
{
await SendResponse(clientStream, "HTTP/1.1 502 Bad Gateway\r\n\r\n", ct);
return;
}
try
{
// Connect to target
using (var targetClient = new TcpClient())
{
await targetClient.ConnectAsync(ip, port);
using (var targetStream = targetClient.GetStream())
{
// Send 200 OK to client
await SendResponse(clientStream, "HTTP/1.1 200 Connection Established\r\n\r\n", ct);
// Tunnel data bidirectionally
await Task.WhenAny(RelayData(clientStream, targetStream, ct), RelayData(targetStream, clientStream, ct));
}
}
}
catch (Exception ex)
{
Logger.Log($"CONNECT tunnel error to {host}: {ex.Message}", LogLevel.WARNING);
await SendResponse(clientStream, "HTTP/1.1 502 Bad Gateway\r\n\r\n", ct);
}
}
private static async Task HandleHttpRequest(NetworkStream clientStream, string request, string url, CancellationToken ct)
{
try
{
var uri = new Uri(url);
IPAddress ip = ResolveHostname(uri.Host, alwaysTryFallback: true);
if (ip == null)
{
await SendResponse(clientStream, "HTTP/1.1 502 Bad Gateway\r\n\r\n", ct);
return;
}
using (var targetClient = new TcpClient())
{
await targetClient.ConnectAsync(ip, uri.Port > 0 ? uri.Port : 80);
using (var targetStream = targetClient.GetStream())
{
// Modify request to use relative path
string modifiedRequest = request.Replace(url, uri.PathAndQuery);
byte[] requestBytes = Encoding.ASCII.GetBytes(modifiedRequest);
await targetStream.WriteAsync(requestBytes, 0, requestBytes.Length, ct);
// Relay response
await RelayData(targetStream, clientStream, ct);
}
}
}
catch (Exception ex)
{
Logger.Log($"HTTP proxy error: {ex.Message}", LogLevel.WARNING);
}
}
private static async Task SendResponse(NetworkStream stream, string response, CancellationToken ct)
{
byte[] bytes = Encoding.ASCII.GetBytes(response);
try { await stream.WriteAsync(bytes, 0, bytes.Length, ct); } catch { }
}
private static async Task RelayData(NetworkStream from, NetworkStream to, CancellationToken ct)
{
byte[] buffer = new byte[8192];
try
{
int bytesRead;
while ((bytesRead = await from.ReadAsync(buffer, 0, buffer.Length, ct)) > 0)
await to.WriteAsync(buffer, 0, bytesRead, ct);
}
catch { }
}
}
}

View File

@@ -0,0 +1,91 @@
using System;
using System.IO;
using System.Threading;
namespace AndroidSideloader.Utilities
{
internal static class FileSystemUtilities
{
public static bool TryDeleteDirectory(string directoryPath, int maxRetries = 3, int delayMs = 150) // 3x 150ms = 450ms total
{
if (string.IsNullOrWhiteSpace(directoryPath))
return true;
if (!Directory.Exists(directoryPath))
return true;
Exception lastError = null;
// Retry deletion several times in case of lock ups
for (int attempt = 0; attempt <= maxRetries; attempt++)
{
try
{
StripReadOnlyAttributes(directoryPath);
Directory.Delete(directoryPath, true);
return true;
}
catch (DirectoryNotFoundException)
{
return true;
}
catch (Exception ex) when (ex is UnauthorizedAccessException || ex is IOException)
{
lastError = ex;
if (attempt < maxRetries)
{
Thread.Sleep(delayMs);
continue;
}
break;
}
catch (Exception ex)
{
// Non-retryable error
lastError = ex;
break;
}
}
// Last resort: rename then delete
try
{
string renamedPath = directoryPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
+ ".deleting." + DateTime.UtcNow.Ticks;
Directory.Move(directoryPath, renamedPath);
StripReadOnlyAttributes(renamedPath);
Directory.Delete(renamedPath, true);
return true;
}
catch (Exception ex)
{
lastError = ex;
}
Logger.Log($"Failed to delete directory: {directoryPath}. Error: {lastError}", LogLevel.WARNING);
return false;
}
private static void StripReadOnlyAttributes(string directoryPath)
{
var root = new DirectoryInfo(directoryPath);
if (!root.Exists) return;
root.Attributes &= ~FileAttributes.ReadOnly;
foreach (var dir in root.EnumerateDirectories("*", SearchOption.AllDirectories))
{
dir.Attributes &= ~FileAttributes.ReadOnly;
}
foreach (var file in root.EnumerateFiles("*", SearchOption.AllDirectories))
{
file.Attributes &= ~FileAttributes.ReadOnly;
}
}
}
}

View File

@@ -93,7 +93,7 @@ namespace AndroidSideloader.Utilities
string error = cmd.StandardError.ReadToEnd();
string output = cmd.StandardOutput.ReadToEnd();
_ = Logger.Log($"Output: {output}");
_ = Logger.Log($"Error: {error}");
_ = Logger.Log($"Error: {error}", LogLevel.ERROR);
return new ProcessOutput(output, error);
}

View File

@@ -1,28 +1,110 @@
using System;
using System.IO;
namespace AndroidSideloader
{
internal class Logger
{
public string logfile = Properties.Settings.Default.CurrentLogPath;
public static bool Log(string text, bool ret = true)
{
string time = DateTime.Now.ToString("hh:mmtt(UTC): ");
if (text.Length > 5)
{
string newline = "\n";
if (text.Length > 40 && text.Contains("\n"))
{
newline += "\n\n";
}
try { File.AppendAllText(Properties.Settings.Default.CurrentLogPath, time + text + newline); } catch { }
return ret;
}
return ret;
}
}
}
using AndroidSideloader.Utilities;
using System;
using System.IO;
using System.Text;
namespace AndroidSideloader
{
public enum LogLevel
{
DEBUG,
INFO,
WARNING,
ERROR,
TRACE,
FATAL
}
public static class Logger
{
private static readonly SettingsManager settings = SettingsManager.Instance;
private static readonly object lockObject = new object();
private static string logFilePath = settings.CurrentLogPath;
public static void Initialize()
{
try
{
// Set default log path if not already set
if (string.IsNullOrEmpty(logFilePath))
{
logFilePath = Path.Combine(Environment.CurrentDirectory, "debuglog.txt");
}
// Create directory if it doesn't exist
string logDirectory = Path.GetDirectoryName(logFilePath);
if (!string.IsNullOrEmpty(logDirectory) && !Directory.Exists(logDirectory))
{
Directory.CreateDirectory(logDirectory);
}
// Create log file if it doesn't exist
if (!File.Exists(logFilePath))
{
using (FileStream fs = File.Create(logFilePath))
{
// Create empty file
}
}
// Update settings with log path
settings.CurrentLogPath = logFilePath;
settings.Save();
// Initial log entry, make it stand out
string time = DateTime.UtcNow.ToString("hh:mm:ss.fff tt (UTC): ");
Log($"\n\n{time}------------ Logger initialized ------------", LogLevel.INFO);
}
catch (Exception ex)
{
Console.WriteLine($"Error initializing logger: {ex.Message}");
}
}
public static bool Log(string text, LogLevel logLevel = LogLevel.INFO, bool ret = true)
{
if (string.IsNullOrWhiteSpace(text) || text.Length <= 5)
return ret;
// Initialize logger if not already initialized
if (string.IsNullOrEmpty(logFilePath))
{
Initialize();
}
string time = DateTime.UtcNow.ToString("hh:mm:ss.fff tt (UTC): ");
string newline = text.Length > 40 && text.Contains("\n") ? "\n\n" : "\n";
string logEntry = time + "[" + logLevel.ToString().ToUpper() + "] [" + GetCallerInfo() + "] " + text + newline;
try
{
lock (lockObject)
{
File.AppendAllText(logFilePath, logEntry);
}
}
catch (Exception ex)
{
Console.WriteLine($"Error writing to log: {ex.Message}");
}
return ret;
}
private static string GetCallerInfo()
{
System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace(true);
if (stackTrace.FrameCount >= 3)
{
var frame = stackTrace.GetFrame(2);
var method = frame.GetMethod();
string className = method.DeclaringType?.Name;
string methodName = method.Name;
string callerInfo = $"{className}.{methodName}";
return callerInfo;
}
return string.Empty;
}
}
}

81
Utilities/Metrics.cs Normal file
View File

@@ -0,0 +1,81 @@
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace AndroidSideloader.Utilities
{
internal class Metrics
{
public static async void CountDownload(string packageName, string versionCode)
{
try
{
var apiUrl = "https://api.vrpirates.wiki/metrics/add";
var requestBody = new
{
packagename = packageName,
versioncode = versionCode
};
var json = JsonConvert.SerializeObject(requestBody);
string res = await Task.Run(() => sendToApi(apiUrl, json, "post"));
_ = Logger.Log(res);
}
catch (Exception ex)
{
Logger.Log($"Unable to log download: {ex.Message}", LogLevel.WARNING);
}
}
private static async Task<string> sendToApi(string apiUrl, string requestBody = null, string type = "get")
{
string token = "cm9va2llOkN0UHlyTE9oUGoxWXg1cE9KdDNBSkswZ25n";
using (var client = new HttpClient())
{
var request = new HttpRequestMessage();
// Set the HTTP method
request.Method = type.ToLower() == "post" ? HttpMethod.Post : HttpMethod.Get;
// For GET requests with parameters, append them to the URL
if (request.Method == HttpMethod.Get && !string.IsNullOrEmpty(requestBody))
{
var uriBuilder = new UriBuilder(apiUrl);
uriBuilder.Query = requestBody;
request.RequestUri = uriBuilder.Uri;
}
else
{
request.RequestUri = new Uri(apiUrl);
}
// For POST requests, set the content
if (request.Method == HttpMethod.Post && !string.IsNullOrEmpty(requestBody))
{
request.Content = new StringContent(requestBody, Encoding.UTF8, "application/json");
}
// Add headers to the request
request.Headers.Add("Authorization", token);
request.Headers.Add("Origin", "rookie");
string responseContent = "";
try
{
HttpResponseMessage response = await client.SendAsync(request);
responseContent = await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
Logger.Log($"Unable to get Metrics Data: {ex.Message}", LogLevel.WARNING);
}
return responseContent;
}
}
}
}

View File

@@ -0,0 +1,332 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
namespace AndroidSideloader.Utilities
{
public class SettingsManager : IDisposable
{
private static readonly Lazy<SettingsManager> _instance = new Lazy<SettingsManager>(() => new SettingsManager());
private static readonly string settingsFilePath = Path.Combine(
Environment.CurrentDirectory,
"settings.json");
// Custom converters for special types
public class FontConverter : JsonConverter<Font>
{
public override Font ReadJson(JsonReader reader, Type objectType, Font existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var jo = JObject.Load(reader);
string fontFamily = jo["FontFamily"]?.Value<string>() ?? "Microsoft Sans Serif";
float fontSize = jo["Size"]?.Value<float>() ?? 11.25f;
return new Font(fontFamily, fontSize);
}
public override void WriteJson(JsonWriter writer, Font value, JsonSerializer serializer)
{
writer.WriteStartObject();
writer.WritePropertyName("FontFamily");
writer.WriteValue(value.FontFamily.Name);
writer.WritePropertyName("Size");
writer.WriteValue(value.Size);
writer.WriteEndObject();
}
}
public class ColorConverter : JsonConverter<Color>
{
public override Color ReadJson(JsonReader reader, Type objectType, Color existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var jo = JObject.Load(reader);
int a = jo["A"]?.Value<int>() ?? 255;
int r = jo["R"]?.Value<int>() ?? 0;
int g = jo["G"]?.Value<int>() ?? 0;
int b = jo["B"]?.Value<int>() ?? 0;
return Color.FromArgb(a, r, g, b);
}
public override void WriteJson(JsonWriter writer, Color value, JsonSerializer serializer)
{
writer.WriteStartObject();
writer.WritePropertyName("A");
writer.WriteValue(value.A);
writer.WritePropertyName("R");
writer.WriteValue(value.R);
writer.WritePropertyName("G");
writer.WriteValue(value.G);
writer.WritePropertyName("B");
writer.WriteValue(value.B);
writer.WriteEndObject();
}
}
[JsonConverter(typeof(FontConverter))]
public Font FontStyle { get; set; } = new Font("Microsoft Sans Serif", 11.25f);
[JsonConverter(typeof(FontConverter))]
public Font BigFontStyle { get; set; } = new Font("Microsoft Sans Serif", 14f);
[JsonConverter(typeof(ColorConverter))]
public Color FontColor { get; set; } = Color.White;
[JsonConverter(typeof(ColorConverter))]
public Color ComboBoxColor { get; set; } = Color.FromArgb(25, 25, 25);
[JsonConverter(typeof(ColorConverter))]
public Color SubButtonColor { get; set; } = Color.FromArgb(25, 25, 25);
[JsonConverter(typeof(ColorConverter))]
public Color TextBoxColor { get; set; } = Color.FromArgb(25, 25, 25);
[JsonConverter(typeof(ColorConverter))]
public Color ButtonColor { get; set; } = Color.Black;
[JsonConverter(typeof(ColorConverter))]
public Color BackColor { get; set; } = Color.FromArgb(1, 1, 1);
public bool CheckForUpdates { get; set; } = true;
public bool EnableMessageBoxes { get; set; } = true;
public bool FirstRun { get; set; } = true;
public bool DeleteAllAfterInstall { get; set; } = true;
public bool AutoUpdateConfig { get; set; } = true;
public bool UserJsonOnGameInstall { get; set; } = false;
public bool CallUpgrade { get; set; } = true;
public string BackPicturePath { get; set; } = string.Empty;
public bool SpoofGames { get; set; } = false;
public bool ResignAPKs { get; set; } = false;
public string IPAddress { get; set; } = string.Empty;
public string InstalledApps { get; set; } = string.Empty;
public string ADBPath { get; set; } = string.Empty;
public string MainDir { get; set; } = string.Empty;
public bool Delsh { get; set; } = false;
public string CurrPckg { get; set; } = string.Empty;
public string ADBFolder { get; set; } = string.Empty;
public bool WirelessADB { get; set; } = false;
public string CurrentGamename { get; set; } = string.Empty;
public bool PackageNameToCB { get; set; } = false;
public bool DownUpHeld { get; set; } = false;
public string CurrentLogPath { get; set; } = string.Empty;
public string CurrentLogName { get; set; } = string.Empty;
public string CurrentCrashPath { get; set; } = string.Empty;
public string CurrentCrashName { get; set; } = string.Empty;
public bool AdbDebugWarned { get; set; } = false;
public bool NodeviceMode { get; set; } = false;
public bool BMBFChecked { get; set; } = true;
public string GamesList { get; set; } = string.Empty;
public bool UploadedGameList { get; set; } = false;
public string GlobalUsername { get; set; } = string.Empty;
public DateTime LastTimeShared { get; set; } = new DateTime(1969, 4, 20, 16, 20, 0);
public bool AutoReinstall { get; set; } = false;
public string NonAppPackages { get; set; } = string.Empty;
public DateTime LastLaunch { get; set; } = new DateTime(1969, 4, 20, 16, 20, 0);
public string SubmittedUpdates { get; set; } = string.Empty;
public bool ListUpped { get; set; } = false;
public DateTime LastLaunch2 { get; set; } = new DateTime(1969, 4, 20, 16, 20, 0);
public bool Wired { get; set; } = false;
public string AppPackages { get; set; } = string.Empty;
public string DownloadDir { get; set; } = string.Empty;
public bool CustomDownloadDir { get; set; } = false;
public bool CustomBackupDir { get; set; } = false;
public string BackupDir { get; set; } = string.Empty;
public bool SingleThreadMode { get; set; } = true;
public bool VirtualFilesystemCompatibility { get; set; } = false;
public bool UpdateSettings { get; set; } = true;
public string UUID { get; set; } = Guid.NewGuid().ToString();
public bool CreatePubMirrorFile { get; set; } = true;
public bool UseDownloadedFiles { get; set; } = false;
public float BandwidthLimit { get; set; } = 0f;
public string[] FavoritedGames { get; set; } = new string[0];
public bool useProxy { get; set; } = false;
public string ProxyAddress { get; set; } = string.Empty;
public string ProxyPort { get; set; } = string.Empty;
public string selectedMirror { get; set; } = string.Empty;
public bool TrailersEnabled { get; set; } = true;
public bool UseGalleryView { get; set; } = true;
// Window state persistence
public int WindowX { get; set; } = -1;
public int WindowY { get; set; } = -1;
public int WindowWidth { get; set; } = -1;
public int WindowHeight { get; set; } = -1;
public bool WindowMaximized { get; set; } = false;
// Sort state persistence
public int SortColumn { get; set; } = 0;
public bool SortAscending { get; set; } = true;
// Download queue persistence
public string[] QueuedGames { get; set; } = new string[0];
private SettingsManager()
{
Load();
Save();
}
public static SettingsManager Instance => _instance.Value;
public void Save()
{
try
{
var settings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
var json = JsonConvert.SerializeObject(this, settings);
File.WriteAllText(settingsFilePath, json);
}
catch (Exception ex)
{
Console.WriteLine($"Error saving settings: {ex.Message}");
}
}
private void Load()
{
Debug.WriteLine("Loading settings...");
if (!File.Exists(settingsFilePath))
{
CreateDefaultSettings();
return;
}
try
{
var json = File.ReadAllText(settingsFilePath);
var settings = new JsonSerializerSettings
{
Error = (sender, args) =>
{
Debug.WriteLine($"Error deserializing setting: {args.ErrorContext.Error.Message}");
args.ErrorContext.Handled = true;
}
};
JsonConvert.PopulateObject(json, this, settings);
}
catch (Exception ex)
{
Debug.WriteLine($"Error loading settings: {ex.Message}");
CreateDefaultSettings();
}
}
private void CreateDefaultSettings()
{
FontStyle = new Font("Microsoft Sans Serif", 11.25f);
BigFontStyle = new Font("Microsoft Sans Serif", 14f);
FontColor = Color.White;
ComboBoxColor = Color.FromArgb(25, 25, 25);
SubButtonColor = Color.FromArgb(25, 25, 25);
TextBoxColor = Color.FromArgb(25, 25, 25);
ButtonColor = Color.Black;
BackColor = Color.FromArgb(1, 1, 1);
CheckForUpdates = true;
EnableMessageBoxes = true;
FirstRun = true;
DeleteAllAfterInstall = true;
AutoUpdateConfig = true;
UserJsonOnGameInstall = false;
CallUpgrade = true;
BackPicturePath = string.Empty;
SpoofGames = false;
ResignAPKs = false;
IPAddress = string.Empty;
InstalledApps = string.Empty;
ADBPath = string.Empty;
MainDir = string.Empty;
Delsh = false;
CurrPckg = string.Empty;
ADBFolder = string.Empty;
WirelessADB = false;
CurrentGamename = string.Empty;
PackageNameToCB = false;
DownUpHeld = false;
CurrentLogPath = string.Empty;
CurrentLogName = string.Empty;
CurrentCrashPath = string.Empty;
CurrentCrashName = string.Empty;
AdbDebugWarned = false;
NodeviceMode = false;
BMBFChecked = true;
GamesList = string.Empty;
UploadedGameList = false;
GlobalUsername = string.Empty;
LastTimeShared = new DateTime(1969, 4, 20, 16, 20, 0);
AutoReinstall = false;
NonAppPackages = string.Empty;
LastLaunch = new DateTime(1969, 4, 20, 16, 20, 0);
SubmittedUpdates = string.Empty;
ListUpped = false;
LastLaunch2 = new DateTime(1969, 4, 20, 16, 20, 0);
Wired = false;
AppPackages = string.Empty;
DownloadDir = string.Empty;
CustomDownloadDir = false;
CustomBackupDir = false;
BackupDir = string.Empty;
SingleThreadMode = true;
VirtualFilesystemCompatibility = false;
UpdateSettings = true;
UUID = Guid.NewGuid().ToString();
CreatePubMirrorFile = true;
UseDownloadedFiles = false;
BandwidthLimit = 0f;
FavoritedGames = new string[0];
useProxy = false;
ProxyAddress = string.Empty;
ProxyPort = string.Empty;
selectedMirror = string.Empty;
TrailersEnabled = true;
UseGalleryView = true;
WindowX = -1;
WindowY = -1;
WindowWidth = -1;
WindowHeight = -1;
WindowMaximized = false;
SortColumn = 0;
SortAscending = true;
QueuedGames = new string[0];
Save();
Debug.WriteLine("Default settings created.");
}
public void AddFavoriteGame(string packageName)
{
if (!FavoritedGames.Contains(packageName))
{
var list = FavoritedGames.ToList();
list.Add(packageName);
FavoritedGames = list.ToArray();
Save();
}
}
public void RemoveFavoriteGame(string packageName)
{
if (FavoritedGames.Contains(packageName))
{
var list = FavoritedGames.ToList();
list.Remove(packageName);
FavoritedGames = list.ToArray();
Save();
}
}
public string GetEffectiveBackupDir()
{
if (CustomBackupDir && Directory.Exists(BackupDir))
{
return BackupDir;
}
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Rookie Backups");
}
public void Dispose()
{
FontStyle?.Dispose();
BigFontStyle?.Dispose();
}
}
}

View File

@@ -61,5 +61,13 @@ namespace AndroidSideloader.Utilities
return s;
}
public static bool TryParseDouble(string value, out double result)
{
return double.TryParse(value,
System.Globalization.NumberStyles.Any,
System.Globalization.CultureInfo.InvariantCulture,
out result);
}
}
}

View File

@@ -3,35 +3,60 @@ using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Windows.Forms;
namespace AndroidSideloader.Utilities
{
public class ExtractionException : Exception
{
public ExtractionException(string message) : base(message) { }
}
internal class Zip
{
private static readonly SettingsManager settings = SettingsManager.Instance;
// Progress callback: (percent, eta)
public static Action<float, TimeSpan?> ExtractionProgressCallback { get; set; }
public static Action<string> ExtractionStatusCallback { get; set; }
public static void ExtractFile(string sourceArchive, string destination)
{
string args = $"x \"{sourceArchive}\" -y -o\"{destination}\"";
string args = $"x \"{sourceArchive}\" -y -o\"{destination}\" -bsp1";
DoExtract(args);
}
public static void ExtractFile(string sourceArchive, string destination, string password)
{
string args = $"x \"{sourceArchive}\" -y -o\"{destination}\" -p\"{password}\"";
string args = $"x \"{sourceArchive}\" -y -o\"{destination}\" -p\"{password}\" -bsp1";
DoExtract(args);
}
private static string extractionError = null;
private static bool errorMessageShown = false;
private static void DoExtract(string args)
{
if (!File.Exists(Environment.CurrentDirectory + "\\7z.exe") || !File.Exists(Environment.CurrentDirectory + "\\7z.dll"))
if (!File.Exists(Path.Combine(Environment.CurrentDirectory, "7z.exe")) || !File.Exists(Path.Combine(Environment.CurrentDirectory, "7z.dll")))
{
_ = Logger.Log("Begin download 7-zip");
WebClient client = new WebClient();
client.DownloadFile("https://github.com/nerdunit/androidsideloader/raw/master/7z.exe", "7z.exe");
client.DownloadFile("https://github.com/nerdunit/androidsideloader/raw/master/7z.dll", "7z.dll");
string architecture = Environment.Is64BitOperatingSystem ? "64" : "";
try
{
// Use DNS fallback download method from GetDependencies
GetDependencies.DownloadFileWithDnsFallback($"https://github.com/VRPirates/rookie/raw/master/7z{architecture}.exe", "7z.exe");
GetDependencies.DownloadFileWithDnsFallback($"https://github.com/VRPirates/rookie/raw/master/7z{architecture}.dll", "7z.dll");
}
catch (Exception ex)
{
_ = FlexibleMessageBox.Show(Program.form, $"You are unable to access the GitHub page with the Exception: {ex.Message}\nSome files may be missing (7z)");
_ = FlexibleMessageBox.Show(Program.form, "7z was unable to be downloaded\nRookie will now close");
Application.Exit();
}
_ = Logger.Log("Complete download 7-zip");
}
ProcessStartInfo pro = new ProcessStartInfo
{
WindowStyle = ProcessWindowStyle.Hidden,
@@ -46,25 +71,150 @@ namespace AndroidSideloader.Utilities
_ = Logger.Log($"Extract: 7z {string.Join(" ", args.Split(' ').Where(a => !a.StartsWith("-p")))}");
Process x = Process.Start(pro);
x.WaitForExit();
if (x.ExitCode != 0)
{
string error = x.StandardError.ReadToEnd();
// Throttle percent reports
float lastReportedPercent = -1;
if (error.Contains("There is not enough space on the disk"))
// ETA engine (percent units)
var etaEstimator = new EtaEstimator(alpha: 0.10, reanchorThreshold: 0.20, minSampleSeconds: 0.10);
// Smooth progress (sub-percent) interpolation (because 7z -bsp1 is integer-only)
System.Threading.Timer smoothTimer = null;
int extractingFlag = 1; // 1 = extracting, 0 = stop
float smoothLastTickPercent = 0f;
DateTime smoothLastTickTime = DateTime.UtcNow;
float smoothLastReported = -1f;
const int SmoothIntervalMs = 80; // ~12.5 updates/sec
const float SmoothReportDelta = 0.10f; // report only if change >= 0.10%
using (Process x = new Process())
{
x.StartInfo = pro;
if (MainForm.isInDownloadExtract && x != null)
{
_ = FlexibleMessageBox.Show(Program.form, $"Not enough space to extract archive.\r\nCheck free space in {Environment.CurrentDirectory} and try again.",
"NOT ENOUGH SPACE",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
// Smooth sub-percent UI, while keeping ETA ticking
smoothTimer = new System.Threading.Timer(_ =>
{
if (System.Threading.Volatile.Read(ref extractingFlag) == 0) return;
if (smoothLastTickPercent <= 0) return; // need at least one 7z tick
// Use current ETA to approximate seconds-per-percent
TimeSpan? displayEta = etaEstimator.GetDisplayEta();
if (!displayEta.HasValue) return; // Skip until ETA exists
var now = DateTime.UtcNow;
var elapsed = (now - smoothLastTickTime).TotalSeconds;
// Approx seconds-per-percent from remaining ETA / remaining percent
double remainingPercent = Math.Max(1.0, 100.0 - smoothLastTickPercent);
double spp = Math.Max(0.05, displayEta.Value.TotalSeconds / remainingPercent);
float candidate = smoothLastTickPercent + (float)(elapsed / spp);
// Clamp
float floorTick = (float)Math.Floor(smoothLastTickPercent);
float ceiling = Math.Min(99.99f, floorTick + 0.999f);
if (candidate > ceiling) candidate = ceiling;
if (candidate < smoothLastTickPercent) candidate = smoothLastTickPercent;
if (smoothLastReported >= 0 && Math.Abs(candidate - smoothLastReported) < SmoothReportDelta) return;
smoothLastReported = candidate;
try
{
MainForm mainForm = (MainForm)Application.OpenForms[0];
if (mainForm != null && !mainForm.IsDisposed)
{
mainForm.BeginInvoke((Action)(() => mainForm.SetProgress(candidate)));
}
}
catch { }
// ETA countdown ticks even if 7z percent is unchanged
ExtractionProgressCallback?.Invoke(candidate, etaEstimator.GetDisplayEta());
}, null, SmoothIntervalMs, SmoothIntervalMs);
x.OutputDataReceived += (sender, e) =>
{
if (e.Data != null)
{
var match = Regex.Match(e.Data, @"^\s*(\d+)%");
if (match.Success && float.TryParse(match.Groups[1].Value, out float percent))
{
// Update ETA from integer percent
if (percent <= 0.0f) etaEstimator.Reset();
else if (percent < 100.0f) etaEstimator.Update(totalUnits: 100, doneUnits: (long)Math.Round(percent));
// Reset smoothing baseline on each integer tick
smoothLastTickPercent = percent;
smoothLastTickTime = DateTime.UtcNow;
smoothLastReported = percent;
if (Math.Abs(percent - lastReportedPercent) >= 0.1f)
{
lastReportedPercent = percent;
MainForm mainForm = (MainForm)Application.OpenForms[0];
if (mainForm != null)
{
mainForm.Invoke((Action)(() => mainForm.SetProgress(percent)));
}
ExtractionProgressCallback?.Invoke(percent, etaEstimator.GetDisplayEta());
}
}
}
};
}
x.ErrorDataReceived += (sender, e) =>
{
if (e.Data != null)
{
var error = e.Data;
if (error.Contains("There is not enough space on the disk") && !errorMessageShown)
{
errorMessageShown = true;
Program.form.Invoke(new Action(() =>
{
_ = FlexibleMessageBox.Show(Program.form, $"Not enough space to extract archive.\r\nMake sure your {Path.GetPathRoot(settings.DownloadDir)} drive has at least double the space of the game, then try again.",
"NOT ENOUGH SPACE",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
return;
}));
}
_ = Logger.Log(error, LogLevel.ERROR);
extractionError = $"Extracting failed: {error}"; // Store the error message directly
return;
}
};
x.Start();
x.BeginOutputReadLine();
x.BeginErrorReadLine();
x.WaitForExit();
// Stop smoother
System.Threading.Interlocked.Exchange(ref extractingFlag, 0);
smoothTimer?.Dispose();
smoothTimer = null;
// Clear callbacks
ExtractionProgressCallback?.Invoke(100, null);
ExtractionStatusCallback?.Invoke("");
errorMessageShown = false;
if (!string.IsNullOrEmpty(extractionError))
{
string errorMessage = extractionError;
extractionError = null; // Reset the error message
throw new ExtractionException(errorMessage);
}
_ = Logger.Log(x.StandardOutput.ReadToEnd());
_ = Logger.Log(error);
throw new ApplicationException($"Extracting failed, status code {x.ExitCode}");
}
}
}
}
}

BIN
aapt.exe

Binary file not shown.

BIN
adb.7z

Binary file not shown.

BIN
adb2.zip

Binary file not shown.

Binary file not shown.

22
build.cmd Normal file
View File

@@ -0,0 +1,22 @@
@echo off
REM Default to Release if no argument is provided
SET CONFIG=Release
IF NOT "%1"=="" (
IF /I "%1"=="debug" SET CONFIG=Debug
)
REM Windows Batch script version
REM Attempts to find MSBuild from common Visual Studio 2022 installation paths
IF EXIST "C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe" (
SET MSBUILD="C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe"
) ELSE IF EXIST "C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Current\Bin\MSBuild.exe" (
SET MSBUILD="C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Current\Bin\MSBuild.exe"
) ELSE IF EXIST "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\MSBuild.exe" (
SET MSBUILD="C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\MSBuild.exe"
) ELSE (
echo MSBuild not found! Please check your Visual Studio installation.
exit /b 1
)
echo Building in %CONFIG% configuration...
%MSBUILD% AndroidSideloader.sln /t:AndroidSideloader /p:Configuration=%CONFIG%

View File

@@ -1,8 +1,12 @@
RSL 2.18
RSL 3.0.1
- Moving Rookie onto a different Directory than C: will not break Pull App To Desktop anymore
- Rookie should now tell the user why it couldnt get the Rclone/ADB etc. Files
- Dialogs will should now no longer hide behind Rookie.
- ADB was updated to the latest version.
~ Chax & fenopy
- Fixed popularity ranking not working on some systems
- Fixed favorites not updating immediately when removing items
- Improved YouTube trailer matching accuracy
- Implemented real-time progress updates for drag and drop operations
- Refined backup button labels and dialogs
- Gallery View: Added grouped tiles for games with multiple versions (e.g. Beat Saber)
- ListView: Uninstall button now shows on hover instead of click
- Public config file is now created automatically without prompt
- Sideloading status label now shows device connection state
- Download button text now reflects sideloading status

6
codenames Normal file
View File

@@ -0,0 +1,6 @@
monterey
hollywood
seacliff
eureka
panther
quest

34708
debuglog.txt

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

BIN
dependencies.7z Normal file

Binary file not shown.

View File

@@ -1,12 +0,0 @@
CC0E0834BFEBFBFF000906E9;5000;ROOKIE.WTF
184204E0178BFBFF00870F10;0;Flow
E5148390BFEBFBFF000906EA;250;Gotard
5ECC5497178BFBFF00870F10;1024;Saidis21
B0374BE2BFEBFBFF00040651;0;ecirbaf
926C60A8178BFBFF00800F82;250;ThePhantomPickaxe
2A8C5999BFEBFBFF0001067A;0;karl
80ACB80FBFEBFBFF000206C2;4096;JJ-4
9AA172C9BFEBFBFF000906ED;1024;Clayton Bigsby
645C9EADBFEBFBFF000906EA;5000;Heracide
1C51D0CD178BFBFF00870F10;0;Mr.Tibby
287B5C6CBFEBFBFF000906EA;0;videobeer

BIN
icon.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -1,6 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Costura.Fody" version="4.1.0" targetFramework="net452" />
<package id="Fody" version="6.0.0" targetFramework="net452" developmentDependency="true" />
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net452" />
<package id="AdvancedSharpAdbClient" version="3.5.15" targetFramework="net452" />
<package id="Costura.Fody" version="5.7.0" targetFramework="net452" developmentDependency="true" />
<package id="Fody" version="6.8.1" targetFramework="net452" developmentDependency="true" />
<package id="Microsoft.NETCore.Platforms" version="7.0.4" targetFramework="net452" />
<package id="Microsoft.Web.WebView2" version="1.0.2478.35" targetFramework="net452" />
<package id="NETStandard.Library" version="2.0.3" targetFramework="net452" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net452" />
<package id="System.Collections" version="4.3.0" targetFramework="net452" />
<package id="System.Collections.Concurrent" version="4.3.0" targetFramework="net452" />
<package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net452" />
<package id="System.Diagnostics.Tools" version="4.3.0" targetFramework="net452" />
<package id="System.Diagnostics.Tracing" version="4.3.0" targetFramework="net452" />
<package id="System.Globalization" version="4.3.0" targetFramework="net452" />
<package id="System.IO" version="4.3.0" targetFramework="net452" />
<package id="System.IO.Compression" version="4.3.0" targetFramework="net452" />
<package id="System.Linq" version="4.3.0" targetFramework="net452" />
<package id="System.Linq.Expressions" version="4.3.0" targetFramework="net452" />
<package id="System.Net.Http" version="4.3.4" targetFramework="net452" />
<package id="System.Net.Primitives" version="4.3.1" targetFramework="net452" />
<package id="System.ObjectModel" version="4.3.0" targetFramework="net452" />
<package id="System.Reflection" version="4.3.0" targetFramework="net452" />
<package id="System.Reflection.Extensions" version="4.3.0" targetFramework="net452" />
<package id="System.Reflection.Primitives" version="4.3.0" targetFramework="net452" />
<package id="System.Resources.ResourceManager" version="4.3.0" targetFramework="net452" />
<package id="System.Runtime" version="4.3.1" targetFramework="net452" />
<package id="System.Runtime.Extensions" version="4.3.1" targetFramework="net452" />
<package id="System.Runtime.InteropServices" version="4.3.0" targetFramework="net452" />
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net452" />
<package id="System.Runtime.Numerics" version="4.3.0" targetFramework="net452" />
<package id="System.Text.Encoding" version="4.3.0" targetFramework="net452" />
<package id="System.Text.Encoding.Extensions" version="4.3.0" targetFramework="net452" />
<package id="System.Text.RegularExpressions" version="4.3.1" targetFramework="net452" />
<package id="System.Threading" version="4.3.0" targetFramework="net452" />
<package id="System.Threading.Tasks" version="4.3.0" targetFramework="net452" />
<package id="System.Threading.Timer" version="4.3.0" targetFramework="net452" />
<package id="System.Xml.ReaderWriter" version="4.3.1" targetFramework="net452" />
<package id="System.Xml.XDocument" version="4.3.0" targetFramework="net452" />
</packages>

Binary file not shown.

View File

@@ -1,5 +0,0 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<WeaverFiles Include="$(MsBuildThisFileDirectory)..\weaver\$(MSBuildThisFileName).dll" />
</ItemGroup>
</Project>

Binary file not shown.

View File

@@ -1,85 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<xs:complexType xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:all>
<xs:element minOccurs="0" maxOccurs="1" name="ExcludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="IncludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged32Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="PreloadOrder" type="xs:string">
<xs:annotation>
<xs:documentation>The order of preloaded assemblies, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:all>
<xs:attribute name="CreateTemporaryAssemblies" type="xs:boolean">
<xs:annotation>
<xs:documentation>This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeDebugSymbols" type="xs:boolean">
<xs:annotation>
<xs:documentation>Controls if .pdbs for reference assemblies are also embedded.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="DisableCompression" type="xs:boolean">
<xs:annotation>
<xs:documentation>Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="DisableCleanup" type="xs:boolean">
<xs:annotation>
<xs:documentation>As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="LoadAtModuleInit" type="xs:boolean">
<xs:annotation>
<xs:documentation>Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IgnoreSatelliteAssemblies" type="xs:boolean">
<xs:annotation>
<xs:documentation>Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="ExcludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Unmanaged32Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Unmanaged64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="PreloadOrder" type="xs:string">
<xs:annotation>
<xs:documentation>The order of preloaded assemblies, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>

Binary file not shown.

Binary file not shown.

View File

@@ -1,110 +0,0 @@
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectWeaverXml Condition="$(ProjectWeaverXml) == ''">$(ProjectDir)FodyWeavers.xml</ProjectWeaverXml>
<FodyPath Condition="$(FodyPath) == ''">$(MSBuildThisFileDirectory)..\</FodyPath>
<FodyAssemblyDirectory Condition="$(MSBuildRuntimeType) == 'Core'">$(FodyPath)netstandardtask</FodyAssemblyDirectory>
<FodyAssemblyDirectory Condition="$(MSBuildRuntimeType) != 'Core'">$(FodyPath)netclassictask</FodyAssemblyDirectory>
<FodyAssembly Condition="$(FodyAssembly) == ''">$(FodyAssemblyDirectory)\Fody.dll</FodyAssembly>
<DefaultItemExcludes>$(DefaultItemExcludes);FodyWeavers.xsd</DefaultItemExcludes>
<FodyGenerateXsd Condition="$(FodyGenerateXsd) == ''">true</FodyGenerateXsd>
<MsBuildMajorVersion>15</MsBuildMajorVersion>
<MsBuildMajorVersion Condition="'$(MSBuildVersion)' != ''">$([System.Version]::Parse($(MSBuildVersion)).Major)</MsBuildMajorVersion>
</PropertyGroup>
<ItemGroup Condition="Exists($(ProjectWeaverXml))">
<UpToDateCheckInput Include="$(ProjectWeaverXml)" />
<CustomAdditionalCompileInputs Include="$(ProjectWeaverXml)" />
</ItemGroup>
<!-- Support for NCrunch -->
<ItemGroup Condition="'$(NCrunch)' == '1' and '$(TargetFramework)' == '' and '$(TargetFrameworks)' == ''">
<None Include="$(FodyAssemblyDirectory)\*.*" />
<None Include="@(WeaverFiles)" />
</ItemGroup>
<UsingTask TaskName="Fody.WeavingTask" AssemblyFile="$(FodyAssembly)" />
<UsingTask TaskName="Fody.UpdateReferenceCopyLocalTask" AssemblyFile="$(FodyAssembly)" />
<UsingTask TaskName="Fody.VerifyTask" AssemblyFile="$(FodyAssembly)" />
<Target
Name="FodyTarget"
AfterTargets="AfterCompile"
Condition="Exists(@(IntermediateAssembly)) And $(DesignTimeBuild) != true And $(DisableFody) != true"
DependsOnTargets="$(FodyDependsOnTargets)"
Inputs="@(IntermediateAssembly);$(ProjectWeaverXml)"
Outputs="$(IntermediateOutputPath)$(MSBuildProjectFile).Fody.CopyLocal.cache">
<Error Condition="($(MsBuildMajorVersion) &lt; 16)"
Text="Fody is only supported on MSBuild 16 and above. Current version: $(MsBuildMajorVersion)." />
<Fody.WeavingTask
AssemblyFile="@(IntermediateAssembly)"
IntermediateDirectory="$(ProjectDir)$(IntermediateOutputPath)"
KeyOriginatorFile="$(KeyOriginatorFile)"
AssemblyOriginatorKeyFile="$(AssemblyOriginatorKeyFile)"
ProjectDirectory="$(MSBuildProjectDirectory)"
ProjectFile="$(MSBuildProjectFullPath)"
SolutionDirectory="$(SolutionDir)"
References="@(ReferencePath)"
SignAssembly="$(SignAssembly)"
ReferenceCopyLocalFiles="@(ReferenceCopyLocalPaths)"
DefineConstants="$(DefineConstants)"
DebugType="$(DebugType)"
DocumentationFile="@(DocFileItem->'%(FullPath)')"
WeaverFiles="@(WeaverFiles)"
NCrunchOriginalSolutionDirectory="$(NCrunchOriginalSolutionDir)"
IntermediateCopyLocalFilesCache="$(IntermediateOutputPath)$(MSBuildProjectFile).Fody.CopyLocal.cache"
GenerateXsd="$(FodyGenerateXsd)"
>
<Output
TaskParameter="ExecutedWeavers"
PropertyName="FodyExecutedWeavers" />
</Fody.WeavingTask>
<ItemGroup>
<FileWrites Include="$(IntermediateOutputPath)$(MSBuildProjectFile).Fody.CopyLocal.cache" />
</ItemGroup>
</Target>
<Target
Name="FodyUpdateCopyLocalFilesTarget"
AfterTargets="FodyTarget"
>
<Fody.UpdateReferenceCopyLocalTask
ReferenceCopyLocalFiles="@(ReferenceCopyLocalPaths)"
IntermediateCopyLocalFilesCache="$(IntermediateOutputPath)$(MSBuildProjectFile).Fody.CopyLocal.cache"
>
<Output
TaskParameter="UpdatedReferenceCopyLocalFiles"
ItemName="FodyUpdatedReferenceCopyLocalPaths" />
</Fody.UpdateReferenceCopyLocalTask>
<ItemGroup>
<ReferenceCopyLocalPaths Remove="@(ReferenceCopyLocalPaths)" />
<ReferenceCopyLocalPaths Include="@(FodyUpdatedReferenceCopyLocalPaths)" />
</ItemGroup>
</Target>
<Target
Name="FodyVerifyTarget"
AfterTargets="AfterBuild"
Condition="'$(NCrunch)' != '1' And $(FodyExecutedWeavers) != '' And $(DisableFody) != true"
DependsOnTargets="$(FodyVerifyDependsOnTargets)">
<Fody.VerifyTask
ProjectDirectory="$(MSBuildProjectDirectory)"
TargetPath="$(TargetPath)"
SolutionDirectory="$(SolutionDir)"
DefineConstants="$(DefineConstants)"
NCrunchOriginalSolutionDirectory="$(NCrunchOriginalSolutionDir)"
/>
</Target>
</Project>

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More