Compare commits

...

848 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
Fenopy
84f444ab55 Revert "Make the settings reset button ressent the downloadDir as Default.Reset didnt seem to do so."
This reverts commit c44b4adf60.
2023-01-05 06:24:30 -06:00
Fenopy
83283d6bda Merge branch 'release/RSL-2.18' of https://github.com/nerdunit/androidsideloader into release/RSL-2.18 2023-01-05 06:23:20 -06:00
Fenopy
4cd3769115 ROLLBACK- Make the settings reset button ressent the downloadDir as Default.Reset didnt seem to do so. 2023-01-05 06:23:00 -06:00
Chax
c44b4adf60 Make the settings reset button ressent the downloadDir as Default.Reset didnt seem to do so. 2023-01-05 06:21:24 -06:00
Chax
fa59f45184 Comparing OBBs with no Device should not cause an exception anymore. 2023-01-05 06:21:16 -06:00
Fenopy
79323dfe58 bump versions 2023-01-04 15:53:57 -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
Fenopy
1462aaa30e Properly exit out of Rookie when needed files were unable to be downloaded and catched. 2023-01-04 09:32:55 -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
Fenopy
8bc0b1cbaf change comparison operator 2023-01-04 09:07:05 -06:00
Chax
b6110ad703 Change TaskResult to just a normal return. 2023-01-04 09:06:03 -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
Fenopy
3e4bfe22d8 bump ADB 2023-01-04 08:39:51 -06:00
Fenopy
199e2cbaec Properly catch exceptions when you cant access a webpage 2023-01-04 08:38:52 -06:00
Fenopy
24f9f961b9 Nouns.txt Removal for building the debuglog.txt 2023-01-04 08:36:54 -06:00
Fenopy
e9b3f52f0e Fix Rookie crashing when pulling game to desktop while Rookie is on another drive than C 2023-01-04 08:35:44 -06:00
Chax
acf0649150 Fix- Compare OBBs 2023-01-04 08:35:11 -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
Fenopy
8c5b2d623b added parent to dialogs 2022-12-20 08:27:36 -06:00
SytheZN
d3a2c4012c Release/RSL-2.17 (#128)
* fix: pull to desktop not resetting work status.

resolves #104

* Create Text File with upload size for verification

* bump version

* better public config handling

* code cleanup

Co-authored-by: Fenopy <Fenopie@Gmail.com>
2022-12-05 23:42:59 +02:00
SytheZN
67a9089952 Merge branch 'release/RSL-2.16' 2022-11-05 03:41:06 +02:00
pmow
22ecbee791 Merge pull request #124 from SytheZN/master
RSL 2.15
2022-10-28 08:33:40 -10:00
Chax
e07ffa54ee RSL 2.15
- Massive UI Changes:
- Rounded buttons
- Added version Number at Bottom Left
- Changed fonts to some extend
- Added background pattern to category buttons and MainForm background
- Added an animation when opening/closing category
- Overall darker theme
- New splash

~Chax
2022-10-23 21:07:43 +02:00
pmow
be560875da Merge pull request #123 from SytheZN/master
RSL 2.14
2022-10-11 11:51:47 -10:00
SytheZN
fdf7e409bf RSL 2.14 2022-10-11 23:42:34 +02:00
pmow
d9255bf02d Merge pull request #122 from SytheZN/master
Add public-config
2022-10-03 21:34:51 -10:00
SytheZN
40b88a2d4c RSL 2.13
- Added new config mode for use with http mirror
- Removed previous http mirror workarounds
- Added Offline Mode launcher to package fetch.
- Add splash screen on startup
2022-10-01 12:59:12 +02:00
pmow
c966066c67 Merge pull request #119 from Fenopy/master
2.12 update
2022-09-13 10:16:56 -10:00
pmow
5a1209f77d Merge pull request #121 from nerdunit/revert-120-master
Revert "Titlebar Changes and Icon Fixes"
2022-09-13 10:16:25 -10:00
pmow
fdd1732331 Revert "Titlebar Changes and Icon Fixes" 2022-09-13 10:15:56 -10:00
pmow
e46af16db5 Merge pull request #120 from Chax1/master
Titlebar Changes and Icon Fixes
2022-09-13 10:04:30 -10:00
Chax
9f6a7044a9 fix 2022-09-13 18:07:41 +02:00
Chax
f4c46184bd Merge branch 'master' of https://github.com/Chax1/androidsideloader 2022-09-13 17:37:26 +02:00
Chax
e7bf9ce9a7 Fixed Icons 2022-09-13 17:37:09 +02:00
Chax
5bf13dc655 Update MainForm.cs 2022-09-13 17:28:20 +02:00
Chax
f4cbeec974 Removed normal titlebar and switched it with a cleaner more custom one, should still keep all the status messages! 2022-09-13 17:24:10 +02:00
Fenopy
eecaefff6a 2.12 update 2022-09-13 10:18:38 -05:00
Fenopy
cd0feb4fd5 Add offline mode prompt 2022-09-13 09:32:40 -05:00
Fenopy
42aba1e8e3 gui updates to game donation screen 2022-09-13 08:39:39 -05:00
Fenopy
7d03fce760 - Fix tooltips; add additional tooltips
- add additional logging
2022-09-13 08:39:21 -05:00
Fenopy
177133041e Restore "Auto Update Config" checkbox 2022-09-13 08:07:42 -05:00
Fenopy
b563a1a859 Updater- fix download url 2022-09-13 07:51:15 -05:00
Fenopy
c81081c2cd Fixed github version vs app version comparison 2022-09-13 07:37:27 -05:00
Fenopy
910c62530e - fixed issue with configuration always updating 2022-09-13 07:37:05 -05:00
Fenopy
f20a4e3954 Remove TopMost to prevent "always on top" 2022-09-09 15:45:16 -05:00
pmow
9254d9d2bb Update version 2022-09-08 10:54:43 -10:00
pmow
e8c0c8928e Merge pull request #118 from Fenopy/master
more rclone command changes
2022-09-08 10:52:48 -10:00
Fenopy
3ad5b584d6 Merge branch 'nerdunit:master' into master 2022-09-07 15:41:08 -05:00
Fenopy
84a2132f9c - more changes to rclone commands to fix ETA 2022-09-07 15:40:35 -05:00
pmow
a70fa29891 Update README.md 2022-09-07 09:25:10 -10:00
pmow
79230e1faa Merge pull request #117 from Fenopy/master
multiple fixes for 2.11 release
2022-09-07 09:22:54 -10:00
Fenopy
d1c6ac4bf6 - additional logging
- syntax fixes
- global rclone args
- about updates
2022-09-07 10:14:57 -05:00
Fenopy
1c0fa27d6a - another rclone limit
- better exit routine on quota error
2022-09-06 16:40:14 -05:00
Fenopy
1f41421c56 - update status text
- change server tasks to synchronous
- spelling fixes
- changed threading for sync commands
2022-09-06 15:00:54 -05:00
pmow
1e5c55429c revert version for now 2022-09-06 08:39:14 -10:00
Fenopy
acf7040ba5 Merge pull request #1 from nerdunit/master
Merge pull request #116 from Fenopy/master
2022-09-06 13:33:53 -05:00
pmow
6a60145bd8 Merge pull request #116 from Fenopy/master
Updates for new server
2022-09-03 14:59:28 -10:00
pmow
e74cd11bd0 Merge branch 'master' into master 2022-09-03 14:59:17 -10:00
pmow
6aa70de7ac Merge pull request #115 from Chax1/master
Removed auto updating configs, and fixed one spelling mistake.
2022-09-03 14:31:47 -10:00
Fenopy
1719cdf2e3 - Restore auto-update config
- Changed update config to point to new source

~f
2022-09-01 15:46:06 -05:00
Fenopy
8c528b2093 - Updated rclone commands
- Minor code cleanup/syntax fixes
2022-08-29 15:08:26 -05:00
Fenopy
8270a3d138 - Disabled Auto-Config update
- Rate limit on downloads
2022-08-29 15:02:39 -05:00
Chax
361bec9060 Removed auto updating configs, and fixed one spelling mistake. 2022-07-19 14:24:17 +02:00
pmow
731d5c6f5f Merge pull request #108 from SytheZN/master
update quota exceeded error message
2022-05-26 16:54:49 -04:00
SytheZN
85ce7971fc update quota exceeded error message 2022-03-06 22:24:04 +02:00
nerdunit
43054c2965 Update README.md 2021-11-05 14:34:39 +02:00
nerdunit
8693c42331 Create LICENSE 2021-11-05 14:33:57 +02:00
pmow
bfe6c15a4f 2.10.4 Start at mirror 02 2021-11-05 00:37:58 -04:00
pmow
fb1a16b861 changelog update 2021-10-28 00:24:15 -04:00
pmow
311b9bd4da changelog 2021-10-28 00:21:23 -04:00
pmow
58e1b1d5d2 2.10.3 2021-10-28 00:19:47 -04:00
pmow
9d6eef85f1 Merge pull request #93 from asphalt123/master
Add offline mode to install games locally when no mirrors with the --offline switch
2021-10-26 20:49:09 -04:00
=
275344d421 Add offline mode to install games locally when no mirrors 2021-10-24 15:13:28 +02:00
nerdunit
e911970c8d Update README.md 2021-10-07 12:35:22 -07:00
William Swartwood
eb5df74dc5 RSL 2.10.2
= Fixed issue where same new apps were being
asked for before 72 hr window if not marked as
free.
= Changed 7zip compression level to Store for
faster zipping for donations (since apks are
already compressed the file size only changes
1-2MB at most, so upload time will be virtually
the same).

HFP
2021-10-05 03:30:25 -04:00
William Swartwood
95d4623e6c RSL 2.10.1
= Fixed sort by Size (MB).
= Fixed issue where game list column headers
were appearing as a game in the games list.

HFP
2021-10-02 01:37:02 -04:00
William Swartwood
8c8e97c610 Last minute touch-ups. Changed version number to 2.10.0 2021-09-29 23:01:58 -04:00
William Swartwood
b68903bc92 Fixed Themeing Issues. Ready for Release! 2021-09-29 22:40:27 -04:00
William Swartwood
bff901dd69 2.10 Finalized 2021-09-29 20:57:14 -04:00
William Swartwood
5dc4273d96 + Added code to allow removing downloading game from queue as long as it's the only queue item.
= Fixed code for column headers.
2021-09-29 03:52:42 -04:00
William Swartwood
a7622a8c5e RSL 2.10.0 beta
+ Added code to store wireless ADB connect command in StoredIP.txt so RSL updates will no longer wipe Wireless ADB settings.
+ Added code to prefer wired if wireless ADB is setup but user plugs device in via USB.
+ Added new Updater form for RSL updates.
+ Added new Donor form with listview so all detected donatable apps will appear in a list.
+ Added NewApps list so users can select free/non-VR apps. This will then upload to pre-determined debuglogs server (can be set in custom rclone config).
+ Threaded entire list population procedure so RSL will no longer freeze when the list is being populated.
+ Added Pull to Desktop option if user wishes to just pull an app without uploading.
= Fixed OBB drag and drop and Copy OBB button.
- Removed gridlines for main updates list for cleaner look.
2021-09-28 23:17:33 -04:00
William Swartwood
8ecad85d3f donor form and update form 2021-09-15 03:34:22 -04:00
William Swartwood
4955178426 Started copying new updater form for asking for game updates, will work on it this weekend. 2021-09-10 04:56:36 -04:00
William Swartwood
e211546923 new forms WIP 2021-09-09 17:22:23 -04:00
nerdunit
055481f8a3 Update README.md 2021-09-08 07:04:38 -07:00
William Swartwood
b84727fcd8 mostly done 2.9.9 2021-09-03 16:51:50 -04:00
William Swartwood
59097dd8d7 test2 2021-09-03 08:32:58 -04:00
William Swartwood
cc9dc38eb4 test 2021-09-03 08:24:32 -04:00
William Swartwood
85b7200d25 2.9.9 2021-09-03 07:57:42 -04:00
harryeffinpotter
349c9941bf newest adb, without directory 2021-09-03 00:19:01 -04:00
William Swartwood
7646edcd23 ninjafix dragdrop 2021-09-02 06:35:05 -04:00
William Swartwood
d5976fb379 dragdrop ninjafix 2021-09-02 06:21:40 -04:00
William Swartwood
a93666b2af RSL 2.9.8
+ Added timer that detects if user has uploaded an
update to an app, if they have it will wait til the
set amount of time passes to ask again.

+ When new app is detected user is now asked if it
is a paid app.

= Fixed debuglog/crashlog upload.

= After sending debuglog to server RSL will now properly
copy Debug Log ID to clipboard.

HFP
2021-09-02 06:12:50 -04:00
William Swartwood
e715bed254 aded code to ask every 48 hours for any new app the user clicks NO on when asked if it is actually a paid VR app and code to ask for game updates every 72 hours after user clicks YES to upload an update. 2021-09-02 05:24:36 -04:00
harryeffinpotter
1af1acea40 2.9.7
2.9.7
2021-08-31 07:17:54 -04:00
harryeffinpotter
060cc07592 2.9.7 2021-08-31 07:16:25 -04:00
William Swartwood
d3c012cad2 -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.9.7

= Fixed bug that caused error message to appear
when no device is connected to PC.
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmEuDXUACgkQx/Rql1Vo
czfmuRAAmwwn2DE1Qu0sDcWFhFF91ApPkjKNbWS1m+Kqi5Zu+Yb1XB3R4U7CVb8n
SfzR3khr0CD9EodJJZkwTmZJYABgNNy1jCp2HsEYG1DhJUfcJ1uIjdwvPqQpqex5
unGiLusEr1WpNp1Vuk9rBvvZOcH1ci/mRWqWdj6W6CLzZ5R3TeN0fKIFJ4B7ea7m
zRNGBA6i3YkP9jbUy16w8J6mYGzvHLg+ZfASG4jCOSyo4p+RQNWhDJEugjw+YImb
vQWiGedfr2c945Bo8Noaul8Pjh61ds1uN4AkobTVWNJIY/IpREFFjY/hoc/fhx9s
H3JPdJi3iaKq0dqhyNp8nlm4sv3y6bX9hXc0YWQMXjkzLg5ug1hHEMIIVk+tS+uU
bf8h+dVshYovE+N+4fjSz+hG9kzE9I6nvtI+gLpTJdxCyCXjjxlldrmuVE6nMd83
bDkKMJvb7Eo9KdIHmxs97TSZcjdfJEazzVXbs+dDmEJ9rGJYFIqydTQ/Pol4afvw
/tr0AmYUxCnkLgufmKjeX3Z/UVAzAs02MuT/Z639YwNoOtvg1bUzwWAzUQLwMdqV
P+d0xJQSEEr65GiwgdSsQY1R/uaGnggTXwIQwmkZPjJuUyNDD6h0zRcEKplDt7nd
jgjJJbVGLafcGLE1OahLlPnbMB24lWvfQM9ST0GuYWaINHCrQcI=
=XacL
-----END PGP SIGNATURE-----
2021-08-31 07:13:49 -04:00
William Swartwood
20e74eade2 RookieList FIX 2021-08-31 06:34:57 -04:00
William Swartwood
d3f9499f03 RSL 2.9.6
=Fixed obb copy for similarly named titles.
=Added packagename to shared zips.
=Fixed laggy upload when sharing apps.
2021-08-30 22:28:19 -04:00
William Swartwood
f99c57eb60 obb install fix and packagename to zip title 2021-08-30 20:29:03 -04:00
pmow
d7074325f4 show version on titlebar 2021-08-30 13:35:13 -04:00
pmow
1ba6fe5260 complete hotfix 2.9.5 implementation 2021-08-30 13:01:19 -04:00
konqi
ee6d62950d down we go 2021-08-30 13:03:01 +02:00
konqi
f8fb2b5483 version++; 2.9.5 2021-08-30 12:27:49 +02:00
konqi
164dc070bd fix false error of dns on start up 2021-08-30 11:09:21 +02:00
William Swartwood
7539b5c507 fix the fix 2021-08-29 23:02:29 -04:00
William Swartwood
68be50b26e adb2 to fix update to new adb folder 2021-08-29 23:02:29 -04:00
harryeffinpotter
6cbe06f394 adb2 for new version 2021-08-29 22:45:51 -04:00
harryeffinpotter
a9f2db6380 old adb replaced due to issue 2021-08-29 22:43:16 -04:00
William Swartwood
4a492ffcd2 -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.9.4

= Changed install.txt logic to search
for any .7z files and use Zip Extract
function from GeneralUtilities, should
fix version issues with users who have
a newer 7zip installed than the 7zip
exe included with install.txt games.

= Fully moved ADB folder to
C:\RSL\platform-rools and removed
adb folder from Rookie directory.
Rookie will do this automatically upon
first launch after updating. This will
make updating to future versions of ADB
much smoother. This should also fix issues
caused by having multiple version of
adb.exe clashing and fix AAPT issues as
well.

= Updated 7z.exe and 7z.dll on Github.

HFP
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmEsQnoACgkQx/Rql1Vo
czeKPRAAi0l9Ti06d48p0vd6Rziw3mHLVXg5dDOZWyrNULu1Y8axnSCVnZOVduVD
ftY0fVjTNNv7aU+fyWtH2Gr33CtJ6DvaQpj5RIsFiZpiTeDN3cOq2Ina55jtVvEl
ZrBVlLdXtv/L/W3ltrMzc/sNnJhrEfBXq7lNDFMj7IT31miphRZ1X9pgIdqGIpdE
gwcVxxkOu/5PXMzR3+eclpVePeagtJQNBH8dYUBQ9OziKObpzAfVYvoYS94MTYUY
ZdoHP5tfXEsuW0tQ6G1dvGrIi+ty8EJWX3GS42mkPW6xlyKbSOdu/IQa4KFPmpkK
7QwCMDzU5FgxJhg4+j35z/4BxJUzUHeTnp2xQYw/HE+GWdiJklIpZoOeqFQ+QLy/
6jwyhqcPD7cni2HMrCi4HxT0GcuVV5jrqqBAtCHdecglH90EMBCS9DrvmZyw1JgA
b9bCPQwWvVRLFw8HfiybiqVM98C2kW3TUsprGJOcnT6l1SzKlHEwiSMJSTVWshuu
bG8Q19gfhKSLEjTdjKVYV9WDHp8it+lrF6ETdneP4kcSsLhigYSgz0AeETx62Ugh
D13MyxcS6K9XqoRGy5Dr3gL/qkT0I5T6dsWL6vj9o317uexIO9sPkmrGTktbGUn1
heW2jD324PwyuuPxxoqM1lVKVquUIS0Difx+cqlJHY0lWqIk/Ck=
=lZeM
-----END PGP SIGNATURE-----
2021-08-29 22:33:38 -04:00
harryeffinpotter
b5c66b50b9 adb changed to one universal dir 2021-08-29 21:21:03 -04:00
harryeffinpotter
ebfe5ace65 Updated 7z exe and dll 2021-08-29 21:17:16 -04:00
William Swartwood
c242dc6f6d -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.9.3

+ Added new background color for apps that are a newer
version than the version on the list. If you see this
darker background please agree to share when prompted
upon program launch or alternatively select the game
from top drop-down installed apps list and click Share
Selected App to help the community!

+ Added logic for file size column, clicking the column
header will now properly sort from biggest to smallest
or vice versa.

= Fixed issue where Rookie would ask for every single
app installed on user's device. It should now only ask
for apps not on Rookie and not on the Blacklist as
intended.

= Changed zip file naming for new game uploads to use
Release Name instead of package name.

HFP
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmEpvs8ACgkQx/Rql1Vo
czc4kg//fgCG0ChVfIVl48I2dww4ZJmRI+sQm/S4Ry38M4NzM+SdKOQVnDFgZ68p
e/z2VqECc4wPlhqrxh+xHoMzlkfrz8LdpsU3cch6jo9uERpaWMj2X1E5jKFRc7iS
4/v6XvSBnp/ZSak3f7DUubNnsCcq6vDH4w1ugzv71AyW9STG7oUXQb+kNQNsQ1RO
WNJjO87RAlHivUuff7wRLlhVKLo21DvZ+MJRmMRzXH5tMxnEGxWeTOkAoWvsDjMa
1L6rmugC9K7X3Qqi09f1ZyUQG6F++fpoDf8u5QB96DegV+IZGjOsBcvL7wE9fdox
RxhFxZMsnQtO4kuo3c/iFg7ohxP+pQ5ayk0IzDRwdjfIYX+w+20LdkGpxsThxHvp
fWZBUCW0nwBFxBjxItXq8dIn7KHsDc3AkNcI2Ox+4eIUoKwuqzr0wCjPaB4dLLLZ
Ofa1tt25ugl7rSTI4doKEH1YmTlzdOuuSKaA2vIMH6L54gpy+h2DkdAQkSEl0PRJ
fApOwvUJqnat/Pipg2PTaJSiZd/lLoImWXdhOdlT2XA/9olD0oLGW/DmK+jjH2GK
Sxd+sLDHUDyujH263yemu1No7+3Zf+iyE+8LTikYj0mVG3SX8tfG/5cOXbQAp2pv
h9CmnbkKBVy8+RXMHKW8MJhsqCS2/wDIJSGYbxwAge3vSe70UWA=
=d6JO
-----END PGP SIGNATURE-----
2021-08-28 00:45:39 -04:00
konqi
bcfcc43956 sort by size 2021-08-28 04:10:57 +02:00
konqi
ab6507965b Now rookie fails if game list is empty. Also refactores some things to make it more eficient 2021-08-28 03:41:14 +02:00
William Swartwood
78b816c5e6 -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.9.2

+ Added option in settings menu
to automatically reinstall apps
when installs fail due to previous
version clashing with update.

NOTE: If your USB or wireless ADB
connection is extremely slow this
setting can cause larger APK installs
to fail in rare cases.

= Fixed auto reinstall for signature
mismatch when if ADB reports error
properly (thanks to ADB update),
this will lead to faster reinstalls
in general when there are issues.

= Fixed crash that would occur when
closing Rookie.

= Added check to see if AAPT was
sucessful in retrieving mew game
name from APK, if not RSL will just
call the name by its package name
instead.

HFP
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmEotsUACgkQx/Rql1Vo
czc2/w/8DkMuVYLb31J0WyqeIdo/12t+TO0oIbmTvGl8Oi1LttnfH1a+PUBYYXFF
VTEcBoEgGde/Jk7+AblKhzce+uQIzwzU9KC7HL+Ab/1AAj3tdczx2vE4uKTPfMUe
k8XuZz0Qe37IOVUgnHugQCac8jjaDCFXzwoIwADnBg7mW/4Ha+LqxJr/+dsMUnz/
WsenolRWWKyhTj3RHIulZu82yEV9Gw+KfIUpO/XJR71xNdCq1IyrAfBRT8KqgI3F
2IuX/pIqMSMvG1N62rWUzxD67BM5NbpjxiDZKg3LmBo39avqowSGclT7gOP+eiGY
eSvvl+zoI08ofL+8Vonhu/mwnQNUcGFQqqpwAvDIcgE/zVDM9uTVAOTRkrjW0ge2
xEUhpdKZVJ/UzbwnUEyR7zeAkt+sVkEbAD9My7NdQ58eD52F20+yYTAWC6CU8dB4
8QJbJBEzd0Pm23H7iY5F5/7UnPy+rxzfyezrJCKV5u+D65Fn4n13VY33CDxBJW4y
kmcpWwal81mbGgwhWpxK6+xy1V2oWL9PLN9YIkfv+n5/KlqJjUfj3wqah+aUG5qI
X/5mqLAWnsQcM/F0bZXzt3dVszInPHH7jjSjoyKOKQwfVxYrh6uuonVhG7NfHfij
LGs8d6O8osorkiB+2Aiziw8A+9o8fWLmfjeDhFcGc5YTb3eMxz0=
=XRAG
-----END PGP SIGNATURE-----
2021-08-27 05:58:45 -04:00
William Swartwood
ec32535419 RSL 2.9.1
+ Added blacklist implementation to request games
found on User's device that are not currently on
Rookie.

+ Added CTRL + L shortcut to copy list of game names
seperated by new lines to clipboard.

+ Added ALT + L shortcut to copy list of game names to
a paragraph copied by commas to clipboard.

+ Rookie will now maintain games list position when it
is refreshed. (e.g.: After finishing install queue,
when pressing refresh all/updates list buttons)

= Fixed issue where Rookie won't load game list properly
after updating, users won't notice the issue fixed until
the next update after this update.

HFP
2021-08-26 07:06:01 -04:00
William Swartwood
c607d1a2fd aapt badging added to get actual release name for new games, duplicate code put in to ask for whitelist first cuz im dumb. 2021-08-25 02:57:08 -04:00
harryeffinpotter
81c1e61d8b Merge pull request #83 from konqiDAM/master
I haven't tried to get an empty list, if rclone fails maybe it gives a null pointer
2021-08-24 18:09:31 -04:00
konqi
1dc0e38865 . 2021-08-24 23:55:16 +02:00
konqi
19dcacfbbe ask every 24h for game list 2021-08-24 23:52:11 +02:00
konqi
10d8498f1b add whitelist and ask for games 2021-08-24 23:21:53 +02:00
konqi
d3620bf033 happy pmow noises 2021-08-24 22:20:46 +02:00
konqi
467041825c not read header 2021-08-24 22:03:31 +02:00
William Swartwood
fa380c75f7 RSL 2.9.0 2021-08-24 04:17:15 -04:00
William Swartwood
2882737958 2.9 2021-08-24 03:35:54 -04:00
harryeffinpotter
aebc719f0b Added a text file that indicates ADB version. 2021-08-24 01:55:41 -04:00
harryeffinpotter
d21b72758b ADB Zip Update 2021-08-24 01:51:17 -04:00
harryeffinpotter
40cc97aedd AAPT.exe for drag+drop sig mismatch auto reinstall 2021-08-24 00:22:33 -04:00
William Swartwood
9a897954b4 -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.8.7

= Added code made by Ivan that makes
RSL load up much faster.
= Added confirmation dialog when using
Upload App button in case of accidental
misclicks.

HFP

-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmEhgj8ACgkQx/Rql1Vo
czc+TA/5ARiMcBdvxbKjB6jZKFDGZvEh0IpPyHRJzLpuZhHI+qR91P3UI9fXCrQ6
Vzi6Nc0I3YMhPIpNnp9uxWkNfeIUDTdBUi+wwIpMopupVxLCEJkR7ZGkjJHFWPYn
Hm19BLmmv9dctJwQlPLyaDgnb0+GKDRzZF8xHKBopbmModbhHwnt0UHTmodD4syc
DinfaVpM+RwGZOptUyzwqLR51aQb03JrbHS3gkB40EbsIr6brZd50lahSvVMW8W4
Btc3gf0rig+BrjXgUkNFlZ1PZ+jM1+12dWflYNvMYCoQ7bRc7Kds7IqCiPIweRF1
2YR5jBh52C2ChhY3sfbc8G6VkbXW7N6ktz/11mrIc7Mc7X4ibpsDzQCUyDiuXNwi
wm3ikq5liQYlM/I/awutD8yDcJPGEoUui8wYehwi23Pzujdh74bBYd9F6cQLg7Tb
AsRLvEDY/n7gsDzqdt/oqUkGtt0x4c0K5ZPEIe9pcQvfNln7j/Mf3BNC4HZRkeei
+FIyuhVf/w4dcz3xm1g5060N2eQLgXCk7690aak3l9ucghivUFlrKGe8vRk7qtE6
FebUemHtTgurKz1bm8XCZjfvq0IR7MY+iGgIfKtXbM8SqjxIDhE7P1QuGXeZD9y3
nkjY4+FHkT7BXSlOUpQ/6tR0wGYxtrxuNfwlXKGEqJlzPBbmCAY=
=Zdt7
-----END PGP SIGNATURE-----
2021-08-21 19:21:36 -04:00
William Swartwood
2e151c7137 Changes by Ivan and HFP added for newest update. 2021-08-21 18:44:02 -04:00
harryeffinpotter
84d6b14e36 Merge pull request #80 from konqiDAM/master
Rclone is async at start and refactored when asking for updates
2021-08-21 15:25:36 -04:00
konqi
97d25c8e9c Now rclone is async on start, so it should load a bit faster. Also refactored a bit the code when the user is asked to upload updated games, so it loads the list before 2021-08-21 19:44:32 +02:00
pmow
82cf72c677 typo correction 2021-08-19 18:09:17 -04:00
William Swartwood
3777b9237b RSL 2.8.6
+ Added Set username option to Quest
Options. This will work for all apps.

HFP
2021-08-21 02:54:39 -04:00
William Swartwood
2ed7034284 RSL 2.8.5
+ Added OPT-IN installed packages check,
this will be used to add common free apps
to a blacklist so a future version of RSL
can ask users to donate new apps.

= Fixed logic for apk signature mismatch
automatic reinstall.

= Increased timer for apk installation
failure to 3 minutes.

= Fixed blacklist implementation for
updates to apps already on RSL.

- - Removed 2nd confirmation dialog for
app uninstalls.

HFP
2021-08-20 03:56:48 -04:00
William Swartwood
98dc7f4738 2.8.4* 2021-08-19 20:24:08 -04:00
pmow
12efbac58a Update packagelist upload message, fixed debug exe from 2.8.3 2021-08-19 18:17:04 -04:00
pmow
00cfc35102 2.8.3 hotfix for blacklist not updating 2021-08-19 16:32:23 -04:00
pmow
31d352a79e Merge pull request #79 from konqiDAM/master
ask user to upload list of installed games
2021-08-19 15:51:14 -04:00
konqi
753e1620ed blacklist download enable 2021-08-19 21:48:13 +02:00
konqi
597caad073 Merge branch 'master' into master 2021-08-19 20:00:14 +02:00
konqi
5b04c0feeb adb 2021-08-19 19:58:44 +02:00
konqi
0d0ce30e65 asks user to upload logs when closing rsl 2021-08-19 19:43:07 +02:00
konqi
5117435c8a ask user to upload installed games 2021-08-19 11:17:26 +02:00
William Swartwood
383e721f4b ADB SDK Update 2021-08-19 05:01:53 -04:00
William Swartwood
1aaa19a969 HFP thanks update 2021-08-19 04:46:48 -04:00
William Swartwood
770df54403 Beta version with new game detection, added detected new packages to log file 2021-08-19 02:29:03 -04:00
William Swartwood
3e67b331ef Autoupload WITHOUT new game detection (for live release) 2021-08-19 02:23:43 -04:00
harryeffinpotter
26442e9516 Merge pull request #78 from konqiDAM/master
upload not listed games
2021-08-18 22:34:07 -04:00
konqi
3cd715afd9 upload not listed games 2021-08-19 04:23:33 +02:00
harryeffinpotter
0ecdaab240 Merge pull request #77 from konqiDAM/master
refactored codee to allow unlimited games upload in case user has multiple updated games
2021-08-18 19:07:15 -04:00
konqi
801ec1a874 fix or when checking null 2021-08-18 22:54:36 +02:00
konqi
ebf0f9372b refactored so upload uses a class and a list, now unlimited upload posible 2021-08-18 22:52:16 +02:00
konqi
06c534dc50 Merge branch 'master' of https://github.com/nerdunit/androidsideloader into nerdunit-master
# Conflicts:
#	MainForm.Designer.cs
2021-08-18 16:14:05 +02:00
Harry Fn Potter
04289885c2 Auto upload in progress 2021-08-18 10:05:18 -04:00
konqi
8e7b9490de Merge branch 'nerdunit:master' into master 2021-08-15 19:26:00 +02:00
konqi
cec1ff48fd added Enable Passthrough API 2021-08-15 19:21:34 +02:00
Harry Fn Potter
85fad7d3ad -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.8.1

= Fixed crash at startup related to Upload Date column.

HFP

-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmEZPjAACgkQx/Rql1Vo
czdfUw/+PiQ03p3FFwuVIndiDHKvwcjcewDWASYiP2KJkDCsbaEj0U5+ntpu7mxo
ytIFuNZd47CwiYGFXi6IzEO1n9yj4LL3ONnVEogZgd3bP3Bb2b28KDdHtDX3IO5d
wWle/WMksKDywqRyjJnaP9dh+7YS0C/49H32cFokF8S69stPwPYh9LSaf4ctK0/f
y8pHN7tgOkmCR8NU6lOY9OLepBZlv+PcOAco0nIz1zHtwdRGz2vjwQqAa8NShfEy
E/rjBhM37YBULf7cOayt0FY/rDYvw6Bj8B9ht0Bn6dmWgJrhfAj01utREL8HcmPd
rNDXB7UxuR56OVy1f1gh1Fzb5f62xDydfELd/0dFYE0oDcdkHjaEkT5CnAukBmrO
y0ntjawsy40dVAwPwPSAPQ+qP9g7mDZdbjOfgP4JNFWwnxgYLlTASoUMis/hd5mg
FLaFAiMM3o72Gip+gjMl3AiuwyefKoCGfAPgNvRx0t8R3bdLfVJ0KIPpgvJ7ZuD8
n4LEHLPoldDmoshdxl/BiQyLhaEM3W3qkwANVic6/8eCqy/N23CM74L4xgUaQDoq
dvkS3PKuhLvcYh4HkZNhALj7Mavvs0x8/pIGx8bK8JCNOhKX7rGylF6erDFfZ8Ym
T2vN0PjVYS/pF//qKHPyV5yVc5piudCB895LBNIDcJK1b67nUUE=
=+e9J
-----END PGP SIGNATURE-----
2021-08-15 12:33:16 -04:00
Harry Fn Potter
028c44ba64 -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.8.1

= Fixed crash at startup related to Upload Date column.

HFP

-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmEZPjAACgkQx/Rql1Vo
czdfUw/+PiQ03p3FFwuVIndiDHKvwcjcewDWASYiP2KJkDCsbaEj0U5+ntpu7mxo
ytIFuNZd47CwiYGFXi6IzEO1n9yj4LL3ONnVEogZgd3bP3Bb2b28KDdHtDX3IO5d
wWle/WMksKDywqRyjJnaP9dh+7YS0C/49H32cFokF8S69stPwPYh9LSaf4ctK0/f
y8pHN7tgOkmCR8NU6lOY9OLepBZlv+PcOAco0nIz1zHtwdRGz2vjwQqAa8NShfEy
E/rjBhM37YBULf7cOayt0FY/rDYvw6Bj8B9ht0Bn6dmWgJrhfAj01utREL8HcmPd
rNDXB7UxuR56OVy1f1gh1Fzb5f62xDydfELd/0dFYE0oDcdkHjaEkT5CnAukBmrO
y0ntjawsy40dVAwPwPSAPQ+qP9g7mDZdbjOfgP4JNFWwnxgYLlTASoUMis/hd5mg
FLaFAiMM3o72Gip+gjMl3AiuwyefKoCGfAPgNvRx0t8R3bdLfVJ0KIPpgvJ7ZuD8
n4LEHLPoldDmoshdxl/BiQyLhaEM3W3qkwANVic6/8eCqy/N23CM74L4xgUaQDoq
dvkS3PKuhLvcYh4HkZNhALj7Mavvs0x8/pIGx8bK8JCNOhKX7rGylF6erDFfZ8Ym
T2vN0PjVYS/pF//qKHPyV5yVc5piudCB895LBNIDcJK1b67nUUE=
=+e9J
-----END PGP SIGNATURE-----
2021-08-15 12:20:03 -04:00
Harry Fn Potter
7923adb4fb -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.8

+ Added modified date to games list columns.
= Drag and drop install will now show current APK name.

HFP

-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmEYjekACgkQx/Rql1Vo
czfNHA//duDNUGGUEaITUfCpqfVPkrd/S/EcPAztVw0AP4eq+f4Nn7tu5JEuhXf4
M2hg7kQhXFc6H3WadI/Code26AgL+qomLeR3nqSGwGt2jzBsTqXJo33KTAOP4/Br
GiyUh4ijPfDgjTOYuUApR/w/hNhYw/rWwiBrtyhnErGEQcge90U2fsJJdlO/zvdX
HfhbJnDCd6OKWlhAJDJyp6HZ4P0Nzoyo1stit+agLGa5YzT71ZYAVuKNVZtrrD68
ZZY3Atd+c1fuNRi1q7djC5v4KoikXAZ+xbfT9YN5WVQETKHIVtaEuw+LtCSIta8X
aU7bntHUkHghHgZagFp9eQ5442sHYQ8yXBhZ/nMM3prIllPHsCNSZ/Xun2FjkOVQ
cDPeSg5QH5eWH8huyxPGIahroiS99MJj67HqN+kWYwmKqL/FsP2fpq3KNFO4Myaj
vbT1QT2VFE86c34Pnvk2wFAZl0uh/h1gcH1ZLYHcibTsBlBCyVtAEu4P1d919P0T
8tWinS5F8as/VEsLp1gu82z/CyXPhJFgamZcN887C1HgtkyYQYqQnXNtrijjwR1x
07A+A1K6hFqE0v4k/IfT+rVlKRDo5PE2PGIcStTskheVeE9dNzpKoI/eKr3bFdLn
43MQdyJJJcc1Gm14zN6Oq5dIsMqSRyvmjy+ihIu+UDc777TIW2E=
=FKLT
-----END PGP SIGNATURE-----
2021-08-15 00:55:10 -04:00
harryeffinpotter
4b9d00a042 Merge pull request #72 from trainwrck/modified-date
Main table: modified date
2021-08-14 15:02:19 -04:00
Harry Fn Potter
0df0384f96 -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.7.2

+ Fixed automated mirror switching to now try every
single mirror (instead of stoping at highest and failing).

+ Fixed BMBF automated drag and drop (zip/zips/folder of
zips) song install. Settings is also now OPT OUT instead
of OPT IN. It can be disabled in the settings menu.

+ Dead mirrors will no longer cause the gamelist to fail
to load, instead RSL will move on after 35 seconds of no
list loading.

+ Simplified Wireless ADB messages and also made it easier
to enable or disable wireless ADB.

+ Fixed bug where if mirror failed it said "No gamelist".

+ Fixed bug where if mirror failed to load RSL would ask
users to donate every app on their device whether it was
actually newer than the mirrors' version or not.

 HFP

-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmEVXukACgkQx/Rql1Vo
czdLaA//ZBZ6ar5Qxlg5bZ409cSCRT+9JRBWkH7GRZZ4X2aejgesHXkcsF9L+NRo
cUjv/4s+lWCScY3EvzJg038Nr9zXUsZYdCfJuT1f3P3uBvEBP+XdtB0+1+bnSQnX
QhjtyP0WwJJqDhiDINB0DHycHu68sGmvaqZXoksWkdrtU5xDpoOw540nkBhIEHTb
anFNUBEdGsjafVj6ZhZ4dYOpewVsV+VJZbg5J7T7z/ZdYZunS6fTBjb7ho9CfnpF
GaTsGrwf03GcBBv13wYkl5UwO7TZiv6U+hUDOv0bkHGmaSQWbto+1Nt5GEo7SPMh
qOiVvLH0vr6RuLLeaKo2QGzKcOgr4sSSXfQFp70yiQGEP/UlIHGBXqRyJ0x/hAtV
yD7ynTpVpVw5bh939lpmjtw2VHNoOzOG9fky9tp61jQSIjDYWGP39Ntow9fkLWyC
Em4pHwHih1aWiHV91kzupWdSKIk8KKewcNIKXmxvV1A9zc6cxxyqnvYIcbOojg0i
d10UmyF+Tjm5bVH5U+AVwNeaFEZiI6d4QrLgTDFl3klyunCj2FWPdJmCvUPxFSzV
fRB5OWgRWnWB85tb63geNTJbbJURH1TJ88PWFn/3opgOEFTDu4UkRiuK6VviRQ8D
F95CsnuCrHRBwBozozv2YHJ8rCi61i+vmXi95jPAVMQd0OmsR0o=
=nH4m
-----END PGP SIGNATURE-----
2021-08-12 14:02:13 -04:00
nerdunit
418a5ebc33 Merge pull request #74 from PaulIRL/patch-1
Update README.md
2021-08-12 08:25:51 -07:00
PaulIRL
43e2343676 Update README.md 2021-08-12 08:22:29 -07:00
trainwrck
5402dadbd9 Main table: modified date
Added modfiied date to main table
2021-08-09 23:43:30 +02:00
harryeffinpotter
8e3faf4682 Merge pull request #69 from konqiDAM/master
Second column now is wider
2021-08-08 15:14:06 -04:00
Harry Fn Potter
26db46e844 -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.7.1

= Fixed apk path for in-place reinstall. Should work correctly now.

HFP

-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmEOGwkACgkQx/Rql1Vo
czfPHw//eXtKkM1g/mjz43/BLTjhT1PTrwvolCmds+LzMTHT1EfDyouJBZ2XxnXQ
NiZkk9SR3m/VmJSr0FO7p7F9Ab07xEUIv280C7Mbqyr0KEPiQTvrlnL/eX4sx/Px
1pmDCuPUL42u2zo4famGxDvduoVToyPrIzzv3QYt8qJGEAYRG4gtn26SB38+JslU
eU6hJkB2E7BNZXRtpiDM7rsejOtzsIROdLeIztSyIyTl/sY4Az9nfgRiJFyHsPsJ
ILRRkquCzkXbzzdjS8AqPPIzt+JZdp+ZZS2H3gSeld4We8rkm5CzBef7pzeP6ZKZ
WGYnw1Et9dtflSAmfDb6i4Rr5Y3mPLk2OzIeYebZ0fCGmyrpJ7QlRfYqJNJ7JMH9
DOmsSNVUvFRR+b0FnT414uSg4Q79cYXWsJ/Wi28PIOQF0nQwtPIKly2x1kB4NE7P
CItXy8VJ//q0/2ijHB3ZQIiMkj78TVePCAC6TQT4RnBgWS0HeItBKZJYCcyvhe72
6H9qzOVsIKHgxnfbRON9tZOWxpxIdnbFsaE5TsiXp+80SsaHhxcxuAyjh0M0CaKu
lh3z9FJmZW8CrxIA1G1sQePxqF3iObVhqJPqp8G7N6UC1wBnhJWmnBKTYm1iHs6P
pj8ggpyFTT55UxGeRklLUyoSxSnqSE7Oj2zluLvBFHPi1KAMlEg=
=HVQ5
-----END PGP SIGNATURE-----
2021-08-07 01:42:59 -04:00
konqi
88bbbb2ced Merge branch 'nerdunit:master' into master 2021-08-06 20:54:26 +02:00
Harry Fn Potter
d02505b172 -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.7

+ Added BMBF drag and drop option for Quest users
in SETTINGS menu.If users drag and drop BMBF song
zip files onto rookie either one by one, an entire
folder, or many zips at once Rookie will automatically
extract the files to a folder then push the folder
to your device then delete the extraction folder.
+ Added automatic timeout during APK install that
will ask user if they'd like Rookie to automatically
uninstall and reinstall the app. Rookie will check
for save data and copy it to a folder temporarily
then put it back after reinstall.
= Cleaned up startup tasks so booting Rookie should
be faster.
= Added automatic deletion of previous crashlog to
decrease clutter in Rookie directory.
= Added automatic installed app refresh after drag
and drop.

HFP

-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmENfVMACgkQx/Rql1Vo
czcxaw//bTVxR5gt1F4c+gDT80dVfJa1Js3h5vSjGgCHjQdmsU6eaU5BQGoUorkw
6ewYF48jt84xDNj/VirkeqHV95mvsj9sksi+mxvELnAa0NdRVYj0DCqZWB/mNDfx
lYZGp2SwiGK3a6KfMpPcbi2IEWqc9ZWpdazIDy6l3oGSBAYaoVqfjl+CJQQpomIJ
x7f67Ei633dyhu8+0612LVu+tZ4ibN2pAAu1Dd5+uvlODUiYLPadhZkLwk+CYC6p
ifs/1qB/fsXJS6YJyPCmuZCbYN//jLCD9ECQ5cqZbTTU82Ub2lDE+TcdO/CsxSxU
pueIxZ+7C5TceiNzsDfKfeCWu65hZlBW1xKReDtLPFzZdZuyXYTI0v00kvapwJIO
C9Eq2qxZvdnp05tw8nY1V4Ae5Jl6oLN0BwWSZv50NhQ4Dpmbl8pemk1r5iZsRhGA
MWQCytBCaX/3EpJda8ifGJxj3kOno2ewbwXdZEMN7QqWt1scN9HxNXFWEJwO29HV
pT6ra3T0OrOBLmCQGt4w71HGFK2t+c7aPq0yI/sayguB9zjR/GpTyekg7nHXZh9g
c+WD5umN2xA0LuLjvvePKhhKTBT6ebmQJh1CRNIRtA150ULbnTAIpZJTDZyhcLXi
IUfWyWPA4G3GsRUqGWf97O2G2bIbbIzLeg3uEdxF7T9LOigYgpU=
=Rwj0
-----END PGP SIGNATURE-----
2021-08-06 14:41:09 -04:00
konqi
cbd54f7319 column width set to 270 2021-08-06 16:36:18 +02:00
Harry Fn Potter
258c251567 2.6 2021-08-03 17:57:33 -04:00
Harry Fn Potter
1f851470e0 -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.6

+ Added No Device mode for those who wish to download games
overnight without interruption.
+ Added automatic config update at launch.
+ Added a unique HWID generator that will generate a unique
number based on your  PC's components and then encrypt it
with SHA256. This is so donors can be anonymously identified
for the purpose of future benefits/recognition. Once.the HWID
is generated RSL then creates  a text file named HWID to put
with the  clean files in the zip. Users can see their unique
HWID at any time by pressing CTRL+H  in Rookie. Your unique HWID
will always be the same.
= Added version code to any zip files created during clean files
prep.
= Added boolean to check if user has been asked to send clean
files, if they have it won't appear again for some time.
= Fixed OBB installation for Install+Download.

HFP

-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmEJuZYACgkQx/Rql1Vo
cze1yg//Yyw+tRfA3hLxLQejXhj/LyFsGOoTCyqTVQDuUtxZxyMAFUN3SCd5sC3h
byVpcoDMaBMGoOgE3FnpEvObd3SHVaB3xqw902saHPAhzuujv4Ru25ou+xRDU8c6
+xXPQ3O4ocgHwlFP+nCVx3eXai8Dz/2OUc6JC2PfpjU/xj3B6cs/ExSrxoHqyCJL
g5lntpE0dFQboe6gu7uEQn2Qal1Gsn2+MqCqQZh1dBIpF/gEPD8/q40St/lN7cdY
jm1lVxW3By7muxlh9wcRYsxQk78cb49+Vsa+EcxS6GK0m8Hibp8nS7RoHFMBtC1i
hgIZW5lt1cCj5WlwI3/t+vLR4Ia/eh7Vi0EiyVGhiOIQvCR2+PpkWWrVDe3zoXUX
n68zFZlL2yoaCXg5AWPp3KiQff3uxlBD58fzNZRtKzjFPNmcYR6x7PcHfai76+JD
aE36aC8Q3aMCaIaH+gH+eAhl+mzyX1Vhdl9NLLHLUXZwBnSA9drMHJsVMh6SOQiC
VMiv1vMXJllKq/7W7EGr1fWFGuPJk6FFjT1g+3+2d71yVKyPL+99JQg7UJzp112w
7ZUAD6Wt8lpW66I2zpcOBPwpoAlxg2uJy2Nc38axfrBpHsscNrYIXFMHEprLG+Xk
PqnfDHPtP1sUxe2uiZTQj3JjlEnb4ES/xBdvSPacc53+fBPs0yM=
=BAUp
-----END PGP SIGNATURE-----
2021-08-03 17:53:27 -04:00
Harry Fn Potter
0070e44917 -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.5.1

+ Added PARTIAL SEARCH compatibility to Quick Search. Quick
Search now works if users type one word or one part of a word
that matches a release. For example if an app was named
"Supercool Awesome Application", typing "Application",
"Awesome", or even "Cool" would work as it works with parts of
words as well.

NOTE:As a reminder you can open Quick Search with F2, CTRL+F
or by cllicking on the Quick Search magnifying glass icon.

+ Added a blocklist for apps that can't be updated for various
reasons. This will prevent users from being asked to send apps
that cannot be used.
+ Added .png support for gameslist thumbnails.
+ Added tip under Notes section at launch to remind users they
can press F1 to view all of the program shortcuts added in a
prior update.
= Some minor syntax updates.

HFP

-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmEGqr8ACgkQx/Rql1Vo
czftJA//ePxg5+fuAoYNfl2D8yKpzhkvQldkdZia1GJiHBb4CJBwdhMDpDeNhKzO
ziJ5yBNLqx6wWSbHV70PF4fagASA81FeLbJxdJX4gFgtX9MpzoR2ppK+Gy1UYVGw
ij1fjQIIIHXyAVDgaS4DcRQrgxtc6R2G3iey3EBaxtaWDh2cR3sWdR6QMcZSJ/0S
/QLXROCCCIHc1mTsf37b2qQVQ/v2Yk/UAGSV6/VdN78QE7Z8dPqCLZvM+10mfXTE
AOJ6WkZBVA7HkpRgEGVtTLor+kwDROshkq+eM7yMREFv6GwHtd5H0hHGhRr/FoVW
E99TfabD9Rd5P7tvcYm2c6Diu7yz2ai7l7N6G77wr+lOfsmIniHeUYEWsPOcZHQH
P+0Xvx84cHj6Dd2+zfCWcI/jPamfgzgxrBS/pSXJ5ktdrCiJRnqeU9FhJxhGF002
7+iIZTumhAU4ab8LhSzkMg+hwhzTrhmgbeZE1jdwLiLQ3vyYszEB5zflubt86pLq
WXVvupQKtCGek1B3ETvU2YzNaQe6vVjqrh0sHkwgYgwfhLg9+gc+c3Rtq6ighqfc
KNDocmVcNOJMNkre/DOTmVU6ULg22pXlj0PgWkxph/vt1SegMbPMVGUJD0ncIqL8
eQbzNOltkzALWel4S2Itvv/JgoDU1YDupJmZNh66ihKBuIdcT0k=
=wU75
-----END PGP SIGNATURE-----
2021-08-01 10:27:04 -04:00
harryeffinpotter
596e126d2b v2.5 2021-07-31 11:52:12 -04:00
Harry Fn Potter
5a6ccf423a -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.5

+ Upgraded Extract APK from device to Extract APP to Desktop. RSL will now automatically extract the APK file, then extract the obb if there is any for the selected app, then it will compress both into a zip file and place it on the desktop so users can share their apps without issue.
+ Added automatic detection of apps that are more updated than the database user is connecting to, and if they are a pop up will ask if user wants to share the app.
= Fixed issue where copying a folder containing an obb folder via drag and drop would cause the containing folder to be copied to the obb folder as well.
= Changelog.txt now only contains the 3 most recent updates including the most current, the older changelogs can be found in the ChangelogHistory text file.

HFP

-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmEFcMEACgkQx/Rql1Vo
czdoWA//cbngmK/it4fy6Hy/5JMGubJvCb0Ar+KY0UCO5FR60Q+ADCrkhPDEaWif
xnM+h4CLqyHUD0NipUxsjZ4j25VRMA20eTy3jiLKSZKq6P6KD+oRv0Tx3vTEs61U
wJdQsWK81cC9+BnhCBDuWpDJ/nCqO9ZJBYdKX0+KuYTVZwaaZSfMXD1dgQsrCQCq
zU5kgrsl+LDUJknnpfda521pbACgHlZ/Cb3bztqgnSviF38jFuYsJe7hJVjsINYz
j6HtmT+vn3Rxhk6uRTtUNhM1YVvuKzsZtUNvqd7cjT3FJXWuuoP8mgdauSahVnpQ
jMLu/dMLx0PkRXox/D8/69tDA1dxNZozXvws0OQfmNlzLfLGPZdVSWVjfU+P0feA
hMZiVICyAAJPnzfNAfVq2yDpU5wJbAhj3qG+Lj9Ox4dpmn7AUrjoUCFZxhZIZJi1
KKKz+7Q9ZDkcFODCUzZAdrI0MP7MNrsfPl8jxSCLYFe6LUIOfTAakBkxOY9x02lF
x08jfbpAsJHU3Wm8bruk+nkB+65J5kqsLkJk2n4yrkW3QtIo9xul/z9vgZKo75S6
LkPCpNo2w5gtx7+8Jv65Z/BlueUVmVWUHhQ2gHRkXenkg3McEZpqwKInqQSZkHpZ
USckVtW+iteVVR0yf+hFPRGp+Shz6/GIn9YnwVQCT2DK+ih9zD4=
=0bPq
-----END PGP SIGNATURE-----
2021-07-31 11:50:15 -04:00
Harry Fn Potter
efadb70d72 -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.4.8

= Fixed wake on wifi prompt after enabling Wireless ADB.
= Gave user an option to back out of automatic wireless
ADB connection restore after device is rebooted or has
gone idle.

HFP

-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmD+JLIACgkQx/Rql1Vo
czcd7Q//TbRjq1ePpP3PFUm+IDFt4HRnwFOyuS8/LVOh1FPUc66vXEh/BtzK5xgN
ZPe4QRojSnZPzizkqzMnsXxhiFlwu+3Y3uA+S5JmdisfE0A0DaU9JBNv0IPCsQBG
A3azgQrN/8waq+R1A/uK9mvcO1033cTV8SkTRIWqBSjcOBT+hdvL4UngAPgrYUvp
GqYMx4/qNq8SyoYkY3VCN4LtX3iswPQkHxjwT1LB08gltMP7Hoxpaz5B1QJiC0gZ
NRAWEOMSuhdZC1X87lo08hPMwUJrt12PDzHrrDjOz8eUSUtEqjqKHY0HTpyJ5wvK
i5t0hvVC6bwXIRKeI7I8RESqGYoQYAoQDOHWMZ2fgclYOwjevMaE/1qbBpOmwBgz
3g2UM7OH5DU2b+4Er+lrHEZSplVdC9fjifGEUjm8sD2Q+sW8PaJV0wtAMYy9mIzd
pTrQE7rYGYllPJ8XAhzzLtGYXCwlrBeMdYhGjxmVet4+dtMEXAksemIq24PNxp2M
1FEcM3+jLpi0GJ+PBF9qSR70ZrKwmQcgu2miVVxoYrRA5WADp3fI5VxbdAqxwOgl
3SrI4htTvu2mBQ5x6wiWVXsWbEsDvCnx2+46FWVutdEc0QKGsmcm2znb6VjBc+Ho
w3osj+rHbWYArCWjEYdezsP1aPRAs3Uly5tJrg4Q0wd9lxJHiaU=
=ljkJ
-----END PGP SIGNATURE-----
2021-07-25 23:00:26 -04:00
harryeffinpotter
b1595931c4 2.4.7 2021-07-25 06:13:39 -04:00
Harry Fn Potter
1e3a9a5a84 -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.4.7

+ Added battery percentage to stats on bottom left.
= Fixed using ENTER key to install option.
= Fixed  using Enter to install from Quick Search.
= Fixed install feed message when using Enter to Install.
= All important message boxes will remain on top
until user selects an option..
= Settings form and Quest Options form will remain on top
until closed.
= Fixed changelog formatting so updater box isn't so wide.

HFP

-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmD9N54ACgkQx/Rql1Vo
czfz2g/+OEMxQBKrmWaeXpe4CD1jXs92P/JMU6DPBepNNiKtE+ltX1iG/I1Ft/04
JzFLJqqQsVKP9HceZBkNie87C4YdTu5Z3LUEOS626IVlEx1t0cxXilsVeXWrFSSJ
IyAgRbXZKQRVF+nhR0yXLUSjAQqlxLTuu9kxCXOHBPSqw+MmDCukUat9SoJUFpRO
+bteP/Y+ihUHHaBcD9AizJJpTzuCgKrKUtyNDPcXPIcOoosx459+tsG+mOAilEay
UHMwQoe21//mJJSsaCPPNYOWkY/BV1JNA4dDE+eBoK02U+SXVyh50eO1LFlV1wDd
0XL0O2npdVKjhszpWk2LuyErhaRB6EDMJvNvA8RgWHMF8gR20NPPM183ui79XRr1
bPpAXWbNEVn1OOOcAD+wTwLB6BCc1ryuI4zTHejQufWLQdVMaWKxhREo7bqTWTh+
4WPLZEC3JOy4uODb2tTJLz0qCBaxXNdr0usVxbO5OaGSKOBsJ2GsBlfBTNCr6If6
tMMi2DXSTYAuIgESCqFo4cEz2YGEmOrIqQ7nx9tBhGV2bfnClBbbiYAmM15ifRHG
QHmGhak7h/NFTawOGrS4BV2HfViBR1ef7FJ9v3T4chVJN+ED9tLpfMNKuTLOZ3at
dwUOnA4jfDJL2g5YV8ebiAzPS75FSSrQ2wfcyYYyywTdlBc5K58=
=0eX4
-----END PGP SIGNATURE-----
2021-07-25 06:10:49 -04:00
harryeffinpotter
e0e9a68cb9 2.4.6
added colorblind mode.
2021-07-23 18:35:14 -04:00
Harry Fn Potter
19c90f0ad4 -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.4.6

+ Added a colorblind mode for the update list. It can be found in the SETTINGS menu.

HFP
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmD7Q1UACgkQx/Rql1Vo
czeuyw/9Grjaeaz0fpM1eq4luU2sKUGReCEjRzZlWv++KqJoLY5BxUZ7rkrrb5jD
oZ5yK88er1lgsii1YBMBjr4ub7K7LEJ13KCagCUJX2wjG1FUGbGtiIdfFf+ryFky
2+OfA3thCxmVCbRRHKzYQ7fks0rd1D62S4qQO/xFE1QbckPSFInLz6wno++WP4iT
Ht7g4us8IKs51kGbjGx5EuA9suG5bDQrrx6QoUMORIEGMVJdoUX286KIUpS6iy1G
nMS/kQePg45+2Y6GDDgCtfRDjqie4J2U3UpXaBQnYAr0tcW116Q54VB9d6aArmKu
DfC/JnUCes3eN9Y/vxHXytoy7RLy+VRQiZw0C6deX4N+XrmPXSGsZboU/YMpFKsV
/6q5EXc0SPDK409HSiFCi5GinIp8mnuGJnIRcwWFFxU9kYBMOjTBA2wHSEcneoZa
8aDYd0fTLNyh/W/vwYnH989ihXWg+reHAyeGym+0u9TUqeWAwbGzyoYaLCri36pK
Pp0pQGOQgt/+s+9evKtMt7YcvnadvdoA2FowVp71HlR23fZb9yLctRPI/cJd6U57
3DBOiPJDb8U4pCo7xf3GZgluBRc/KMGqMysi4S3Hpez2yZ68gvGJh5F7f8OVyGHt
CwVhODnbDlX5W8hYMF23K4B8zSaZpnVJQXYQS5SCzPagg/Pbt8E=
=Glvg
-----END PGP SIGNATURE-----
2021-07-23 18:32:26 -04:00
harryeffinpotter
bcc7499807 2.4.5 2021-07-21 16:09:57 -04:00
Harry Fn Potter
0716ca27c4 -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.5.5

= fixed issue where removing userfolder from adb command changes broke install.txt

HFP
-----BEGIN PGP SIGNATURE-----

iQIyBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmD4fh4ACgkQx/Rql1Vo
czdrvg/3ZzJNCs7+G7Nq4jGNWokm8xNPD8b8YY1zsZy5Vbort0OQVwL875E2UvpS
HDutQWKi9LgUsKRqgXm0T/O/11jTkYqGWowFa9lvuhmFYNiHo+GXirA0Mcsjoxbm
mNp8PI3TE9NjuDj55P4LXXv4v6qngzXsD1A+EqXq/43nKhuv/QWq2rrG52P24MLE
cCK8O1Ir2S0ZpZH5Q/WoeiBJRID+j/ydVuJaXpC95SoBN9TmbSEnAh65gZ5P4eiJ
oYSvgyNtG9y9I5vjTOR3P4khhWhA0NQBNiQPPdi3LuucYcTdKsQb3y3BOQ8ei3Yn
tHxA73Xdfk0mbSlk0GggSUKOLcD4Ij4QQsva4YwG786J2qGf/QvKC0ujf5B/ezzC
bPmWPVl9qJJ+2CVBZ1DuUP/G7+teEz4IGo9VTgiqiwUT61mRv8o7CIdG1RYCNobo
yMX+cHB08KgUb8NE26NrjEm6mqd4AWgvL4Zj/OdKTBdZ/R02WEvQkJgLRji2AZqn
f35TyYxSW64vhOCk1nMpcSJzGfRV0MwA3UU9WOko33ukikXp22dTXtT0LMY2qqZI
vDS7eXHHLI+ctb+gVNms7FagQbDYChSRKy0S5EX6e2EkBGz6jVxfg2lXNfkTHK2V
K8YZkfI5Rx/PKc3pNDIIrdx6h/ya8eEiBCJZ6scLbJvyzNYpfw==
=jTyb
-----END PGP SIGNATURE-----
2021-07-21 16:08:34 -04:00
harryeffinpotter
652d7cb290 2.4.4 2021-07-21 14:13:59 -04:00
harryeffinpotter
919286f85c v2.4.4
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.4.4

= Updated logs to remove all instances of windows user folder for privacy purposes.

HFP
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmD4Ym8ACgkQx/Rql1Vo
czfCuA//elSOQ1dUQactp0BZ7cdpDcgoW4YwpEzddc96PwVYULsRxNnREIWKTrjg
2IGDhR53M9kTpsQ51lFwn+IAanKSl7dTsMh+yJDuZenKJga6eECaxiDHPu6JWnQY
Hy/zA012f8qoMfK/w0Ti2X3ITMtHGDqhewU9KdNGQNuAp8bH8o6lyqV01QFvyts+
FTGVB/76I9/OpffDX3fTxTS8PYJvGNAArowAOvYFhf2JcjdjpULo/oH9BVAoCoGT
8XmTYU/rAukmpWy3ensq0FxtjdD/tJt2NKgJcEzJDpkuxMYdzWUW7Sum+mexyX7k
VErW/s3w3rUHM/xW7NZgmRcpAqSRwpvvrSFxAKmGXNxEYmWrJ1jM5Nt6zjxzgk8J
0W7v1M+e/qoUz1HB42Fk++lWENhvZcjJkdRIh8NfjufiA5jc0cZXyZMJClXvMA1K
kG2PP4OqPnUNZdSPUafSGGjKlkr35tRV8SY1mx9phnMGgMZaVk6V0TPKVjHTfSsV
FBlFBuUl4EoQUz5bKUNhVszz3Y70aTBRk2M31GjojaBpOrHIj2Z0YiWoFp5maD2o
cfQ4HjhHAQAqyoD4ID9phgP9UOa0T9eoc1otkzCfRMAkBPS2DysFPpqNCdKOuuJy
gtfsvQxhALWe1qdDohIOekb2exmKj4NREGI5UjtgT+7Uf13s0Jo=
=8ePT
-----END PGP SIGNATURE-----
2021-07-21 14:11:27 -04:00
Harry Fn Potter
3be98530b6 -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.4.4

= Updated logs to remove all instances of windows user folder for privacy purposes.

HFP
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmD4Ym8ACgkQx/Rql1Vo
czfCuA//elSOQ1dUQactp0BZ7cdpDcgoW4YwpEzddc96PwVYULsRxNnREIWKTrjg
2IGDhR53M9kTpsQ51lFwn+IAanKSl7dTsMh+yJDuZenKJga6eECaxiDHPu6JWnQY
Hy/zA012f8qoMfK/w0Ti2X3ITMtHGDqhewU9KdNGQNuAp8bH8o6lyqV01QFvyts+
FTGVB/76I9/OpffDX3fTxTS8PYJvGNAArowAOvYFhf2JcjdjpULo/oH9BVAoCoGT
8XmTYU/rAukmpWy3ensq0FxtjdD/tJt2NKgJcEzJDpkuxMYdzWUW7Sum+mexyX7k
VErW/s3w3rUHM/xW7NZgmRcpAqSRwpvvrSFxAKmGXNxEYmWrJ1jM5Nt6zjxzgk8J
0W7v1M+e/qoUz1HB42Fk++lWENhvZcjJkdRIh8NfjufiA5jc0cZXyZMJClXvMA1K
kG2PP4OqPnUNZdSPUafSGGjKlkr35tRV8SY1mx9phnMGgMZaVk6V0TPKVjHTfSsV
FBlFBuUl4EoQUz5bKUNhVszz3Y70aTBRk2M31GjojaBpOrHIj2Z0YiWoFp5maD2o
cfQ4HjhHAQAqyoD4ID9phgP9UOa0T9eoc1otkzCfRMAkBPS2DysFPpqNCdKOuuJy
gtfsvQxhALWe1qdDohIOekb2exmKj4NREGI5UjtgT+7Uf13s0Jo=
=8ePT
-----END PGP SIGNATURE-----
2021-07-21 14:09:58 -04:00
harryeffinpotter
b4e21a9d5b 2.4.3
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.4.3

= Moved nouns.txt library to a more suitable location.

HFP
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmD4T5kACgkQx/Rql1Vo
czd9Ew/+LvPnoSuNl3ynFRAjTs8Yo1r2FCY82UfWLLddYTc4waH4s2HZQkUyFBGo
emkvZUa/TZuYGzNLAB4JpPDYYONyF2LmOx8z0Pwtp1+RoEJbGe7WsZa3oF+PQ2AU
+1+hE0u8RW546uaGdzyToX3N5JnPAA2MctG0+3X5J+k8C+CHWgMJyoDnoz62b2Qb
gER2HZMBlrebJwSJto+tKeLRDk2FI4UmQDu2CQpvlwkQMwa98ORRrulaO4LOEH3I
z5S6WyxlcF0Cy4dwD5PVsx4YTHn7Rjy9HHniMYe+vv7LQOz6w+DZZQv6x1BZ2xSF
ZqS76T3x5XAVpcH2JX7gce7dTFVpft/P/C/vOVAa8E2pUp3stuUFoIyVoeWPwgvJ
3wkJFeoG4umlLQZY934Mb6Hb2tu38xlcQd/xsO3aZRPUOZBuYMcbpf8V7qv7f+p+
YDSXwI92ostB3RNkXlUy2onSWrXoLQF70lq3zaNcUeyQt6HILc80h1pXC6VWnITz
RRH9ojt6CWLyzIXKgqdKWKVJJQ5GIR0YITk4kMyGqM/CP3pUPCUKPAVbWK4ajoGA
1A2JBniQwYmCj+vkfQDpwkacK3zmYy/JRpTYdyj7BuOXjGnPpberjdd+jpgGBRCH
IeOpD/kmWaKm5lH1KmEOPGe86bhpQstOba7EsiBM4/x6QpoqVOU=
=yWnO
-----END PGP SIGNATURE-----
2021-07-21 12:51:17 -04:00
Harry Fn Potter
6d3885dc48 -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.4.3

= Moved nouns.txt library to a more suitable location.

HFP
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmD4T5kACgkQx/Rql1Vo
czd9Ew/+LvPnoSuNl3ynFRAjTs8Yo1r2FCY82UfWLLddYTc4waH4s2HZQkUyFBGo
emkvZUa/TZuYGzNLAB4JpPDYYONyF2LmOx8z0Pwtp1+RoEJbGe7WsZa3oF+PQ2AU
+1+hE0u8RW546uaGdzyToX3N5JnPAA2MctG0+3X5J+k8C+CHWgMJyoDnoz62b2Qb
gER2HZMBlrebJwSJto+tKeLRDk2FI4UmQDu2CQpvlwkQMwa98ORRrulaO4LOEH3I
z5S6WyxlcF0Cy4dwD5PVsx4YTHn7Rjy9HHniMYe+vv7LQOz6w+DZZQv6x1BZ2xSF
ZqS76T3x5XAVpcH2JX7gce7dTFVpft/P/C/vOVAa8E2pUp3stuUFoIyVoeWPwgvJ
3wkJFeoG4umlLQZY934Mb6Hb2tu38xlcQd/xsO3aZRPUOZBuYMcbpf8V7qv7f+p+
YDSXwI92ostB3RNkXlUy2onSWrXoLQF70lq3zaNcUeyQt6HILc80h1pXC6VWnITz
RRH9ojt6CWLyzIXKgqdKWKVJJQ5GIR0YITk4kMyGqM/CP3pUPCUKPAVbWK4ajoGA
1A2JBniQwYmCj+vkfQDpwkacK3zmYy/JRpTYdyj7BuOXjGnPpberjdd+jpgGBRCH
IeOpD/kmWaKm5lH1KmEOPGe86bhpQstOba7EsiBM4/x6QpoqVOU=
=yWnO
-----END PGP SIGNATURE-----
2021-07-21 12:49:13 -04:00
pmow
8aff424ccc add nouns text file 2021-07-20 08:26:26 -04:00
Harry Fn Potter
f50ebdd28c Fixed QU Settings config installing when not set. 2021-07-20 04:00:31 -04:00
harryeffinpotter
5608565275 2.4.2
2.4.2
2021-07-20 02:18:56 -04:00
Harry Fn Potter
9f783b3b1a 2.4.2 2021-07-20 02:17:18 -04:00
harryeffinpotter
02156985aa 2.4.1 2021-07-20 01:26:34 -04:00
Harry Fn Potter
a81cb1e496 2.4.1 2021-07-20 01:25:09 -04:00
harryeffinpotter
688bf0a274 2.4hf 2021-07-20 01:23:00 -04:00
Harry Fn Potter
5d84133c3e Fixed issues with debuglog and crashlog upload. Both can now be shared with moderation staff without issue. 2021-07-20 01:16:46 -04:00
Harry Fn Potter
8e9e5cfb88 crashfix if no nouns.txt 2021-07-18 23:06:05 -04:00
Harry Fn Potter
3d19cab51c updater wont work with 3 digits or HF for some reason. 2021-07-18 20:18:54 -04:00
Harry Fn Potter
e8a3f5e34e 2.3HF1 2021-07-18 20:13:44 -04:00
Harry Fn Potter
e1ed9af1d7 Added check for nouns.txt 2021-07-18 20:09:00 -04:00
Harry Fn Potter
d154d67c57 BeugLog server support, MAJOR OBB BREAK FIX. 2021-07-18 19:42:10 -04:00
Harry Fn Potter
fb9d4ab0d3 Fixed debuglog code issues until server until debuglog server is live 2021-07-18 01:25:27 -04:00
harryeffinpotter
2f34ac8848 Merge pull request #67 from SamHoque/master
bugs/download-progress-bar-not-working-as-intended
2021-07-17 20:13:25 -04:00
Harry Fn Potter
9e352c3144 changelog to add 2.1.1 hotfix 2021-07-17 15:45:13 -04:00
Harry Fn Potter
b07e6bc12c 2.2.1 2021-07-17 15:43:10 -04:00
Harry Fn Potter
f8ee9b493a -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.2

+ Added custom naming scheme to Debug Log. Debug Log now uses 2 random nouns as its name when generated.
+ Added shortcut commands. Press F1 to see list of shortcuts.

  F1  = Shortcuts List
  F2  = Search
  F3  = Quest Options
  F4  = Rookie Settings
  F5  = Refresh Games List
  F11 = Copy Debug log to Desktop.
  F12 = Copy Crash Log to Desktop (if one exists).

  CTRL + R  = Run custom ADB command.
  CTRL + F  = Search
  CTRL + P  = Copy package name of selected game to Clipboard.
  CTRL + F4 = Restart program.

+ Added ability to upload debug logs to remote server (the server should be updated to reflect this sometime this week!)
= If Install.txt exists apk install and Obb copy will only be run from install.txt - this is to prevent double copy of obb files or double install of APKS.
= Fixed issue where if user started DL+install on one title then quickly selected a different title in the games list it would download the newly selected item instead of the originally intended item.
= Fixed issue that caused download progress bar to not work if multiple items were queued.
= Fixed other obb copying issues.
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmDzKesACgkQx/Rql1Vo
czeiXQ/+OsH5CdnShheJR85/uIkVLGDf75Hkybj7k+5gV688imYwWbCRarudqa8k
aVC5g69mdKD8rD8DzWjccpFwxvOSq5MRQMWB/Nl/M2142Rnr1M4WdP6xEmMq1qmQ
X39T3tdpgxxKyDPIt4EX/iLT6qSHdB69qFtMeEoOsHmisqGDFDCHGs835VUUL9FU
PaZ218Dd07r9/W9cJKX5EI/ihWZ5lv+0defBofQu2A8GCOXzuWIudLtcG5uYIJwO
9frevYIKteuq+MmcXedgu3uWUb0CnQ+tEyShPYjTdrFdMJYgFrOzIIlogBWK5fm3
cp8YSzeHJpBnD8qQar/W92WTTzvWd7I9vk7YB/Pdn6MIzzTJX540R2fu2rwsYBCG
DciqA8+V5sDx5eRmrcUxrmxrsDMPlbpqos0R+yujCHyiLHzt1d/oEohZ/mNw24y4
3MrjGyzlJqRZZFnaMLGNooLBrT9EeGcTXk/MgJjSO1XQjCVAWmrrsEHCXqcKwi8i
IW5OMKld80wqWr79LRF6dPxjcuwRgVE8mNd6G6qedX5TXvqjHUEVymMLFFvhVQnQ
QnOHX0HzWXF/64ghD6r3OR4pQ8C5xb7Z6tEwXX7soQVo610m7nJT1zaHm2m9HSJN
AA1bdfK9SEKXl3KJT7SCEnQ/2ED6Qr/VBFXcJB/10Il/n8AdJ2o=
=DFRR
-----END PGP SIGNATURE-----v
2021-07-17 15:08:55 -04:00
Sam Hoque
d66e6f55d3 bugs/download-progress-bar-not-working-as-intended
- Fixed the progress bar being stuck in marquee mode.
2021-07-17 14:25:05 +06:00
harryeffinpotter
25358d3a6d Merge pull request #66 from SamHoque/bugs/fix-download-games-dialog
bugs/fix-download-games-dialog
2021-07-17 04:16:29 -04:00
Sam Hoque
8088c2cf11 bugs/fix-download-games-dialog
- Recreated branch https://github.com/nerdunit/androidsideloader/pull/61 created by @dkgitdev since the branch had some merge conflicts, that was too long to resolve.
2021-07-17 14:15:57 +06:00
Harry Fn Potter
3b684142e9 Log upload 2021-07-17 02:47:12 -04:00
harryeffinpotter
e0945a71ac Merge pull request #64 from SamHoque/master
Tidied up mirror drop down
2021-07-17 02:44:23 -04:00
harryeffinpotter
a63b8b2a5c Merge pull request #65 from SamHoque/bug/fix-crashes
bug/fix-crashes
2021-07-17 02:44:07 -04:00
Sam Hoque
69dfd9ca00 bug/fix-crashes
- Fixes two crashes that occurs when closing the application
2021-07-17 11:26:08 +06:00
Sam Hoque
ebacfc0e42 Tidied up mirror drop down
- Replaced the "VRP-mirror" text to be an empty string inside `remoteList.items`
- Changed `currentRemote` to concat ` "VRP-mirror" ` before the currently selected `remotelist` item
2021-07-17 11:05:57 +06:00
Harry Fn Potter
9a3e9617b5 F3 and F4 now open Rookie Settings and Quest Options, respectively. 2021-07-17 00:28:38 -04:00
Harry Fn Potter
20607bf3b4 Logger update. 2021-07-17 00:16:42 -04:00
Harry Fn Potter
063b0d05d5 Hotkeys update, fixed obb loading taking forever. 2021-07-17 00:15:25 -04:00
Harry Fn Potter
306e2eb19b 2.1.6
bug fix
2021-07-14 07:02:25 -04:00
Harry Fn Potter
119a95406e -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.1.5

+ Added Releasename to installing messages and download size message.
= Mulitple bugfixes.

-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmDums0ACgkQx/Rql1Vo
czc+PRAAmKSXHjNqO6JXpUKIQczrs3q15yZztgmOQMULNUImis3+/ykOxsMJit1A
W6ORtj3i2VH0TDBx/hhRghMPo+dNilyKnCWm928mXc3taMOEOKu+Dz8WfhnuNp0A
ENmV5fKfuChqrn/6jkUY8UG12NM0eu3A+7fH6LvRADxRxESVH81JUtcf4bSAlier
JuL/q67nv8mThFS3tRtjjvmu0nQMCUcvagfwRNFOUW0aob7NBAp6wxTsbxZo0yEq
oVOxyTAOR84IGHpWeDKDyMw+AySMnHQXl9Ty385zg4eHZr/l1uQcK61uNfiqVCK+
Pag6I1+vLs/3Z3v6qD84iUnis/RV2lqiZcenNeTHQgBKzSsxegB5t0WaID7qlnec
655QnEUbLbIxENS7HodMR1+CzWKgmmYpIH9jV+vH0+mTaKnAYmW56q/ae8OmjXqw
6NXSfog3Dv7U8Imq21Q8glxt/CXh1fY0I8JLxpMjbwEDm56fvvzdRAeE3TeXJR0y
CD9HbH/G8oH/OhLaj1UR5y9FR3PLIBhHmnl9VHtxcvv/nrHMQHbM9bAjPUpNdnSg
30yZxPw/eqv8KLhGxxc+cBnXv3p5bNf2j27gY1Q8XbgcL8PAM6NlVBN/9oaDQ5cL
r5Cc4d5r9F+Q+QrhpZlw3jshHdoVKqgc4ut6xBAWulztNWNvLok=
=tySN
-----END PGP SIGNATURE-----
2021-07-14 04:24:54 -04:00
Harry Fn Potter
2bd4bda513 2.1.5 many bugfixes added gamename to installation messaging. 2021-07-14 04:23:44 -04:00
Harry Fn Potter
4126c82f4d updater fix 2021-07-12 06:29:13 -04:00
Harry Fn Potter
d35fed0217 2.1.4
2.1.3.2 wouldnt work with updater.
2021-07-12 06:21:07 -04:00
Harry Fn Potter
111d3a7176 -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

RSL 2.1.3.2

+ Added checkbox to enable Enter to Download+Install.
= Fixed crash that occurred when user held down up arrow or down arrow on gamelist.
= Fixed all issues with Install.txt path.
= Clicking out of search box now closes it.
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmDsFZ8ACgkQx/Rql1Vo
czcwJg/+PFiXfh6PPZNIRf2Zeht7WN8oxuUFV907KnD5+gKD0rzNd1NzgrcPOgAh
/oJakvY6ryLyaopJFZ8bm7++tQ3w6mbnzPaGweNoOF7/oLDb9NKVlK5/VjA3JPPN
QtOebRgBsigoRGGTq0s7ZRZV99vtSm2R1+1z9qlEu5Fm2hEiIggLvSBdGWDhbGnZ
jnvAZixeYgR+R6x4MZTsDao+qIYMio7heiVdN03gQ4hAaPbZ/I6s3cS+K33k02Nz
IEATqkwoLOevmN/D773A0cMzh/ZQWs557E3aKtwB+EHo/0ucCk0WaMSt4WdH8Qqe
WWF6+UT8FEEN2M76SQrg80xBj3QxzfIi92c231rVvtHMiVn5qUhTrDYb2wqx7J99
cJeMgANBmWSBZRBNW7jwhAxUVXE9vE8eqBza9MarXbJaLtq3Mf0ECLWvFPllA8dU
lnBcdSuTESvZkBAokkCwEJOb7sootBpEz5BZtE268mZEfsHUGWo3/VZDrJ7lLXJW
mEE2aCcHpNWysa9CsSee/QWNgLH2v+9PM3r/1SJyQ23C2hwyQI+F2lkch8Ar5Qfn
YxMWnhAcYXTRtcxY0s7xuhpUr/TGc6m9QY6NeVYMvb3g1gbmGOyyN/MELNOkEF/l
FXnrY4AfaNTs6EL6/id/viGcHc1MnnjgzlTNg5rgoxtFrHdVwP4=
=o0TQ
-----END PGP SIGNATURE-----
2021-07-12 06:17:21 -04:00
Harry Fn Potter
5e235c5125 Search will now select games at bottom of list too.
Added hidden shortcut CTRL+P which will turn on a property that copies the packagename of selected games to the clipboard.
2021-07-10 13:06:33 -04:00
Harry Fn Potter
9ccf7811e5 2.1.1H wouldnt work so, 2.1.3 2021-07-10 06:28:09 -04:00
harryeffinpotter
93d606bcac remove newline 2021-07-10 06:24:30 -04:00
Harry Fn Potter
8d0eb4a84d version code HF 2021-07-10 06:18:35 -04:00
Harry Fn Potter
6f20154599 Hotfix - adb kill server was running BEFORE adb was copied to correct directory. It's not an RSL update without one program breaking bug! 2021-07-10 06:18:19 -04:00
Harry Fn Potter
bbe84000b7 Fixed crash when clicking DragDrop label during load. 2021-07-10 05:34:29 -04:00
Harry Fn Potter
30ead79ed1 version # 2021-07-10 05:25:30 -04:00
Harry Fn Potter
97b6da3abe 2.1.2 ALL issues fixed. 2021-07-10 05:18:13 -04:00
Harry Fn Potter
6c2a7413b4 Fixed all merge/branch destruction. 2021-07-10 01:17:25 -04:00
137 changed files with 29314 additions and 43835 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/

View File

@@ -1,7 +0,0 @@
{
"ExpandedNodes": [
""
],
"SelectedNode": "\\Sideloader.cs",
"PreviewInSolutionExplorer": false
}

Binary file not shown.

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.

1164
ADB.cs

File diff suppressed because it is too large Load Diff

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

Binary file not shown.

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>
@@ -15,6 +15,7 @@
<Deterministic>true</Deterministic>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<IsWebBootstrapper>false</IsWebBootstrapper>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
@@ -27,9 +28,9 @@
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup>
<UseShortFileNames>True</UseShortFileNames>
@@ -48,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>
@@ -135,23 +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" />
@@ -159,8 +184,56 @@
</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>
<Compile Include="ColumnSort.cs" />
<Compile Include="Donors.cs" />
<Compile Include="DonorsListView.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="DonorsListView.Designer.cs">
<DependentUpon>DonorsListView.cs</DependentUpon>
</Compile>
<Compile Include="FlexibleMessageBox.cs" />
<Compile Include="RoundedRectangleF.cs" />
<Compile Include="Settings.cs" />
<Compile Include="Sideloader\GetDependencies.cs" />
<Compile Include="Models\PublicConfig.cs" />
<Compile Include="NewApps.cs">
<SubType>Form</SubType>
</Compile>
<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>
</Compile>
<Compile Include="UpdateForm.Designer.cs">
<DependentUpon>UpdateForm.cs</DependentUpon>
</Compile>
<Compile Include="MainForm.cs">
<SubType>Form</SubType>
</Compile>
@@ -170,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>
@@ -185,19 +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="ThemeForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="ThemeForm.Designer.cs">
<DependentUpon>ThemeForm.cs</DependentUpon>
</Compile>
<Compile Include="Updater.cs" />
<Compile Include="UsernameForm.cs">
<SubType>Form</SubType>
@@ -208,22 +270,31 @@
<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="NewApps.resx">
<DependentUpon>NewApps.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="UpdateForm.resx">
<DependentUpon>UpdateForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="MainForm.resx">
<DependentUpon>MainForm.cs</DependentUpon>
<SubType>Designer</SubType>
</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>
@@ -232,17 +303,14 @@
<DependentUpon>SettingsForm.cs</DependentUpon>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="SpoofForm.resx">
<DependentUpon>SpoofForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="ThemeForm.resx">
<DependentUpon>ThemeForm.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" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
@@ -252,6 +320,16 @@
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<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>
<None Include="App.config" />
@@ -259,6 +337,10 @@
<ItemGroup>
<Content Include="changelog.txt" />
<Content Include="icon.ico" />
<Content Include="ChangelogHistory.txt" />
<None Include="Resources\battery.png" />
<None Include="Resources\ajax-loader.gif" />
<None Include="Resources\SearchGlass.PNG" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.5.2">
@@ -274,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>
@@ -290,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,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectView>ShowAllFiles</ProjectView>
<PublishUrlHistory>publish\</PublishUrlHistory>
<InstallUrlHistory />
<SupportUrlHistory />
<UpdateUrlHistory />
<BootstrapperUrlHistory />
<ErrorReportUrlHistory />
<FallbackCulture>en-US</FallbackCulture>
<VerifyUploadedFiles>false</VerifyUploadedFiles>
</PropertyGroup>
</Project>

View File

@@ -1,180 +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="BackColor" serializeAs="String">
<value>45, 45, 45</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, 9.5pt</value>
</setting>
<setting name="BackPicturePath" serializeAs="String">
<value />
</setting>
<setting name="SpoofGames" serializeAs="String">
<value>False</value>
</setting>
<setting name="BandwithLimit" 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>
</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>

2601
ChangelogHistory.txt Normal file

File diff suppressed because it is too large Load Diff

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,105 +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>
/// Specifies the column to be sorted
/// Case-insensitive comparer object used for comparing strings.
/// </summary>
private int ColumnToSort;
private readonly CaseInsensitiveComparer ObjectCompare;
/// <summary>
/// Specifies the order in which to sort (i.e. 'Ascending').
/// </summary>
private SortOrder OrderOfSort;
/// <summary>
/// Case insensitive comparer object
/// </summary>
private 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'
ColumnToSort = 0;
// Default column index for sorting
SortColumn = 0;
// Initialize the sort order to 'none'
OrderOfSort = SortOrder.Ascending;
// 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;
// Compare the two items
compareResult = ObjectCompare.Compare(listviewX.SubItems[ColumnToSort].Text, listviewY.SubItems[ColumnToSort].Text);
// Calculate correct return value based on object comparison
if (OrderOfSort == SortOrder.Ascending)
// Special handling for column 6 (Popularity ranking)
if (SortColumn == 6)
{
// Ascending sort is selected, return normal result of compare operation
return compareResult;
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))
{
rankX = parsedX;
}
if (textY.StartsWith("#") && int.TryParse(textY.Substring(1), out int parsedY))
{
rankY = parsedY;
}
// Compare the numeric ranks
compareResult = rankX.CompareTo(rankY);
}
else if (OrderOfSort == SortOrder.Descending)
// Special handling for column 5 (Size)
else if (SortColumn == 5)
{
// Descending sort is selected, return negative result of compare operation
return (-compareResult);
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
{
// Return '0' to indicate they are equal
// Default to string comparison for non-numeric columns
compareResult = ObjectCompare.Compare(listviewX.SubItems[SortColumn].Text, listviewY.SubItems[SortColumn].Text);
}
// Determine the return value based on the specified sort order
if (Order == SortOrder.Ascending)
{
return compareResult;
}
else if (Order == SortOrder.Descending)
{
return -compareResult;
}
else
{
return 0; // Indicate equality
}
}
/// <summary>
/// Parses a numeric value from a string for accurate numeric comparison.
/// </summary>
/// <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)
{
// 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 number of the column to which to apply the sorting operation (Defaults to '0').
/// Gets or sets the index of the column to be sorted (default is '0').
/// </summary>
public int SortColumn
{
set
{
ColumnToSort = value;
}
get
{
return ColumnToSort;
}
}
public int SortColumn { get; set; }
/// <summary>
/// Gets or sets the order of sorting to apply (for example, 'Ascending' or 'Descending').
/// Gets or sets the order of sorting (Ascending or Descending).
/// </summary>
public SortOrder Order
{
set
{
OrderOfSort = value;
}
get
{
return OrderOfSort;
}
}
}
public SortOrder Order { get; set; }
}

54
Donors.cs Normal file
View File

@@ -0,0 +1,54 @@
using System.Collections.Generic;
namespace AndroidSideloader
{
internal class Donors
{
public static int GameNameIndex = 0;
public static int PackageNameIndex = 1;
public static int VersionCodeIndex = 2;
public static int UpdateOrNew = 3;
/* Game Name
* Package Name
* Version Code
* Update or New app
*/
public static List<string> newAppProperties = new List<string>();
public static List<string> donorGameProperties = new List<string>();
public static List<string[]> donorGames = new List<string[]>();
public static List<string[]> newApps = new List<string[]>();
public static void initDonorGames()
{
donorGameProperties.Clear();
donorGames.Clear();
if (!string.IsNullOrEmpty(MainForm.donorApps))
{
string[] gameListSplited = MainForm.donorApps.Split('\n');
foreach (string game in gameListSplited)
{
if (game.Length > 1)
{
donorGames.Add(game.Split(';'));
}
}
}
}
public static void initNewApps()
{
newApps.Clear();
if (!string.IsNullOrEmpty(DonorsListViewForm.newAppsForList))
{
string[] newListSplited = DonorsListViewForm.newAppsForList.Split('\n');
foreach (string game in newListSplited)
{
if (game.Length > 1)
{
newApps.Add(game.Split(';'));
}
}
}
}
}
}

344
DonorsListView.Designer.cs generated Normal file
View File

@@ -0,0 +1,344 @@

namespace AndroidSideloader
{
partial class DonorsListViewForm
{
/// <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.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.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.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 = 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[] {
this.GameNameIndex,
this.PackageNameIndex,
this.VersionCodeIndex,
this.UpdateOrNew});
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.Location = new System.Drawing.Point(1, 1);
this.DonorsListView.MinimumSize = new System.Drawing.Size(100, 100);
this.DonorsListView.Name = "DonorsListView";
this.DonorsListView.Size = new System.Drawing.Size(418, 248);
this.DonorsListView.TabIndex = 0;
this.DonorsListView.UseCompatibleStateImageBehavior = false;
this.DonorsListView.View = System.Windows.Forms.View.Details;
this.DonorsListView.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.DonorsListView_ItemChecked);
this.DonorsListView.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseDown);
this.DonorsListView.MouseMove += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseMove);
this.DonorsListView.MouseUp += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseUp);
//
// GameNameIndex
//
this.GameNameIndex.Text = "App Name";
this.GameNameIndex.Width = 220;
//
// PackageNameIndex
//
this.PackageNameIndex.DisplayIndex = 2;
this.PackageNameIndex.Text = "Package";
this.PackageNameIndex.Width = 0;
//
// VersionCodeIndex
//
this.VersionCodeIndex.DisplayIndex = 3;
this.VersionCodeIndex.Text = "Version";
this.VersionCodeIndex.Width = 100;
//
// UpdateOrNew
//
this.UpdateOrNew.DisplayIndex = 1;
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("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(228, 20);
this.bothdet.TabIndex = 3;
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);
this.bothdet.MouseUp += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseUp);
//
// newdet
//
this.newdet.AutoSize = true;
this.newdet.BackColor = System.Drawing.Color.Transparent;
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(149, 20);
this.newdet.TabIndex = 3;
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);
this.newdet.MouseUp += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseUp);
//
// upddet
//
this.upddet.AutoSize = true;
this.upddet.BackColor = System.Drawing.Color.Transparent;
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(135, 20);
this.upddet.TabIndex = 3;
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);
this.upddet.MouseUp += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseUp);
//
// label2
//
this.label2.AutoSize = true;
this.label2.BackColor = System.Drawing.Color.Transparent;
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(338, 15);
this.label2.TabIndex = 3;
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("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(292, 13);
this.TimerDesc.TabIndex = 3;
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);
//
// DonorsListViewForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
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.None;
this.Name = "DonorsListViewForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Load += new System.EventHandler(this.DonorsListViewForm_Load);
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseDown);
this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseMove);
this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseUp);
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
this.panel2.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.ListView DonorsListView;
private System.Windows.Forms.Panel panel1;
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.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;
}
}

324
DonorsListView.cs Normal file
View File

@@ -0,0 +1,324 @@
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);
// 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();
var seen = new HashSet<string>();
var DGameList = new List<ListViewItem>();
foreach (string[] release in Donors.donorGames)
{
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();
DonorsListView.Items.AddRange(arr);
DonorsListView.EndUpdate();
}
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;
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 = UpdateHighlightColor;
}
}
private async void DonateButton_Click(object sender, EventArgs e)
{
if (DonorsListView.CheckedItems.Count > 0)
{
bool uncheckednewapps = false;
foreach (ListViewItem listItem in DonorsListView.Items)
{
if (!listItem.Checked && listItem.SubItems[Donors.UpdateOrNew].Text.Contains("New"))
{
uncheckednewapps = true;
newAppsForList += listItem.SubItems[Donors.GameNameIndex].Text + ";" + listItem.SubItems[Donors.PackageNameIndex].Text + "\n";
}
}
if (uncheckednewapps)
{
new NewApps().ShowDialog();
Hide();
}
else
{
Hide();
}
for (int i = 0; i < DonorsListView.CheckedItems.Count; i++)
{
ulong vcode = Convert.ToUInt64(DonorsListView.CheckedItems[i].SubItems[Donors.VersionCodeIndex].Text);
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();
Close();
}
private void DonorsListView_ItemChecked(object sender, ItemCheckedEventArgs e)
{
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)
{
foreach (ListViewItem listItem in DonorsListView.Items)
{
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) => TitleArea_MouseDown(sender, e);
private void DonorsListViewForm_MouseMove(object sender, MouseEventArgs e) { }
private void DonorsListViewForm_MouseUp(object sender, MouseEventArgs e) { }
private void skip_forever_Click(object sender, EventArgs e)
{
var appsToBlacklist = DonorsListView.CheckedItems.Cast<ListViewItem>()
.Select(item => item.SubItems[Donors.PackageNameIndex].Text).ToList();
if (appsToBlacklist.Count == 0)
{
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);
}
}
}
}

View File

@@ -1,132 +1,123 @@
<?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>
<metadata name="colorDialog1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="openFileDialog1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>138, 17</value>
</metadata>
<metadata name="fontDialog1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>278, 17</value>
</metadata>
<metadata name="openThemeDialog.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>394, 17</value>
</metadata>
<?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>
<metadata name="DonationTimer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

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

674
LICENSE Normal file
View File

@@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

2798
MainForm.Designer.cs generated

File diff suppressed because it is too large Load Diff

10840
MainForm.cs Normal file → Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -117,4 +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>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>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>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>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>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>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>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>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>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>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>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>223, 54</value>
</metadata>
<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="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>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>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>428, 54</value>
</metadata>
<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>

22
Models/PublicConfig.cs Normal file
View File

@@ -0,0 +1,22 @@
using Newtonsoft.Json;
using System;
using System.Text;
namespace AndroidSideloader.Models
{
[JsonObject(MemberSerialization.OptIn)]
public class PublicConfig
{
[JsonProperty("baseUri")]
public string BaseUri { get; set; }
private string password;
[JsonProperty("password")]
public string Password
{
get => password;
set => password = Encoding.UTF8.GetString(Convert.FromBase64String(value));
}
}
}

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; }
}
}

199
NewApps.Designer.cs generated Normal file
View File

@@ -0,0 +1,199 @@

namespace AndroidSideloader
{
partial class NewApps
{
/// <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.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.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 = 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.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(1, 1);
this.NewAppsListView.Name = "NewAppsListView";
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;
this.NewAppsListView.MouseDown += new System.Windows.Forms.MouseEventHandler(this.label2_MouseDown);
this.NewAppsListView.MouseMove += new System.Windows.Forms.MouseEventHandler(this.label2_MouseMove);
this.NewAppsListView.MouseUp += new System.Windows.Forms.MouseEventHandler(this.label2_MouseUp);
//
// GameNameIndex
//
this.GameNameIndex.Text = "App Name";
this.GameNameIndex.Width = 280;
//
// PackageNameIndex
//
this.PackageNameIndex.Width = 0;
//
// panel1
//
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("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(223, 15);
this.label2.TabIndex = 9;
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);
//
// titleLabel
//
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 = 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.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);
}
#endregion
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;
}
}

203
NewApps.cs Normal file
View File

@@ -0,0 +1,203 @@
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
{
// 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 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;
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 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;
}
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";
else
Properties.Settings.Default.AppPackages += listItem.SubItems[Donors.PackageNameIndex].Text + "\n";
Properties.Settings.Default.Save();
}
MainForm.newPackageUpload();
Close();
}
private void NewApps_Load(object sender, EventArgs e)
{
NewAppsListView.Items.Clear();
Donors.initNewApps();
var NewAppList = new List<ListViewItem>();
foreach (string[] release in Donors.newApps)
{
ListViewItem NGame = new ListViewItem(release);
if (!NewAppList.Contains(NGame))
NewAppList.Add(NGame);
}
NewAppsListView.BeginUpdate();
NewAppsListView.Items.AddRange(NewAppList.ToArray());
NewAppsListView.EndUpdate();
}
}
}

View File

@@ -1,120 +1,120 @@
<?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>
<?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>

View File

@@ -1,35 +1,110 @@
using System;
using System.Windows.Forms;
using System.Security.Permissions;
using System.IO;
namespace AndroidSideloader
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlAppDomain)]
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;
static void MyHandler(object sender, UnhandledExceptionEventArgs args)
{
Exception e = (Exception)args.ExceptionObject;
string date_time = DateTime.Today.ToString("dddd, MMMM dd @ hh:mmtt");
File.WriteAllText(Sideloader.CrashLogPath, $"\n\n################\nDate/Time of crash: {date_time}################\n\nMessage: {e.Message}\nData: {e.Data}\nSource: {e.Source}\nTargetSite: {e.TargetSite}");
}
}
}
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

@@ -1,5 +1,4 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@@ -7,9 +6,9 @@ using System.Runtime.InteropServices;
// associated with an assembly.
//[assembly: AssemblyKeyFileAttribute("keypair.snk")]
[assembly: AssemblyTitle("AndroidSideloader")]
[assembly: AssemblyDescription("Rookie's Sideloader")]
[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

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is automatically generated by Visual Studio .Net. It is
used to store generic object data source configuration information.
Renaming the file extension or editing the content of this file may
cause the file to be unrecognizable by the program.
-->
<GenericObjectDataSource DisplayName="Resources" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
<TypeInfo>AndroidSideloader.Properties.Resources, Properties.Resources.Designer.cs.dll, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null</TypeInfo>
</GenericObjectDataSource>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is automatically generated by Visual Studio .Net. It is
used to store generic object data source configuration information.
Renaming the file extension or editing the content of this file may
cause the file to be unrecognizable by the program.
-->
<GenericObjectDataSource DisplayName="Settings" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
<TypeInfo>AndroidSideloader.Properties.Settings, Properties.Resources.Designer.cs.dll, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null</TypeInfo>
</GenericObjectDataSource>

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", "16.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 {
@@ -59,5 +59,35 @@ namespace AndroidSideloader.Properties {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
public static System.Drawing.Bitmap ajax_loader {
get {
object obj = ResourceManager.GetObject("ajax-loader", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
public static System.Drawing.Bitmap battery {
get {
object obj = ResourceManager.GetObject("battery", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
public static System.Drawing.Bitmap SearchGlass {
get {
object obj = ResourceManager.GetObject("SearchGlass", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
}
}

View File

@@ -117,4 +117,14 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<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="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>
</root>

File diff suppressed because it is too large Load Diff

View File

@@ -23,26 +23,8 @@
<Setting Name="CallUpgrade" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="BackColor" Type="System.Drawing.Color" Scope="User">
<Value Profile="(Default)">45, 45, 45</Value>
</Setting>
<Setting Name="ButtonColor" Type="System.Drawing.Color" Scope="User">
<Value Profile="(Default)">ActiveCaptionText</Value>
</Setting>
<Setting Name="SubButtonColor" Type="System.Drawing.Color" Scope="User">
<Value Profile="(Default)">64, 64, 64</Value>
</Setting>
<Setting Name="TextBoxColor" Type="System.Drawing.Color" Scope="User">
<Value Profile="(Default)">45, 45, 45</Value>
</Setting>
<Setting Name="ComboBoxColor" Type="System.Drawing.Color" Scope="User">
<Value Profile="(Default)">45, 45, 45</Value>
</Setting>
<Setting Name="FontColor" Type="System.Drawing.Color" Scope="User">
<Value Profile="(Default)">White</Value>
</Setting>
<Setting Name="FontStyle" Type="System.Drawing.Font" Scope="User">
<Value Profile="(Default)">Microsoft Sans Serif, 9.5pt</Value>
<Value Profile="(Default)">Microsoft Sans Serif, 10pt</Value>
</Setting>
<Setting Name="BackPicturePath" Type="System.String" Scope="User">
<Value Profile="(Default)" />
@@ -50,9 +32,6 @@
<Setting Name="SpoofGames" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="BandwithLimit" 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>
@@ -68,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>
@@ -110,5 +62,137 @@
<Setting Name="WirelessADB" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="CurrentGamename" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="PackageNameToCB" 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>
<Setting Name="CurrentLogPath" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="CurrentLogName" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="CurrentCrashPath" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="CurrentCrashName" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="adbdebugwarned" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="nodevicemode" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="BMBFchecked" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="GamesList" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="UploadedGameList" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="GlobalUsername" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="lastTimeShared" Type="System.DateTime" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="AutoReinstall" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="NonAppPackages" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="LastLaunch" Type="System.DateTime" Scope="User">
<Value Profile="(Default)">04/20/1969 16:20:00</Value>
</Setting>
<Setting Name="SubmittedUpdates" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="ListUpped" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="LastLaunch2" Type="System.DateTime" Scope="User">
<Value Profile="(Default)">04/20/1969 16:20:00</Value>
</Setting>
<Setting Name="Wired" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="FontColor" Type="System.Drawing.Color" Scope="User">
<Value Profile="(Default)">White</Value>
</Setting>
<Setting Name="ComboBoxColor" Type="System.Drawing.Color" Scope="User">
<Value Profile="(Default)">25, 25, 25</Value>
</Setting>
<Setting Name="SubButtonColor" Type="System.Drawing.Color" Scope="User">
<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)">32, 35, 45</Value>
</Setting>
<Setting Name="BackColor" Type="System.Drawing.Color" Scope="User">
<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>

1201
QuestForm.Designer.cs generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,329 +1,199 @@
using System;
using System.Collections.Generic;
using AndroidSideloader.Utilities;
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
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.ToString()}");
ADB.RunAdbCommandToString($"shell settings put global 90hz_global {RefreshRateComboBox.SelectedIndex}");
ADB.RunAdbCommandToString($"shell settings put global 90hzglobal {RefreshRateComboBox.SelectedIndex}");
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();
Int32.TryParse(TextureResTextBox.Text, out int result);
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.ToString()}");
_ = ADB.RunAdbCommandToString($"shell setprop debug.oculus.cpuLevel {CPUComboBox.SelectedIndex}");
ChangesMade = true;
}
if (GPUComboBox.SelectedIndex != -1)
{
ADB.RunAdbCommandToString($"shell setprop debug.oculus.gpuLevel {GPUComboBox.SelectedItem.ToString()}");
_ = 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)
{
if (DeleteShots.Checked)
delsh = true;
else
delsh = false;
}
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;
}
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;
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 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)
private void QuestForm_FormClosed(object sender, FormClosedEventArgs 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)
{
Random r = new Random();
int x = r.Next(999999999);
int y = r.Next(9999999);
var sum = ((long)y * (long)1000000000) + (long)x;
int x2 = r.Next(999999999);
int y2 = r.Next(9999999);
var sum2 = ((long)y2 * (long)1000000000) + (long)x2;
QUSon = true;
string selected = this.QURfrRt.GetItemText(this.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);
}
}
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)
{
if (Properties.Settings.Default.delsh)
DeleteShots.Checked = true;
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;
}
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 = this.QURfrRt.GetItemText(this.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"))
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\"");
if (!Directory.Exists($"{path}\\Quest Screenshots"))
{
_ = Directory.CreateDirectory($"{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");
{
_ = 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"))
Directory.CreateDirectory($"{path}\\Quest VideoShots");
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\"");
if (!Directory.Exists($"{path}\\Quest Recordings"))
{
_ = Directory.CreateDirectory($"{path}\\Quest Recordings");
}
_ = 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");
_ = 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)
{
UsernameForm Form = new UsernameForm();
Form.Show();
_ = MessageBox.Show("Transfer finished!\n\nRecordings can be found in:\nDesktop\\Quest Recordings",
"Complete", MessageBoxButtons.OK, MessageBoxIcon.Information);
Program.form.changeTitle("Done!");
}
private void label5_Click(object sender, EventArgs e)
private void btnApplyUsername_Click(object sender, EventArgs e)
{
_ = ADB.RunAdbCommandToString($"shell settings put global username {GlobalUsername.Text}");
_ = MessageBox.Show($"Username set to: {GlobalUsername.Text}", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void label6_Click(object sender, EventArgs e)
private void btnClose_Click(object sender, EventArgs e)
{
Close();
}
private void label8_Click(object sender, EventArgs e)
protected override bool ProcessDialogKey(Keys keyData)
{
if (Form.ModifierKeys == Keys.None && keyData == Keys.Escape)
{
Close();
return true;
}
return base.ProcessDialogKey(keyData);
}
private void label7_Click(object sender, EventArgs e)
private void GlobalUsername_TextChanged(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;
}
}
}
}

383
RCLONE.cs
View File

@@ -1,21 +1,46 @@
using System;
using AndroidSideloader.Utilities;
using JR.Utils.GUI.Forms;
using System;
using System.Diagnostics;
using System.Text;
using System.IO;
using System.Management;
using System.Text;
using System.Windows.Forms;
namespace AndroidSideloader
{
class RCLONE
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"))
process.Kill();
var parentProcessId = Process.GetCurrentProcess().Id;
var processes = Process.GetProcessesByName("rclone");
foreach (var process in processes)
{
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");
@@ -25,57 +50,173 @@ namespace AndroidSideloader
}
}
//Change if you want to use a config
public static string configPath = ""; // ".\\a"
// 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 = "";
private static Process rclone = new Process();
//Run rclone command
public static ProcessOutput runRcloneCommand(string command, string bandwithLimit = "")
{
if (!MainForm.HasInternet) return new ProcessOutput("", "No internet");
private static readonly Process rclone = new Process();
// Run an RCLONE Command that accesses the Download Config.
public static ProcessOutput runRcloneCommand_DownloadConfig(string command)
{
if (MainForm.isOffline)
{
return new ProcessOutput("", "No internet");
}
//Set the password for rclone configs
Environment.SetEnvironmentVariable("RCLONE_CRYPT_REMOTE", rclonepw);
Environment.SetEnvironmentVariable("RCLONE_CONFIG_PASS", rclonepw);
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 (bandwithLimit.Length > 0)
// set configpath if there is any
if (downloadConfigPath.Length > 0)
{
command += $" --bwlimit={bandwithLimit}";
command += $" --config {downloadConfigPath}";
}
//set configpath if there is any
if (configPath.Length > 0)
{
command += $" --config {configPath}";
}
command += $" --inplace";
//set rclonepw
// set rclonepw
if (rclonepw.Length > 0)
{
command += " --ask-password=false";
}
Logger.Log($"Running Rclone command: {command}");
string logcmd = Utilities.StringUtilities.RemoveEverythingBeforeFirst(command, "rclone.exe");
if (logcmd.Contains($"\"{settings.CurrentLogPath}\""))
{
logcmd = logcmd.Replace($"\"{settings.CurrentLogPath}\"", $"\"{settings.CurrentLogName}\"");
}
rclone.StartInfo.FileName = Environment.CurrentDirectory + "\\rclone\\rclone.exe";
if (logcmd.Contains(Environment.CurrentDirectory))
{
logcmd = logcmd.Replace($"{Environment.CurrentDirectory}", $"CurrentDirectory");
}
_ = Logger.Log($"Running Rclone command: {logcmd}");
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)
rclone.StartInfo.CreateNoWindow = false;
rclone.StartInfo.UseShellExecute = false;
rclone.Start();
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.Close();
string output = rclone.StandardOutput.ReadToEnd();
string error = rclone.StandardError.ReadToEnd();
rclone.WaitForExit();
if (error.Contains("There is not enough space"))
{
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.", "");
}
// 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
{
retSM = Program.form.SwitchMirrors();
}
catch
{
return new ProcessOutput("All mirrors are on quota or down...", "All mirrors are on quota or down...");
}
if (retSM)
{
prcoutput = runRcloneCommand_DownloadConfig(originalCommand.Replace(oldRemote, MainForm.currentRemote));
}
}
else
{
prcoutput.Output = output;
prcoutput.Error = error;
}
if (!output.Contains("Game Name;Release Name;") && !output.Contains("package:") && !output.Contains(".meta"))
{
if (!string.IsNullOrWhiteSpace(error))
{
_ = Logger.Log($"Rclone error: {error}\n", LogLevel.ERROR);
}
if (!string.IsNullOrWhiteSpace(output))
{
_ = Logger.Log($"Rclone Output: {output}");
}
}
return prcoutput;
}
public static ProcessOutput runRcloneCommand_UploadConfig(string command)
{
ProcessOutput prcoutput = new ProcessOutput();
// Rclone output is unicode, else it will show garbage instead of unicode characters
rclone.StartInfo.StandardOutputEncoding = Encoding.UTF8;
// set configpath if there is any
if (uploadConfigPath.Length > 0)
{
command += $" --config {uploadConfigPath}";
}
string logcmd = Utilities.StringUtilities.RemoveEverythingBeforeFirst(command, "rclone.exe");
if (logcmd.Contains($"\"{settings.CurrentLogPath}\""))
{
logcmd = logcmd.Replace($"\"{settings.CurrentLogPath}\"", $"\"{settings.CurrentLogName}\"");
}
if (logcmd.Contains(Environment.CurrentDirectory))
{
logcmd = logcmd.Replace($"{Environment.CurrentDirectory}", $"CurrentDirectory");
}
_ = Logger.Log($"Running Rclone command: {logcmd}");
command += " --checkers 1 --retries 2 --inplace";
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 = Path.Combine(Environment.CurrentDirectory, "rclone");
rclone.StartInfo.CreateNoWindow = 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();
@@ -84,31 +225,167 @@ namespace AndroidSideloader
string error = rclone.StandardError.ReadToEnd();
rclone.WaitForExit();
//if there is one of these errors, we switch the mirrors
if (error.Contains("cannot fetch token") || error.Contains("authError") || (error.Contains("quota") || error.Contains("exceeded")))
// 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"))
{
string oldRemote = MainForm.currentRemote;
try
{
Program.form.SwitchMirrors();
}
catch
{
return new ProcessOutput("All mirrors are on quota or down...", "All mirrors are on quota or down...");
}
prcoutput = runRcloneCommand(originalCommand.Replace(oldRemote, MainForm.currentRemote), bandwithLimit);
_ = Logger.Log(error, LogLevel.ERROR);
return new ProcessOutput("Upload Failed.", "Upload failed.");
}
else
{
prcoutput.Output = output;
prcoutput.Error = error;
}
if (!output.Contains("Game Name;Release APK Path;"))
Logger.Log($"Rclone error: {error}\nRclone Output: {output}");
if (error.Contains("There is not enough space"))
MessageBox.Show("There isn't enough space on your PC to properly install this game. Please have at least 2x the size of the game you are trying to download/install available on the drive where Rookie is installed.", "NOT ENOUGH SPACE");
return prcoutput;
if (!output.Contains("Game Name;Release Name;") && !output.Contains("package:") && !output.Contains(".meta"))
{
if (!string.IsNullOrWhiteSpace(error))
{
_ = Logger.Log($"Rclone error: {error}\n", LogLevel.ERROR);
}
if (!string.IsNullOrWhiteSpace(output))
{
_ = Logger.Log($"Rclone Output: {output}");
}
}
return prcoutput;
}
public static ProcessOutput runRcloneCommand_PublicConfig(string command)
{
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.StartInfo.StandardOutputEncoding = Encoding.UTF8;
string logcmd = Utilities.StringUtilities.RemoveEverythingBeforeFirst(command, "rclone.exe");
if (logcmd.Contains($"\"{settings.CurrentLogPath}\""))
{
logcmd = logcmd.Replace($"\"{settings.CurrentLogPath}\"", $"\"{settings.CurrentLogName}\"");
}
if (logcmd.Contains(Environment.CurrentDirectory))
{
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 = 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 = Path.Combine(Environment.CurrentDirectory, "rclone");
rclone.StartInfo.CreateNoWindow = 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();
string error = rclone.StandardError.ReadToEnd();
rclone.WaitForExit();
if (error.Contains("There is not enough space"))
{
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.", string.Empty);
}
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")
|| error.Contains("exceeded")
|| error.Contains("directory not found")
|| error.Contains("Failed to"))
{
_ = Logger.Log(error, LogLevel.ERROR);
return new ProcessOutput("Failed to fetch from public mirror.", "Failed to fetch from public mirror.");
}
else
{
prcoutput.Output = output;
prcoutput.Error = error;
}
if (!output.Contains("Game Name;Release Name;") && !output.Contains("package:") && !output.Contains(".meta"))
{
if (!string.IsNullOrWhiteSpace(error))
{
_ = Logger.Log($"Rclone error: {error}\n", LogLevel.ERROR);
}
if (!string.IsNullOrWhiteSpace(output))
{
_ = Logger.Log($"Rclone Output: {output}");
}
}
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,18 +1,33 @@
# androidsideloader
# AndroidSideloader
This app might get detected as malware, however both the sideloader and the sideloader launcher are open source
![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)
https://www.virustotal.com/gui/file/977105693610cf360fc29339b918e224180ba393ba05a64b6255af3845cbf376/relations
## Disclaimer
This application might get flagged as malware by some antivirus software; however, both the Sideloader and the Sideloader Launcher are open source.
Special thanks to
- [Everyone who donated!](https://raw.githubusercontent.com/nerdunit/androidsideloader/master/donators.txt)
- pmow for all of his work, including rclone, wonka and other projects
- flow for being friendly and helping every one
- succ for creating and maintaining the server
- badcoder5000 for redesigning the UI
- gotard for the theme changer
- [7zip](https://www.7-zip.org/) team for [7zip](https://www.7-zip.org/) :)
- [rclone](https://rclone.org/) team for [rclone](https://rclone.org/) :D
- [erike](https://stackoverflow.com/users/57611/erike) for the folder browser dialog code
- Serge Weinstock for developing SergeUtils, which is used to search the combo box
- https://www.c-sharpcorner.com/members/mike-gold2 for the scrollable message box
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.
## 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.

BIN
Resources/SearchGlass.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
Resources/ajax-loader.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 723 B

BIN
Resources/battery.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

10
Rookie Offline.cmd Normal file
View File

@@ -0,0 +1,10 @@
@echo off
for /f "tokens=* usebackq" %%f in (`dir /b AndroidSideloader*.exe`) do (set "sideloader=%%f" & goto :next)
:next
echo %sideloader%
start "" "%sideloader%" "--offline"

336
RoundButton.cs Normal file
View File

@@ -0,0 +1,336 @@
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace AndroidSideloader
{
[Description("Rounded Button")]
public class RoundButton : Control, IButtonControl
{
#region Variables
private int radius;
private MouseState state;
private RoundedRectangleF roundedRect;
private Color inactive1, inactive2, active1, active2;
private Color strokeColor;
private bool stroke;
public bool Stroke
{
get => stroke;
set
{
stroke = value;
Invalidate();
}
}
public Color StrokeColor
{
get => strokeColor;
set
{
strokeColor = value;
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()
{
Width = 65;
Height = 30;
stroke = false;
strokeColor = Color.Gray;
inactive1 = Color.FromArgb(44, 188, 210);
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);
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer |
ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor |
ControlStyles.UserPaint, true);
BackColor = Color.Transparent;
ForeColor = Color.Black;
Font = new System.Drawing.Font("Comic Sans MS", 10, FontStyle.Bold);
state = MouseState.Leave;
Transparency = false;
}
#endregion
#region Events
protected override void OnPaint(PaintEventArgs e)
{
#region Transparency
if (Transparency)
{
Transparenter.MakeTransparent(this, e.Graphics);
}
#endregion
#region Drawing
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
roundedRect = new RoundedRectangleF(Width, Height, radius);
e.Graphics.FillRectangle(Brushes.Transparent, ClientRectangle);
int R1 = (active1.R + inactive1.R) / 2;
int G1 = (active1.G + inactive1.G) / 2;
int B1 = (active1.B + inactive1.B) / 2;
int R2 = (active2.R + inactive2.R) / 2;
int G2 = (active2.G + inactive2.G) / 2;
int B2 = (active2.B + inactive2.B) / 2;
Rectangle rect = new Rectangle(0, 0, Width, Height);
if (Enabled)
{
if (state == MouseState.Leave)
{
using (LinearGradientBrush inactiveGB = new LinearGradientBrush(rect, inactive1, inactive2, 90f))
{
e.Graphics.FillPath(inactiveGB, roundedRect.Path);
}
}
else if (state == MouseState.Enter)
{
using (LinearGradientBrush activeGB = new LinearGradientBrush(rect, active1, active2, 90f))
{
e.Graphics.FillPath(activeGB, roundedRect.Path);
}
}
else if (state == MouseState.Down)
{
using (LinearGradientBrush downGB = new LinearGradientBrush(rect, Color.FromArgb(R1, G1, B1), Color.FromArgb(R2, G2, B2), 90f))
{
e.Graphics.FillPath(downGB, roundedRect.Path);
}
}
if (stroke)
{
using (Pen pen = new Pen(strokeColor, 1))
using (GraphicsPath path = new RoundedRectangleF(Width - (radius > 0 ? 0 : 1), Height - (radius > 0 ? 0 : 1), radius).Path)
{
e.Graphics.DrawPath(pen, path);
}
}
}
else
{
using (LinearGradientBrush disabledGB = new LinearGradientBrush(rect, disabled1, disabled2, 90f))
{
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);
}
}
}
#endregion
#region Text Drawing
using (StringFormat sf = new StringFormat()
{
LineAlignment = StringAlignment.Center,
Alignment = StringAlignment.Center,
})
using (Brush brush = new SolidBrush(ForeColor))
{
e.Graphics.DrawString(Text, Font, brush, ClientRectangle, sf);
}
#endregion
base.OnPaint(e);
}
protected override void OnMouseDoubleClick(MouseEventArgs e)
{
base.OnMouseDoubleClick(e);
base.OnClick(e);
}
protected override void OnClick(EventArgs e)
{
base.OnClick(e);
}
protected override void OnEnabledChanged(EventArgs e)
{
Invalidate();
base.OnEnabledChanged(e);
}
protected override void OnResize(EventArgs e)
{
Invalidate();
base.OnResize(e);
}
protected override void OnMouseEnter(EventArgs e)
{
state = MouseState.Enter;
base.OnMouseEnter(e);
Invalidate();
}
protected override void OnMouseLeave(EventArgs e)
{
state = MouseState.Leave;
base.OnMouseLeave(e);
Invalidate();
}
protected override void OnMouseDown(MouseEventArgs e)
{
Capture = false;
state = MouseState.Down;
base.OnMouseDown(e);
Invalidate();
}
protected override void OnMouseUp(MouseEventArgs e)
{
if (state != MouseState.Leave)
{
state = MouseState.Enter;
}
base.OnMouseUp(e);
Invalidate();
}
#endregion
#region Properties
public int Radius
{
get => radius;
set
{
radius = value;
Invalidate();
}
}
public Color Inactive1
{
get => inactive1;
set
{
inactive1 = value;
Invalidate();
}
}
public Color Inactive2
{
get => inactive2;
set
{
inactive2 = value;
Invalidate();
}
}
public Color Active1
{
get => active1;
set
{
active1 = value;
Invalidate();
}
}
public Color Active2
{
get => active2;
set
{
active2 = value;
Invalidate();
}
}
public bool Transparency { get; set; }
public override string Text
{
get => base.Text;
set
{
base.Text = value;
Invalidate();
}
}
public override Color ForeColor
{
get => base.ForeColor;
set
{
base.ForeColor = value;
Invalidate();
}
}
public DialogResult DialogResult
{
get => System.Windows.Forms.DialogResult.OK;
set
{
}
}
public void NotifyDefault(bool value)
{
}
public void PerformClick()
{
OnClick(EventArgs.Empty);
}
#endregion
}
public enum MouseState
{
Enter,
Leave,
Down,
Up,
}
}

48
RoundedRectangleF.cs Normal file
View File

@@ -0,0 +1,48 @@
using System.Drawing;
using System.Drawing.Drawing2D;
namespace AndroidSideloader
{
public class RoundedRectangleF
{
private Point location;
private readonly float x, y;
private readonly float width, height;
public RoundedRectangleF(float width, float height, float radius, float x = 0, float y = 0)
{
location = new Point(0, 0);
Radius = radius;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
Path = new GraphicsPath();
if (radius <= 0)
{
Path.AddRectangle(new RectangleF(x, y, width, height));
return;
}
RectangleF upperLeftRect = new RectangleF(x, y, 2 * radius, 2 * radius);
RectangleF upperRightRect = new RectangleF(width - (2 * radius) - 1, x, 2 * radius, 2 * radius);
RectangleF lowerLeftRect = new RectangleF(x, height - (2 * radius) - 1, 2 * radius, 2 * radius);
RectangleF lowerRightRect = new RectangleF(width - (2 * radius) - 1, height - (2 * radius) - 1, 2 * radius, 2 * radius);
Path.AddArc(upperLeftRect, 180, 90);
Path.AddArc(upperRightRect, 270, 90);
Path.AddArc(lowerRightRect, 0, 90);
Path.AddArc(lowerLeftRect, 90, 90);
Path.CloseAllFigures();
}
public RoundedRectangleF()
{
}
public GraphicsPath Path { get; }
public RectangleF Rect => new RectangleF(x, y, width, height);
public float Radius { get; set; }
}
}

View File

@@ -14,19 +14,18 @@ namespace AndroidSideloader
{
private string _initialDirectory;
private string _title;
private string _fileName = "";
public string InitialDirectory
{
get { return string.IsNullOrEmpty(_initialDirectory) ? Environment.CurrentDirectory : _initialDirectory; }
set { _initialDirectory = value; }
get => string.IsNullOrEmpty(_initialDirectory) ? Environment.CurrentDirectory : _initialDirectory;
set => _initialDirectory = value;
}
public string Title
{
get { return _title ?? "Select a folder"; }
set { _title = value; }
get => _title ?? "Select a folder";
set => _title = value;
}
public string FileName { get { return _fileName; } }
public string FileName { get; private set; } = "";
public bool Show() { return Show(IntPtr.Zero); }
@@ -34,10 +33,10 @@ namespace AndroidSideloader
/// <returns>true if the user clicks OK</returns>
public bool Show(IntPtr hWndOwner)
{
var result = Environment.OSVersion.Version.Major >= 6
ShowDialogResult result = Environment.OSVersion.Version.Major >= 6
? VistaDialog.Show(hWndOwner, InitialDirectory, Title)
: ShowXpDialog(hWndOwner, InitialDirectory, Title);
_fileName = result.FileName;
FileName = result.FileName;
return result.Result;
}
@@ -49,13 +48,13 @@ namespace AndroidSideloader
private static ShowDialogResult ShowXpDialog(IntPtr ownerHandle, string initialDirectory, string title)
{
var folderBrowserDialog = new FolderBrowserDialog
FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog
{
Description = title,
SelectedPath = initialDirectory,
ShowNewFolderButton = false
};
var dialogResult = new ShowDialogResult();
ShowDialogResult dialogResult = new ShowDialogResult();
if (folderBrowserDialog.ShowDialog(new WindowWrapper(ownerHandle)) == DialogResult.OK)
{
dialogResult.Result = true;
@@ -69,26 +68,26 @@ namespace AndroidSideloader
private const string c_foldersFilter = "Folders|\n";
private const BindingFlags c_flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
private readonly static Assembly s_windowsFormsAssembly = typeof(FileDialog).Assembly;
private readonly static Type s_iFileDialogType = s_windowsFormsAssembly.GetType("System.Windows.Forms.FileDialogNative+IFileDialog");
private readonly static MethodInfo s_createVistaDialogMethodInfo = typeof(OpenFileDialog).GetMethod("CreateVistaDialog", c_flags);
private readonly static MethodInfo s_onBeforeVistaDialogMethodInfo = typeof(OpenFileDialog).GetMethod("OnBeforeVistaDialog", c_flags);
private readonly static MethodInfo s_getOptionsMethodInfo = typeof(FileDialog).GetMethod("GetOptions", c_flags);
private readonly static MethodInfo s_setOptionsMethodInfo = s_iFileDialogType.GetMethod("SetOptions", c_flags);
private readonly static uint s_fosPickFoldersBitFlag = (uint)s_windowsFormsAssembly
private static readonly Assembly s_windowsFormsAssembly = typeof(FileDialog).Assembly;
private static readonly Type s_iFileDialogType = s_windowsFormsAssembly.GetType("System.Windows.Forms.FileDialogNative+IFileDialog");
private static readonly MethodInfo s_createVistaDialogMethodInfo = typeof(OpenFileDialog).GetMethod("CreateVistaDialog", c_flags);
private static readonly MethodInfo s_onBeforeVistaDialogMethodInfo = typeof(OpenFileDialog).GetMethod("OnBeforeVistaDialog", c_flags);
private static readonly MethodInfo s_getOptionsMethodInfo = typeof(FileDialog).GetMethod("GetOptions", c_flags);
private static readonly MethodInfo s_setOptionsMethodInfo = s_iFileDialogType.GetMethod("SetOptions", c_flags);
private static readonly uint s_fosPickFoldersBitFlag = (uint)s_windowsFormsAssembly
.GetType("System.Windows.Forms.FileDialogNative+FOS")
.GetField("FOS_PICKFOLDERS")
.GetValue(null);
private readonly static ConstructorInfo s_vistaDialogEventsConstructorInfo = s_windowsFormsAssembly
private static readonly ConstructorInfo s_vistaDialogEventsConstructorInfo = s_windowsFormsAssembly
.GetType("System.Windows.Forms.FileDialog+VistaDialogEvents")
.GetConstructor(c_flags, null, new[] { typeof(FileDialog) }, null);
private readonly static MethodInfo s_adviseMethodInfo = s_iFileDialogType.GetMethod("Advise");
private readonly static MethodInfo s_unAdviseMethodInfo = s_iFileDialogType.GetMethod("Unadvise");
private readonly static MethodInfo s_showMethodInfo = s_iFileDialogType.GetMethod("Show");
private static readonly MethodInfo s_adviseMethodInfo = s_iFileDialogType.GetMethod("Advise");
private static readonly MethodInfo s_unAdviseMethodInfo = s_iFileDialogType.GetMethod("Unadvise");
private static readonly MethodInfo s_showMethodInfo = s_iFileDialogType.GetMethod("Show");
public static ShowDialogResult Show(IntPtr ownerHandle, string initialDirectory, string title)
{
var openFileDialog = new OpenFileDialog
OpenFileDialog openFileDialog = new OpenFileDialog
{
AddExtension = false,
CheckFileExists = false,
@@ -99,11 +98,11 @@ namespace AndroidSideloader
Title = title
};
var iFileDialog = s_createVistaDialogMethodInfo.Invoke(openFileDialog, new object[] { });
s_onBeforeVistaDialogMethodInfo.Invoke(openFileDialog, new[] { iFileDialog });
s_setOptionsMethodInfo.Invoke(iFileDialog, new object[] { (uint)s_getOptionsMethodInfo.Invoke(openFileDialog, new object[] { }) | s_fosPickFoldersBitFlag });
var adviseParametersWithOutputConnectionToken = new[] { s_vistaDialogEventsConstructorInfo.Invoke(new object[] { openFileDialog }), 0U };
s_adviseMethodInfo.Invoke(iFileDialog, adviseParametersWithOutputConnectionToken);
object iFileDialog = s_createVistaDialogMethodInfo.Invoke(openFileDialog, new object[] { });
_ = s_onBeforeVistaDialogMethodInfo.Invoke(openFileDialog, new[] { iFileDialog });
_ = s_setOptionsMethodInfo.Invoke(iFileDialog, new object[] { (uint)s_getOptionsMethodInfo.Invoke(openFileDialog, new object[] { }) | s_fosPickFoldersBitFlag });
object[] adviseParametersWithOutputConnectionToken = new[] { s_vistaDialogEventsConstructorInfo.Invoke(new object[] { openFileDialog }), 0U };
_ = s_adviseMethodInfo.Invoke(iFileDialog, adviseParametersWithOutputConnectionToken);
try
{
@@ -116,7 +115,7 @@ namespace AndroidSideloader
}
finally
{
s_unAdviseMethodInfo.Invoke(iFileDialog, new[] { adviseParametersWithOutputConnectionToken[1] });
_ = s_unAdviseMethodInfo.Invoke(iFileDialog, new[] { adviseParametersWithOutputConnectionToken[1] });
}
}
}
@@ -124,9 +123,8 @@ namespace AndroidSideloader
// Wrap an IWin32Window around an IntPtr
private class WindowWrapper : IWin32Window
{
private readonly IntPtr _handle;
public WindowWrapper(IntPtr handle) { _handle = handle; }
public IntPtr Handle { get { return _handle; } }
public WindowWrapper(IntPtr handle) { Handle = handle; }
public IntPtr Handle { get; }
}
}

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.
}
}
}

1179
SettingsForm.Designer.cs generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,100 +1,400 @@
using JR.Utils.GUI.Forms;
using System;
using System.Windows.Forms;
namespace AndroidSideloader
{
public partial class SettingsForm : Form
{
public SettingsForm()
{
InitializeComponent();
}
private void SettingsForm_Load(object sender, EventArgs e)
{
this.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;
if (Properties.Settings.Default.BandwithLimit.Length>1)
{
BandwithTextbox.Text = Properties.Settings.Default.BandwithLimit.Remove(Properties.Settings.Default.BandwithLimit.Length - 1);
BandwithComboBox.Text = Properties.Settings.Default.BandwithLimit[Properties.Settings.Default.BandwithLimit.Length-1].ToString();
}
}
void intToolTips()
{
ToolTip checkForUpdatesToolTip = new ToolTip();
checkForUpdatesToolTip.SetToolTip(this.checkForUpdatesCheckBox, "If this is checked, the software will check for available updates");
ToolTip enableMessageBoxesToolTip = new ToolTip();
enableMessageBoxesToolTip.SetToolTip(this.enableMessageBoxesCheckBox, "If this is checked, the software will display message boxes after every completed task");
ToolTip deleteAfterInstallToolTip = new ToolTip();
deleteAfterInstallToolTip.SetToolTip(this.deleteAfterInstallCheckBox, "If this is checked, the software will delete all game files after downloading and installing a game from a remote server");
}
//Apply settings
private void applyButton_Click(object sender, EventArgs e)
{
if (BandwithTextbox.Text.Length > 0 && BandwithTextbox.Text != "0")
if (BandwithComboBox.SelectedIndex == -1)
{
FlexibleMessageBox.Show("You need to select something from the combobox");
return;
}
else
{
Properties.Settings.Default.BandwithLimit = $"{BandwithTextbox.Text.Replace(" ", "")}{BandwithComboBox.Text}";
}
else
Properties.Settings.Default.BandwithLimit = "";
Properties.Settings.Default.Save();
FlexibleMessageBox.Show("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;
}
}
}
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,254 +1,272 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using System.Net;
using System.Windows.Forms;
using JR.Utils.GUI.Forms;
namespace AndroidSideloader
{
class Sideloader
{
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 (var userJson in UsernameForm.userJsons)
{
UsernameForm.createUserJsonByName(Utilities.GeneralUtilities.randomString(16), userJson);
ADB.RunAdbCommandToString("push \"" + Environment.CurrentDirectory + $"\\{userJson}\" " + " /sdcard/");
File.Delete(userJson);
}
}
//Remove folder from device
public static ProcessOutput RemoveFolder(string path)
{
ADB.WakeDevice();
return ADB.RunAdbCommandToString($"shell rm -r {path}");
}
public static ProcessOutput RemoveFile(string path)
{
ADB.WakeDevice();
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 file)
{
ADB.WakeDevice();
ProcessOutput output = new ProcessOutput();
var commands = File.ReadAllLines(file);
foreach (string cmd in commands)
{
if (cmd.Contains("7z.exe"))
{
Program.form.ChangeTitle($"Running {cmd}");
Logger.Log($"Logging command: {cmd} from file: {file}");
output += ADB.RunCommandToString(cmd, file);
}
if (cmd.StartsWith("adb"))
{
string replacement = "";
string pattern = "adb";
if (ADB.DeviceID.Length > 1)
replacement = $"{Properties.Settings.Default.ADBPath} -s {ADB.DeviceID}";
else
replacement = $"{Properties.Settings.Default.ADBPath}";
Regex rgx = new Regex(pattern);
string result = rgx.Replace(cmd, replacement);
Program.form.ChangeTitle($"Running {result}");
Logger.Log($"Logging command: {result} from file: {file}");
output += ADB.RunAdbCommandToStringWOADB(result, file);
if (output.Error.Contains("mkdir"))
output.Error = "";
if (output.Output.Contains("reserved"))
output.Output = "";
}
}
output.Output += "Custom install successful!";
return output;
}
//Recursive sideload any apk fileD
public static ProcessOutput RecursiveOutput = new ProcessOutput();
public static void RecursiveSideload(string FolderPath)
{
try
{
foreach (string f in Directory.GetFiles(FolderPath))
{
if (Path.GetExtension(f)==".apk")
RecursiveOutput += ADB.Sideload(f);
}
foreach (string d in Directory.GetDirectories(FolderPath))
{
RecursiveSideload(d);
}
}
catch (Exception ex) { Logger.Log(ex.Message); }
}
//Recursive copy any obb folder
public static void RecursiveCopyOBB(string FolderPath)
{
try
{
foreach (string f in Directory.GetFiles(FolderPath))
{
RecursiveOutput += ADB.CopyOBB(f);
}
foreach (string d in Directory.GetDirectories(FolderPath))
{
RecursiveCopyOBB(d);
}
}
catch (Exception ex) { Logger.Log(ex.Message); }
}
//uninstalls an app
public static ProcessOutput UninstallGame(string GameName)
{
ADB.WakeDevice();
ProcessOutput output = new ProcessOutput("", "");
string packageName = Sideloader.gameNameToPackageName(GameName);
DialogResult dialogResult = FlexibleMessageBox.Show($"Are you sure you want to uninstall {packageName}? this CANNOT be undone!", "WARNING!", MessageBoxButtons.YesNo);
if (dialogResult != DialogResult.Yes)
return output;
output = ADB.UninstallPackage(packageName);
//remove both data and obb if there is any
Sideloader.RemoveFolder("/sdcard/Android/obb/" + packageName);
Sideloader.RemoveFolder("/sdcard/Android/data/" + packageName);
return output;
}
public static ProcessOutput DeleteFile(string GameName)
{
ADB.WakeDevice();
ProcessOutput output = new ProcessOutput("", "");
string packageName = Sideloader.gameNameToPackageName(GameName);
DialogResult dialogResult = FlexibleMessageBox.Show($"Are you sure you want to uninstall custom QU settings for {packageName}? this CANNOT be undone!", "WARNING!", MessageBoxButtons.YesNo);
if (dialogResult != DialogResult.Yes)
return output;
output = Sideloader.RemoveFile($"/sdcard/Android/data/{packageName}/private/Config.Json");
return output;
}
//Extracts apk from device, saves it by package name to sideloader folder
public static ProcessOutput getApk(string GameName)
{
ADB.WakeDevice();
ProcessOutput output = new ProcessOutput("", "");
string packageName = Sideloader.gameNameToPackageName(GameName);
output = ADB.RunAdbCommandToString("shell pm path " + packageName);
string apkPath = output.Output; //Get apk
apkPath = apkPath.Remove(apkPath.Length - 1);
apkPath = apkPath.Remove(0, 8); //remove package:
apkPath = apkPath.Remove(apkPath.Length - 1);
output += ADB.RunAdbCommandToString("pull " + apkPath); //pull apk
if (File.Exists(Properties.Settings.Default.MainDir + "\\" + packageName + ".apk"))
File.Delete(Properties.Settings.Default.MainDir + "\\" + packageName + ".apk");
File.Move(Properties.Settings.Default.ADBFolder + "\\base.apk", Properties.Settings.Default.MainDir + "\\" + packageName + ".apk");
return output;
}
public static string gameNameToPackageName(string gameName)
{
foreach (string[] game in SideloaderRCLONE.games)
{
if (gameName.Contains(game[SideloaderRCLONE.GameNameIndex]) || gameName.Contains(game[SideloaderRCLONE.PackageNameIndex]))
return game[SideloaderRCLONE.PackageNameIndex];
}
return gameName;
}
//Downloads the required files
public static void downloadFiles()
{
var client = new WebClient();
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
try
{
if (!File.Exists("Sideloader Launcher.exe"))
client.DownloadFile("https://github.com/nerdunit/androidsideloader/raw/master/Sideloader%20Launcher.exe", "Sideloader Launcher.exe");
if (!Directory.Exists(ADB.adbFolderPath)) //if there is no adb folder, download and extract
{
client.DownloadFile("https://github.com/nerdunit/androidsideloader/raw/master/adb.7z", "adb.7z");
Utilities.Zip.ExtractFile(Environment.CurrentDirectory + "\\adb.7z", Environment.CurrentDirectory);
File.Delete("adb.7z");
}
if (!Directory.Exists(Environment.CurrentDirectory + "\\rclone"))
{
string url;
if (Environment.Is64BitOperatingSystem)
url = "https://downloads.rclone.org/v1.55.1/rclone-v1.55.1-windows-amd64.zip";
else
url = "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
{
FlexibleMessageBox.Show("Your internet is not working properly or rclone/github servers are down, some files may be missing (adb, rclone or launcher)");
}
}
}
}
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";
//push user.json to device (required for some devices like oculus quest)
public static void PushUserJsons()
{
foreach (string userJson in UsernameForm.userJsons)
{
UsernameForm.createUserJsonByName(Utilities.GeneralUtilities.randomString(16), userJson);
_ = ADB.RunAdbCommandToString("push \"" + Environment.CurrentDirectory + $"\\{userJson}\" " + " /sdcard/");
File.Delete(userJson);
}
}
//List of all installed package names from connected device
//public static List<string> InstalledPackageNames = new List<string>(); //Remove folder from device
public static ProcessOutput RemoveFolder(string 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)
{
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)
{
ProcessOutput output = new ProcessOutput();
string[] commands = File.ReadAllLines(path);
string currfolder = Path.GetDirectoryName(path);
string[] zipz = Directory.GetFiles(currfolder, "*.7z", SearchOption.AllDirectories);
foreach (string zip in zipz)
{
Utilities.Zip.ExtractFile($"{zip}", currfolder);
}
foreach (string cmd in commands)
{
if (cmd.StartsWith("adb"))
{
string replacement = "";
string pattern = "adb";
replacement = ADB.DeviceID.Length > 1
? $"{settings.ADBPath} -s {ADB.DeviceID}"
: $"{settings.ADBPath}";
Regex rgx = new Regex(pattern);
string result = rgx.Replace(cmd, replacement);
Program.form.changeTitle($"Running {result}");
_ = Logger.Log($"Logging command: {result} from file: {path}");
output += ADB.RunAdbCommandToStringWOADB(result, path);
if (output.Error.Contains("mkdir"))
{
output.Error = "";
}
if (output.Output.Contains("reserved"))
{
output.Output = "";
}
}
}
output.Output += "Custom install successful!";
return output;
}
//Recursive sideload any apk file
public static ProcessOutput RecursiveOutput = new ProcessOutput();
public static async Task RecursiveSideloadAsync(
string FolderPath,
Action<float, TimeSpan?> progressCallback = null,
Action<string> statusCallback = null)
{
try
{
foreach (string f in Directory.GetFiles(FolderPath))
{
if (Path.GetExtension(f) == ".apk")
{
string gameName = Path.GetFileNameWithoutExtension(f);
statusCallback?.Invoke(gameName);
RecursiveOutput += await ADB.SideloadWithProgressAsync(f, progressCallback, statusCallback, "", gameName);
}
}
foreach (string d in Directory.GetDirectories(FolderPath))
{
await RecursiveSideloadAsync(d, progressCallback, statusCallback);
}
}
catch (Exception ex) { _ = Logger.Log(ex.Message, LogLevel.ERROR); }
}
//Recursive copy any obb folder
public static async Task RecursiveCopyOBBAsync(
string FolderPath,
Action<float, TimeSpan?> progressCallback = null,
Action<string> statusCallback = null)
{
try
{
foreach (string d in Directory.GetDirectories(FolderPath))
{
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, LogLevel.ERROR); }
}
// Removes the game package and its OBB + Data Folders.
public static ProcessOutput UninstallGame(string packagename)
{
ProcessOutput output = ADB.UninstallPackage(packagename);
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)
{
ProcessOutput output = new ProcessOutput("", "");
string packageName = Sideloader.gameNameToPackageName(GameName);
DialogResult dialogResult = FlexibleMessageBox.Show(Program.form, $"Are you sure you want to uninstall custom QU settings for {packageName}? this CANNOT be undone!", "WARNING!", MessageBoxButtons.YesNo);
if (dialogResult != DialogResult.Yes)
{
return output;
}
output = Sideloader.RemoveFile($"/sdcard/Android/data/{packageName}/private/Config.Json");
return output;
}
//Extracts apk from device, saves it by package name to sideloader folder
public static ProcessOutput getApk(string GameName)
{
_ = new ProcessOutput("", "");
string packageName = Sideloader.gameNameToPackageName(GameName);
ProcessOutput output = ADB.RunAdbCommandToString("shell pm path " + packageName);
string apkPath = output.Output; //Get apk
apkPath = apkPath.Remove(apkPath.Length - 1);
apkPath = apkPath.Remove(0, 8); //remove package:
apkPath = apkPath.Remove(apkPath.Length - 1);
if (File.Exists($"{settings.ADBFolder}\\base.apk"))
{
File.Delete($"{settings.ADBFolder}\\base.apk");
}
if (File.Exists($"{settings.MainDir}\\{packageName}\\{packageName}.apk"))
{
File.Delete($"{settings.MainDir}\\{packageName}\\{packageName}.apk");
}
output += ADB.RunAdbCommandToString("pull " + apkPath); //pull apk
if (Directory.Exists($"{settings.MainDir}\\{packageName}"))
{
FileSystemUtilities.TryDeleteDirectory($"{settings.MainDir}\\{packageName}");
}
_ = Directory.CreateDirectory($"{settings.MainDir}\\{packageName}");
File.Move($"{settings.ADBFolder}\\base.apk", $"{settings.MainDir}\\{packageName}\\{packageName}.apk");
return output;
}
public static string gameNameToPackageName(string gameName)
{
foreach (string[] game in SideloaderRCLONE.games)
{
if (gameName.Equals(game[SideloaderRCLONE.GameNameIndex]) || gameName.Equals(game[SideloaderRCLONE.ReleaseNameIndex]))
return game[SideloaderRCLONE.PackageNameIndex];
}
return gameName;
}
public static string gameNameToVersionCode(string gameName)
{
foreach (string[] game in SideloaderRCLONE.games)
{
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]) || gameName.Equals(game[SideloaderRCLONE.ReleaseNameIndex]))
return game[SideloaderRCLONE.GameNameIndex];
}
return gameName;
}
public static string PackageNameToSimpleName(string packageName)
{
foreach (string[] game in SideloaderRCLONE.games)
{
if (packageName.Contains(game[SideloaderRCLONE.PackageNameIndex]))
return game[SideloaderRCLONE.GameNameIndex];
}
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,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AndroidSideloader
namespace AndroidSideloader
{
public class ProcessOutput
{

View File

@@ -1,26 +1,27 @@
using System;
using AndroidSideloader.Utilities;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Net;
using System.Security.Cryptography;
namespace AndroidSideloader
{
class SideloaderRCLONE
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 ReleaseAPKPathIndex = 2;
public static int PackageNameIndex = 3;
public static int VersionCodeIndex = 4;
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
@@ -32,105 +33,300 @@ namespace AndroidSideloader
*/
public static List<string[]> games = new List<string[]>();
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)
{
_ = Logger.Log($"Updating Nouns");
_ = RCLONE.runRcloneCommand_DownloadConfig($"sync \"{remote}:{RcloneGamesFolder}/.meta/nouns\" \"{Nouns}\"");
}
public static void UpdateGamePhotos(string remote)
{
RCLONE.runRcloneCommand($"sync \"{remote}:{RcloneGamesFolder}/.meta/thumbnails\" \"{ThumbnailsFolder}\"");
_ = Logger.Log($"Updating Thumbnails");
_ = RCLONE.runRcloneCommand_DownloadConfig($"sync \"{remote}:{RcloneGamesFolder}/.meta/thumbnails\" \"{ThumbnailsFolder}\" --transfers 10");
}
public static void UpdateGameNotes(string remote)
{
RCLONE.runRcloneCommand($"sync \"{remote}:{RcloneGamesFolder}/.meta/notes\" \"{NotesFolder}\"");
_ = Logger.Log($"Updating Game Notes");
_ = RCLONE.runRcloneCommand_DownloadConfig($"sync \"{remote}:{RcloneGamesFolder}/.meta/notes\" \"{NotesFolder}\"");
}
public static void RefreshRemotes()
public static void UpdateMetadataFromPublic()
{
RemotesList.Clear();
var remotes = RCLONE.runRcloneCommand("listremotes").Output.Split('\n');
_ = Logger.Log($"Downloading Metadata");
string rclonecommand =
$"sync \":http:/meta.7z\" \"{Environment.CurrentDirectory}\"";
_ = RCLONE.runRcloneCommand_PublicConfig(rclonecommand);
}
Logger.Log("Loaded following remotes: ");
foreach (string r in remotes)
public static void ProcessMetadataFromPublic()
{
try
{
if (r.Length > 1)
var sw = Stopwatch.StartNew();
string currentDir = Environment.CurrentDirectory;
string metaRoot = Path.Combine(currentDir, "meta");
string metaArchive = Path.Combine(currentDir, "meta.7z");
string metaDotMeta = Path.Combine(metaRoot, ".meta");
// Check if archive exists and is newer than existing metadata
if (!File.Exists(metaArchive))
{
var remote = r.Remove(r.Length - 1);
if (remote.Contains("mirror"))
Logger.Log("meta.7z not found, skipping extraction", LogLevel.WARNING);
return;
}
// 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))
{
var archiveTime = File.GetLastWriteTimeUtc(metaArchive);
var gameListTime = File.GetLastWriteTimeUtc(gameListPath);
// If game list is newer than archive, skip extraction
if (gameListTime > archiveTime && games.Count > 0)
{
Logger.Log(remote);
RemotesList.Add(remote);
Logger.Log($"Metadata already up-to-date, skipping extraction");
return;
}
}
_ = 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)
{
_ = Logger.Log(e.Message);
_ = Logger.Log(e.StackTrace);
}
}
public static void initGames(string remote)
{
_ = Logger.Log($"Initializing Games List");
gameProperties.Clear();
games.Clear();
string tempGameList = RCLONE.runRcloneCommand($"cat \"{remote}:{RcloneGamesFolder}/GameList.txt\"").Output;
// Fetch once, then process as lines
string tempGameList = RCLONE.runRcloneCommand_DownloadConfig($"cat \"{remote}:{RcloneGamesFolder}/VRP-GameList.txt\"").Output;
if (MainForm.debugMode)
File.WriteAllText("GamesList.txt", tempGameList);
string gamePropertiesLine = Utilities.StringUtilities.RemoveEverythingAfterFirst(tempGameList, "\n");
foreach (string gameProperty in gamePropertiesLine.Split(';'))
{
gameProperties.Add(gameProperty);
// Avoid redundant disk I/O: write only if non-empty
if (!string.IsNullOrEmpty(tempGameList))
{
File.WriteAllText("VRP-GamesList.txt", tempGameList);
}
}
tempGameList = Utilities.StringUtilities.RemoveEverythingBeforeFirst(tempGameList, "\n");
foreach (string game in tempGameList.Split('\n'))
if (!string.IsNullOrEmpty(tempGameList))
{
if (game.Length > 1)
games.Add(game.Split(';'));
}
bool isFirstLine = true;
foreach (var line in SplitLines(tempGameList))
{
if (isFirstLine)
{
isFirstLine = false; // skip header
continue;
}
//Output
//Console.WriteLine("Headers:");
//foreach (string s in gameProperties)
//{
// Console.WriteLine($"gameProperty: {s}");
//}
foreach (string[] s in games)
{
string output = "";
for (int i = 0; i < gameProperties.Count; i++)
output += s[i] + " ";
if (string.IsNullOrWhiteSpace(line))
{
continue;
}
var splitGame = line.Split(new[] { ';' }, StringSplitOptions.None);
if (splitGame.Length > 1)
{
games.Add(splitGame);
}
}
}
}
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
public static long GetFolderSize(string FolderName, string remote)
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
public static void updateUploadConfig()
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12
| SecurityProtocolType.Ssl3;
_ = Logger.Log($"Attempting to Update Upload Config");
try
{
string configUrl = "https://vrpirates.wiki/downloads/vrp.upload.config";
// 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($"Retrieved updated config from: {configUrl}");
// 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}", LogLevel.ERROR);
}
}
// 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)
{
// Avoid exceptions when directory is missing
if (!Directory.Exists(path))
return;
try
{
// Use Windows rd command which is ~10x faster than .NET's recursive delete
var psi = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = $"/c rd /s /q \"{path}\"",
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true
};
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 ex)
{
Logger.Log($"Fast delete failed for {path}: {ex.Message}", LogLevel.WARNING);
// Fallback to standard .NET delete
FallbackDelete(path);
}
}
// Fallback delete method using standard .NET
private static void FallbackDelete(string path)
{
try
{
dynamic results = JsonConvert.DeserializeObject<dynamic>(RCLONE.runRcloneCommand($"size \"{remote}:{RcloneGamesFolder}/{FolderName}\" --json").Output);
long gameSize = results.bytes.ToObject<long>();
return gameSize / 1000000;
if (Directory.Exists(path))
{
FileSystemUtilities.TryDeleteDirectory(path);
}
}
catch (Exception ex)
{
Logger.Log($"Fallback delete also failed for {path}: {ex.Message}", LogLevel.ERROR);
}
catch { return 0; }
}
public static void updateConfig(string remote)
// Move directory only if source exists
private static void MoveIfExists(string sourceDir, string destDir)
{
string localHash = "";
try { localHash = File.ReadAllText(Environment.CurrentDirectory + "\\rclone\\hash.txt"); } catch { } //file may not exist
string hash = RCLONE.runRcloneCommand($"md5sum \"{remote}:Quest Homebrew/Sideloading Methods/1. Rookie Sideloader - VRP Edition/VRP.download.config\"").Output;
try { hash = hash.Substring(0, hash.LastIndexOf(" ")); } catch { return; } //remove stuff after hash
Debug.WriteLine("The local file hash is " + localHash + " and the current a file hash is " + hash);
if (!string.Equals(localHash, hash))
if (Directory.Exists(sourceDir))
{
RCLONE.runRcloneCommand(string.Format($"copy \"{remote}:Quest Homebrew/Sideloading Methods/1. Rookie Sideloader - VRP Edition/VRP.download.config\" \"{Environment.CurrentDirectory}\\rclone\""));
RCLONE.killRclone();
File.WriteAllText(Environment.CurrentDirectory + "\\rclone\\hash.txt", hash);
// 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,17 +1,52 @@
using System.IO;
using AndroidSideloader.Utilities;
using System;
using System.IO;
using System.Management;
using System.Security.Cryptography;
using System.Text;
namespace AndroidSideloader
{
class SideloaderUtilities
internal class SideloaderUtilities
{
private static readonly SettingsManager settings = SettingsManager.Instance;
public static bool CheckFolderIsObb(string path)
{
string[] files = Directory.GetFiles(path);
foreach (string file in files)
if (file.EndsWith(".obb"))
{
if (file.EndsWith(".obb") || (Path.GetDirectoryName(file).Contains(".") && !Path.GetDirectoryName(file).Contains("_data")))
{
return true;
}
}
return false;
}
private static string uuid = null;
public static string UUID()
{
uuid = settings.UUID;
if (string.IsNullOrEmpty(uuid) != true)
{
return uuid;
}
var bytes = new byte[16];
using (var rng = new RNGCryptoServiceProvider())
{
rng.GetBytes(bytes);
}
uuid = BitConverter.ToString(bytes).Replace("-", "");
settings.UUID = uuid;
settings.Save();
return uuid;
}
}
}

118
SpoofForm.Designer.cs generated
View File

@@ -1,118 +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.RandomizeButton = new System.Windows.Forms.Button();
this.progressBar1 = new System.Windows.Forms.ProgressBar();
this.SpoofButton = new System.Windows.Forms.Button();
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;
//
// RandomizeButton
//
this.RandomizeButton.BackColor = global::AndroidSideloader.Properties.Settings.Default.SubButtonColor;
this.RandomizeButton.DataBindings.Add(new System.Windows.Forms.Binding("Font", global::AndroidSideloader.Properties.Settings.Default, "FontStyle", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.RandomizeButton.DataBindings.Add(new System.Windows.Forms.Binding("ForeColor", global::AndroidSideloader.Properties.Settings.Default, "FontColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.RandomizeButton.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "SubButtonColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.RandomizeButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.RandomizeButton.Font = global::AndroidSideloader.Properties.Settings.Default.FontStyle;
this.RandomizeButton.ForeColor = global::AndroidSideloader.Properties.Settings.Default.FontColor;
this.RandomizeButton.Location = new System.Drawing.Point(12, 72);
this.RandomizeButton.Name = "RandomizeButton";
this.RandomizeButton.Size = new System.Drawing.Size(110, 42);
this.RandomizeButton.TabIndex = 2;
this.RandomizeButton.Text = "Randomize";
this.RandomizeButton.UseVisualStyleBackColor = false;
this.RandomizeButton.Click += new System.EventHandler(this.RandomizeButton_Click);
//
// 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.BackColor = global::AndroidSideloader.Properties.Settings.Default.SubButtonColor;
this.SpoofButton.DataBindings.Add(new System.Windows.Forms.Binding("Font", global::AndroidSideloader.Properties.Settings.Default, "FontStyle", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.SpoofButton.DataBindings.Add(new System.Windows.Forms.Binding("ForeColor", global::AndroidSideloader.Properties.Settings.Default, "FontColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.SpoofButton.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "SubButtonColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.SpoofButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.SpoofButton.Font = global::AndroidSideloader.Properties.Settings.Default.FontStyle;
this.SpoofButton.ForeColor = global::AndroidSideloader.Properties.Settings.Default.FontColor;
this.SpoofButton.Location = new System.Drawing.Point(176, 72);
this.SpoofButton.Name = "SpoofButton";
this.SpoofButton.Size = new System.Drawing.Size(110, 42);
this.SpoofButton.TabIndex = 4;
this.SpoofButton.Text = "Spoof!";
this.SpoofButton.UseVisualStyleBackColor = false;
this.SpoofButton.Click += new System.EventHandler(this.SpoofButton_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.ClientSize = new System.Drawing.Size(300, 131);
this.Controls.Add(this.SpoofButton);
this.Controls.Add(this.progressBar1);
this.Controls.Add(this.RandomizeButton);
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.Button RandomizeButton;
private System.Windows.Forms.ProgressBar progressBar1;
private System.Windows.Forms.Button SpoofButton;
}
}

View File

@@ -1,74 +0,0 @@
using JR.Utils.GUI.Forms;
using System;
using System.Threading.Tasks;
using System.Threading;
using System.Windows.Forms;
using Spoofer;
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);
});
t1.IsBackground = true;
t1.Start();
while (t1.IsAlive)
await Task.Delay(100);
progressBar1.Style = ProgressBarStyle.Continuous;
if (output.Contains("is not recognized as an internal or external command"))
FlexibleMessageBox.Show(Sideloader.SpooferWarning);
else
FlexibleMessageBox.Show($"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();
}
}
}

235
ThemeForm.Designer.cs generated
View File

@@ -1,235 +0,0 @@
namespace AndroidSideloader
{
partial class themeForm
{
/// <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.colorDialog1 = new System.Windows.Forms.ColorDialog();
this.button1 = new System.Windows.Forms.Button();
this.button4 = new System.Windows.Forms.Button();
this.button5 = new System.Windows.Forms.Button();
this.button7 = new System.Windows.Forms.Button();
this.button8 = new System.Windows.Forms.Button();
this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog();
this.button9 = new System.Windows.Forms.Button();
this.button10 = new System.Windows.Forms.Button();
this.fontDialog1 = new System.Windows.Forms.FontDialog();
this.button3 = new System.Windows.Forms.Button();
this.button11 = new System.Windows.Forms.Button();
this.button12 = new System.Windows.Forms.Button();
this.button13 = new System.Windows.Forms.Button();
this.openThemeDialog = new System.Windows.Forms.OpenFileDialog();
this.button2 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// colorDialog1
//
this.colorDialog1.AnyColor = true;
this.colorDialog1.FullOpen = true;
//
// button1
//
this.button1.Location = new System.Drawing.Point(13, 12);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(128, 23);
this.button1.TabIndex = 0;
this.button1.Text = "Set backgorund color";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// button4
//
this.button4.Location = new System.Drawing.Point(13, 125);
this.button4.Name = "button4";
this.button4.Size = new System.Drawing.Size(128, 23);
this.button4.TabIndex = 0;
this.button4.Text = "Set button color";
this.button4.UseVisualStyleBackColor = true;
this.button4.Click += new System.EventHandler(this.button4_Click);
//
// button5
//
this.button5.Location = new System.Drawing.Point(13, 181);
this.button5.Name = "button5";
this.button5.Size = new System.Drawing.Size(128, 23);
this.button5.TabIndex = 0;
this.button5.Text = "Set combobox color";
this.button5.UseVisualStyleBackColor = true;
this.button5.Click += new System.EventHandler(this.button5_Click);
//
// button7
//
this.button7.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.button7.Location = new System.Drawing.Point(185, 11);
this.button7.Name = "button7";
this.button7.Size = new System.Drawing.Size(93, 52);
this.button7.TabIndex = 1;
this.button7.Text = "Save";
this.button7.UseVisualStyleBackColor = true;
this.button7.Click += new System.EventHandler(this.button7_Click);
//
// button8
//
this.button8.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.button8.Location = new System.Drawing.Point(185, 69);
this.button8.Name = "button8";
this.button8.Size = new System.Drawing.Size(93, 52);
this.button8.TabIndex = 2;
this.button8.Text = "Reset";
this.button8.UseVisualStyleBackColor = true;
this.button8.Click += new System.EventHandler(this.button8_Click);
//
// openFileDialog1
//
this.openFileDialog1.Filter = "Images|*.png;*.jpg;*.bmp;*.gif";
//
// button9
//
this.button9.Location = new System.Drawing.Point(13, 40);
this.button9.Name = "button9";
this.button9.Size = new System.Drawing.Size(128, 23);
this.button9.TabIndex = 3;
this.button9.Text = "Set background picture";
this.button9.UseVisualStyleBackColor = true;
this.button9.Click += new System.EventHandler(this.button9_Click);
//
// button10
//
this.button10.Location = new System.Drawing.Point(13, 97);
this.button10.Name = "button10";
this.button10.Size = new System.Drawing.Size(128, 23);
this.button10.TabIndex = 4;
this.button10.Text = "Set font style";
this.button10.UseVisualStyleBackColor = true;
this.button10.Click += new System.EventHandler(this.button10_Click);
//
// fontDialog1
//
this.fontDialog1.Color = System.Drawing.Color.White;
this.fontDialog1.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
//
// button3
//
this.button3.Location = new System.Drawing.Point(13, 69);
this.button3.Name = "button3";
this.button3.Size = new System.Drawing.Size(128, 23);
this.button3.TabIndex = 5;
this.button3.Text = "Set font color";
this.button3.UseVisualStyleBackColor = true;
this.button3.Click += new System.EventHandler(this.button3_Click);
//
// button11
//
this.button11.Location = new System.Drawing.Point(13, 153);
this.button11.Name = "button11";
this.button11.Size = new System.Drawing.Size(128, 23);
this.button11.TabIndex = 6;
this.button11.Text = "Set sub-button color";
this.button11.UseVisualStyleBackColor = true;
this.button11.Click += new System.EventHandler(this.button11_Click);
//
// button12
//
this.button12.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.button12.Location = new System.Drawing.Point(185, 127);
this.button12.Name = "button12";
this.button12.Size = new System.Drawing.Size(93, 52);
this.button12.TabIndex = 7;
this.button12.Text = "Export Theme";
this.button12.UseVisualStyleBackColor = true;
this.button12.Click += new System.EventHandler(this.button12_Click);
//
// button13
//
this.button13.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.button13.Location = new System.Drawing.Point(185, 185);
this.button13.Name = "button13";
this.button13.Size = new System.Drawing.Size(93, 52);
this.button13.TabIndex = 8;
this.button13.Text = "Import theme";
this.button13.UseVisualStyleBackColor = true;
this.button13.Click += new System.EventHandler(this.button13_Click);
//
// openThemeDialog
//
this.openThemeDialog.Filter = "Text Files|*.txt";
//
// button2
//
this.button2.Location = new System.Drawing.Point(13, 210);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(128, 23);
this.button2.TabIndex = 9;
this.button2.Text = "Set textbox color";
this.button2.UseVisualStyleBackColor = true;
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// themeForm
//
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(290, 248);
this.Controls.Add(this.button2);
this.Controls.Add(this.button13);
this.Controls.Add(this.button12);
this.Controls.Add(this.button11);
this.Controls.Add(this.button3);
this.Controls.Add(this.button10);
this.Controls.Add(this.button9);
this.Controls.Add(this.button8);
this.Controls.Add(this.button7);
this.Controls.Add(this.button5);
this.Controls.Add(this.button4);
this.Controls.Add(this.button1);
this.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "BackColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.Name = "themeForm";
this.Text = "Create Your Theme";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.ColorDialog colorDialog1;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button4;
private System.Windows.Forms.Button button5;
private System.Windows.Forms.Button button7;
private System.Windows.Forms.Button button8;
private System.Windows.Forms.OpenFileDialog openFileDialog1;
private System.Windows.Forms.Button button9;
private System.Windows.Forms.Button button10;
private System.Windows.Forms.FontDialog fontDialog1;
private System.Windows.Forms.Button button3;
private System.Windows.Forms.Button button11;
private System.Windows.Forms.Button button12;
private System.Windows.Forms.Button button13;
private System.Windows.Forms.OpenFileDialog openThemeDialog;
private System.Windows.Forms.Button button2;
}
}

View File

@@ -1,171 +0,0 @@
using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using System.Collections.Specialized;
namespace AndroidSideloader
{
public partial class themeForm : Form
{
public themeForm()
{
InitializeComponent();
}
//Code made by @Gotard#9164 from discord (id 352745203322585088)
//Steam profile: https://steamcommunity.com/id/GotardPL/
private void button4_Click(object sender, EventArgs e)
{
if (colorDialog1.ShowDialog() == DialogResult.OK)
Properties.Settings.Default.ButtonColor = colorDialog1.Color;
}
private void button1_Click(object sender, EventArgs e)
{
if (colorDialog1.ShowDialog() == DialogResult.OK)
Properties.Settings.Default.BackColor = colorDialog1.Color;
}
private void button7_Click(object sender, EventArgs e)
{
Properties.Settings.Default.Save();
this.Close();
}
private void button8_Click(object sender, EventArgs e)
{
Properties.Settings.Default.BackColor = Color.FromArgb(45,45,45);
Properties.Settings.Default.ComboBoxColor = Color.FromArgb(45, 45, 45);
Properties.Settings.Default.TextBoxColor = Color.FromArgb(45,45,45);
Properties.Settings.Default.ButtonColor = SystemColors.ActiveCaptionText;
Properties.Settings.Default.SubButtonColor=Color.FromArgb(64, 64, 64);
Properties.Settings.Default.BackPicturePath = "";
Properties.Settings.Default.FontStyle = new Font("Microsoft Sans Serif", 11, FontStyle.Regular);
Properties.Settings.Default.FontColor = Color.White;
Properties.Settings.Default.Save();
}
private void button5_Click(object sender, EventArgs e)
{
if (colorDialog1.ShowDialog() == DialogResult.OK)
Properties.Settings.Default.ComboBoxColor = colorDialog1.Color;
}
private void button9_Click(object sender, EventArgs e)
{
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
String extension = Path.GetExtension(openFileDialog1.FileName);
if (File.Exists(Environment.CurrentDirectory + "\\pic" + extension))
File.Delete(Environment.CurrentDirectory + "\\pic" + extension);
File.Copy(openFileDialog1.FileName, Environment.CurrentDirectory + "\\pic" + extension);
Properties.Settings.Default.BackPicturePath = Environment.CurrentDirectory + "\\pic" + extension ;
}
}
private void button10_Click(object sender, EventArgs e)
{
if (fontDialog1.ShowDialog() == DialogResult.OK)
{
Properties.Settings.Default.FontStyle = fontDialog1.Font;
}
}
private void button3_Click(object sender, EventArgs e)
{
if (colorDialog1.ShowDialog() == DialogResult.OK)
Properties.Settings.Default.FontColor = colorDialog1.Color;
}
private void button11_Click(object sender, EventArgs e)
{
if (colorDialog1.ShowDialog() == DialogResult.OK)
Properties.Settings.Default.SubButtonColor = colorDialog1.Color;
}
private void button12_Click(object sender, EventArgs e)
{
String BackColor = ColorTranslator.ToHtml(Properties.Settings.Default.BackColor);
String TextBoxColor = ColorTranslator.ToHtml(Properties.Settings.Default.TextBoxColor);
String ComboBoxColor = ColorTranslator.ToHtml(Properties.Settings.Default.ComboBoxColor);
String ButtonColor = ColorTranslator.ToHtml(Properties.Settings.Default.ButtonColor);
String SubButtonColor = ColorTranslator.ToHtml(Properties.Settings.Default.SubButtonColor);
String FontColor = ColorTranslator.ToHtml(Properties.Settings.Default.FontColor);
var cvt = new FontConverter();
string FontStyle = cvt.ConvertToString(Properties.Settings.Default.FontStyle);
int i;
if (File.Exists(Environment.CurrentDirectory + "\\theme.txt"))
{
if (File.Exists(Environment.CurrentDirectory + "\\theme11.txt"))
MessageBox.Show("You can't export more than 12 themes, sorry :(");
else
{
for (i = 1; i <= 10; i++)
{
if (File.Exists(Environment.CurrentDirectory + "\\theme" + i + ".txt"))
continue;
else
{
break;
}
}
File.WriteAllText(Environment.CurrentDirectory + "\\theme" + i + ".txt", "#SideloaderTheme# \n" + BackColor + "\n" + ButtonColor + "\n" + SubButtonColor + "\n"
+ TextBoxColor + "\n" + ComboBoxColor + "\n" + FontColor + "\n" + FontStyle);
MessageBox.Show("Theme exported as theme" + i + ".txt");
}
}
else
{
File.WriteAllText(Environment.CurrentDirectory + "\\theme.txt", "#SideloaderTheme# \n" + BackColor + "\n" + ButtonColor + "\n" + SubButtonColor + "\n"
+ TextBoxColor + "\n" + ComboBoxColor + "\n" + FontColor + "\n" + FontStyle);
MessageBox.Show("Theme exported as theme.txt");
}
}
private void button13_Click(object sender, EventArgs e)
{
openThemeDialog.InitialDirectory = Environment.CurrentDirectory;
if (openThemeDialog.ShowDialog() == DialogResult.OK) {
using (StreamReader sr = new StreamReader(openThemeDialog.FileName))
{
StringCollection myCol = new StringCollection();
myCol.AddRange(File.ReadAllLines(openThemeDialog.FileName));
if (myCol.Contains("#SideloaderTheme# "))
{
String[] settings = new String[myCol.Count];
myCol.CopyTo(settings, 0);
Color BackColor = ColorTranslator.FromHtml(settings[1]);
Color ButtonColor = ColorTranslator.FromHtml(settings[2]);
Color SubButtonColor = ColorTranslator.FromHtml(settings[3]);
Color TextBoxColor = ColorTranslator.FromHtml(settings[4]);
Color ComboBoxColor = ColorTranslator.FromHtml(settings[5]);
Color FontColor = ColorTranslator.FromHtml(settings[6]);
Properties.Settings.Default.BackColor = BackColor;
Properties.Settings.Default.ButtonColor = ButtonColor;
Properties.Settings.Default.SubButtonColor = SubButtonColor;
Properties.Settings.Default.TextBoxColor = TextBoxColor;
Properties.Settings.Default.ComboBoxColor = ComboBoxColor;
Properties.Settings.Default.FontColor = FontColor;
System.ComponentModel.TypeConverter converter =
System.ComponentModel.TypeDescriptor.GetConverter(typeof(Font));
var cvt = new FontConverter();
Font f = cvt.ConvertFromString(settings[7]) as Font;
Properties.Settings.Default.FontStyle = f;
}
else
MessageBox.Show("The file you've selected is not a Rookie's Sideloader theme file!");
}
}
}
private void button2_Click(object sender, EventArgs e)
{
if (colorDialog1.ShowDialog() == DialogResult.OK)
Properties.Settings.Default.TextBoxColor = colorDialog1.Color;
}
}
}

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);
}
}
}

44
Transparenter.cs Normal file
View File

@@ -0,0 +1,44 @@
using System.Drawing;
using System.Windows.Forms;
namespace AndroidSideloader
{
public class Transparenter
{
public static void MakeTransparent(Control control, Graphics g)
{
Control parent = control.Parent;
if (parent == null)
{
return;
}
Rectangle bounds = control.Bounds;
Control.ControlCollection siblings = parent.Controls;
int index = siblings.IndexOf(control);
Bitmap behind = null;
for (int i = siblings.Count - 1; i > index; i--)
{
Control c = siblings[i];
if (!c.Bounds.IntersectsWith(bounds))
{
continue;
}
if (behind == null)
{
behind = new Bitmap(control.Parent.ClientSize.Width, control.Parent.ClientSize.Height);
}
c.DrawToBitmap(behind, c.Bounds);
}
if (behind == null)
{
return;
}
g.DrawImage(behind, control.ClientRectangle, bounds, GraphicsUnit.Pixel);
behind.Dispose();
}
}
}

190
UpdateForm.Designer.cs generated Normal file
View File

@@ -0,0 +1,190 @@
namespace AndroidSideloader
{
partial class UpdateForm
{
/// <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.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.panel1.SuspendLayout();
this.panel3.SuspendLayout();
this.SuspendLayout();
//
// panel1
//
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.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(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.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.Location = new System.Drawing.Point(20, 50);
this.panel3.Name = "panel3";
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 = System.Drawing.Color.FromArgb(((int)(((byte)(28)))), ((int)(((byte)(32)))), ((int)(((byte)(38)))));
this.UpdateTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None;
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, 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(416, 180);
this.UpdateTextBox.TabIndex = 1;
this.UpdateTextBox.Text = "";
this.UpdateTextBox.TextChanged += new System.EventHandler(this.UpdateTextBox_TextChanged);
this.UpdateTextBox.MouseDown += new System.Windows.Forms.MouseEventHandler(this.UpdateForm_MouseDown);
this.UpdateTextBox.MouseMove += new System.Windows.Forms.MouseEventHandler(this.UpdateForm_MouseMove);
this.UpdateTextBox.MouseUp += new System.Windows.Forms.MouseEventHandler(this.UpdateForm_MouseUp);
//
// 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("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(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("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(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.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(73, 15);
this.SkipUpdate.TabIndex = 4;
this.SkipUpdate.Text = "Skip for now";
this.SkipUpdate.Click += new System.EventHandler(this.SkipUpdate_Click);
//
// UpdateForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
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.None;
this.Name = "UpdateForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.UpdateForm_MouseDown);
this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.UpdateForm_MouseMove);
this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.UpdateForm_MouseUp);
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
this.panel3.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Label SkipUpdate;
private System.Windows.Forms.Label CurVerLabel;
private System.Windows.Forms.Label UpdateVerLabel;
private System.Windows.Forms.Panel panel3;
private System.Windows.Forms.RichTextBox UpdateTextBox;
private System.Windows.Forms.Panel panel1;
private RoundButton YesUpdate;
}
}

250
UpdateForm.cs Normal file
View File

@@ -0,0 +1,250 @@
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace AndroidSideloader
{
public partial class UpdateForm : Form
{
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 = $"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();
Close();
}
private void SkipUpdate_Click(object sender, EventArgs e)
{
Close();
}
private void UpdateTextBox_TextChanged(object sender, EventArgs e)
{
}
private void UpdateForm_MouseDown(object sender, MouseEventArgs e)
{
mouseDown = true;
lastLocation = e.Location;
}
private void UpdateForm_MouseMove(object sender, MouseEventArgs e)
{
if (mouseDown)
{
Location = new Point(
Location.X - lastLocation.X + e.X, Location.Y - lastLocation.Y + e.Y);
Update();
}
}
private void UpdateForm_MouseUp(object sender, MouseEventArgs e)
{
mouseDown = false;
}
}
}

120
UpdateForm.resx Normal file
View File

@@ -0,0 +1,120 @@
<?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>

View File

@@ -1,76 +1,110 @@
using System;
using System.Text;
using System.Diagnostics;
using JR.Utils.GUI.Forms;
using System.Net;
using System.Windows.Forms;
using System.Net.Http;
using System.IO;
using AndroidSideloader;
using System.Threading.Tasks;
namespace AndroidSideloader
{
class Updater
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";
<<<<<<< HEAD
static readonly public string LocalVersion = "2.1.1HF1";
=======
static readonly public string LocalVersion = "2.1.1";
>>>>>>> + Added optional Wake on Wifi setting so Wireless ADB will still connect as long as device is not powered off or dead.
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()
{
HttpClient client = new HttpClient();
try
using (HttpClient client = new HttpClient())
{
currentVersion = client.GetStringAsync($"{RawGitHubUrl}/master/version").Result;
changelog = client.GetStringAsync($"{RawGitHubUrl}/master/changelog.txt").Result;
client.Dispose();
try
{
currentVersion = await client.GetStringAsync($"{RawGitHubUrl}/master/version");
changelog = await client.GetStringAsync($"{RawGitHubUrl}/master/changelog.txt");
currentVersion = currentVersion.Trim();
}
catch (HttpRequestException)
{
return false;
}
}
catch { return false; }
return LocalVersion != currentVersion;
// Compare versions - only return true if server version is greater than local version
return CompareVersions(currentVersion, LocalVersion.Trim()) > 0;
}
//Call this to ask the user if they want to update
public static void Update()
// 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)
{
RawGitHubUrl = $"https://raw.githubusercontent.com/nerdunit/androidsideloader";
GitHubUrl = $"https://github.com/nerdunit/androidsideloader";
if (IsUpdateAvailable())
doUpdate();
}
//If the user wants to update
private static void doUpdate()
{
DialogResult dialogResult = FlexibleMessageBox.Show($"There is a new update you have version {LocalVersion}, do you want to update?\nCHANGELOG\n{changelog}", $"Version {currentVersion} is available", MessageBoxButtons.YesNo);
if (dialogResult != DialogResult.Yes)
return;
//Download new sideloader with version appended to file name so there is no chance of overwriting the current exe
try
{
var fileClient = new WebClient();
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
Logger.Log($"Downloading update from {RawGitHubUrl}/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();
// Parse versions into parts
string[] parts1 = version1.Split('.');
string[] parts2 = version2.Split('.');
Logger.Log($"Starting {AppName} v{currentVersion}.exe");
Process.Start($"{AppName} v{currentVersion}.exe");
//Delete current version
// 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);
}
}
// Ask the user if they want to update
public static async Task Update()
{
if (await IsUpdateAvailableAsync())
{
UpdateForm upForm = new UpdateForm();
_ = upForm.ShowDialog();
}
}
// If the user wants to update
public static void doUpdate()
{
try
{
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}");
}
}
}
}
}

186
UsernameForm.Designer.cs generated
View File

@@ -1,92 +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 System.Windows.Forms.Button();
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.BackColor = global::AndroidSideloader.Properties.Settings.Default.SubButtonColor;
this.button1.DataBindings.Add(new System.Windows.Forms.Binding("Font", global::AndroidSideloader.Properties.Settings.Default, "FontStyle", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.button1.DataBindings.Add(new System.Windows.Forms.Binding("ForeColor", global::AndroidSideloader.Properties.Settings.Default, "FontColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.button1.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "SubButtonColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.button1.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.button1.Font = global::AndroidSideloader.Properties.Settings.Default.FontStyle;
this.button1.ForeColor = global::AndroidSideloader.Properties.Settings.Default.FontColor;
this.button1.Location = new System.Drawing.Point(13, 51);
this.button1.Margin = new System.Windows.Forms.Padding(0);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(418, 34);
this.button1.TabIndex = 1;
this.button1.Text = "Create User.Json";
this.button1.UseVisualStyleBackColor = 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.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 System.Windows.Forms.Button 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

@@ -1,9 +1,9 @@
using System;
using System.Windows.Forms;
using System.IO;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AndroidSideloader
{
@@ -12,9 +12,12 @@ namespace AndroidSideloader
public UsernameForm()
{
InitializeComponent();
// Use same icon as the executable
this.Icon = System.Drawing.Icon.ExtractAssociatedIcon(Application.ExecutablePath);
}
string defaultText;
private string defaultText;
private void usernameForm_Load(object sender, EventArgs e)
{
@@ -26,7 +29,7 @@ namespace AndroidSideloader
{
if (textBox1.Text == defaultText || textBox1.Text.Length == 0)
{
MessageBox.Show("Please enter your username first");
_ = MessageBox.Show("Please enter your username first");
return;
}
@@ -34,12 +37,16 @@ namespace AndroidSideloader
{
createUserJson(textBox1.Text);
});
t1.IsBackground = true;
})
{
IsBackground = true
};
t1.Start();
while (t1.IsAlive)
{
await Task.Delay(100);
}
MainForm.notify("Done");
@@ -49,11 +56,11 @@ namespace AndroidSideloader
public static void createUserJson(string username)
{
ADB.RunAdbCommandToString($"shell settings put global username {username}");
foreach (var jsonFileName in userJsons)
_ = ADB.RunAdbCommandToString($"shell settings put global username \"{username}\"");
foreach (string jsonFileName in userJsons)
{
createUserJsonByName(username, jsonFileName);
ADB.RunAdbCommandToString("push \"" + Environment.CurrentDirectory + $"\\{jsonFileName}\" " + " /sdcard/");
_ = ADB.RunAdbCommandToString("push \"" + Environment.CurrentDirectory + $"\\{jsonFileName}\" " + " /sdcard/");
File.Delete(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

@@ -1,18 +1,13 @@
using System;
using System.Text;
using System.Diagnostics;
using JR.Utils.GUI.Forms;
using System.Net;
using System.Windows.Forms;
using System.Net.Http;
using System.IO;
using AndroidSideloader;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace AndroidSideloader.Utilities
{
class GeneralUtilities
internal class GeneralUtilities
{
public static long GetDirectorySize(string folderPath)
{
@@ -29,13 +24,15 @@ namespace AndroidSideloader.Utilities
public static void ExecuteCommand(string command)
{
var processInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
processInfo.RedirectStandardError = true;
processInfo.RedirectStandardOutput = true;
ProcessStartInfo processInfo = new ProcessStartInfo("cmd.exe", "/c " + command)
{
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true
};
var process = Process.Start(processInfo);
Process process = Process.Start(processInfo);
process.OutputDataReceived += (object sender, DataReceivedEventArgs e) =>
CommandOutput += e.Data;
@@ -52,7 +49,7 @@ namespace AndroidSideloader.Utilities
public static void Melt()
{
Process.Start(new ProcessStartInfo()
_ = Process.Start(new ProcessStartInfo()
{
Arguments = "/C choice /C Y /N /D Y /T 5 & Del \"" + Application.ExecutablePath + "\"",
WindowStyle = ProcessWindowStyle.Hidden,
@@ -61,7 +58,8 @@ namespace AndroidSideloader.Utilities
});
Environment.Exit(0);
}
static Random rand = new Random();
private static readonly Random rand = new Random();
public static string randomString(int length)
{
string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
@@ -70,7 +68,7 @@ namespace AndroidSideloader.Utilities
int randomInteger = rand.Next(0, valid.Length);
while (0 < length--)
{
res.Append(valid[randomInteger]);
_ = res.Append(valid[randomInteger]);
randomInteger = rand.Next(0, valid.Length);
}
return res.ToString();
@@ -78,7 +76,7 @@ namespace AndroidSideloader.Utilities
public static ProcessOutput startProcess(string process, string path, string command)
{
Logger.Log($"Ran process {process} with command {command} in path {path}");
_ = Logger.Log($"Ran process {process} with command {command} in path {path}");
Process cmd = new Process();
cmd.StartInfo.FileName = "cmd.exe";
cmd.StartInfo.RedirectStandardInput = true;
@@ -87,38 +85,17 @@ namespace AndroidSideloader.Utilities
cmd.StartInfo.WorkingDirectory = path;
cmd.StartInfo.CreateNoWindow = true;
cmd.StartInfo.UseShellExecute = false;
cmd.Start();
_ = cmd.Start();
cmd.StandardInput.WriteLine(command);
cmd.StandardInput.Flush();
cmd.StandardInput.Close();
cmd.WaitForExit();
string error = cmd.StandardError.ReadToEnd();
string output = cmd.StandardOutput.ReadToEnd();
Logger.Log($"Output: {output}");
Logger.Log($"Error: {error}");
return new ProcessOutput(output,error);
_ = Logger.Log($"Output: {output}");
_ = Logger.Log($"Error: {error}", LogLevel.ERROR);
return new ProcessOutput(output, error);
}
}
class Zip
{
public static void ExtractFile(string sourceArchive, string destination)
{
if (!File.Exists(Environment.CurrentDirectory + "\\7z.exe"))
{
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");
}
ProcessStartInfo pro = new ProcessStartInfo();
pro.WindowStyle = ProcessWindowStyle.Hidden;
pro.FileName = "7z.exe";
pro.Arguments = string.Format("x \"{0}\" -y -o\"{1}\"", sourceArchive, destination);
Process x = Process.Start(pro);
x.WaitForExit();
}
}
}

View File

@@ -1,18 +1,110 @@
using System.IO;
namespace AndroidSideloader
{
class Logger
{
public static string logfile = "debuglog.txt";
public static bool Log(string text, bool ret = true)
{
string newline = "\n";
if (text.Length > 40 && text.Contains("\n"))
newline += "\n\n";
try { File.AppendAllText(logfile, text + newline); } catch { }
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

@@ -1,18 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Linq;
namespace AndroidSideloader.Utilities
{
class StringUtilities
internal class StringUtilities
{
public static string RemoveEverythingAfterFirst(string s, string removeMe)
{
int index = s.IndexOf(removeMe);
if (index > 0)
{
s = s.Substring(0, index);
}
return s;
}
@@ -20,7 +19,10 @@ namespace AndroidSideloader.Utilities
{
int index = s.LastIndexOf(removeMe);
if (index > 0)
{
s = s.Substring(0, index);
}
return s;
}
@@ -28,7 +30,10 @@ namespace AndroidSideloader.Utilities
{
int index = s.IndexOf(removeMe);
if (index > 0)
{
s = s.Substring(index);
}
return s;
}
@@ -50,8 +55,19 @@ namespace AndroidSideloader.Utilities
{
int index = s.LastIndexOf(removeMe);
if (index > 0)
{
s = s.Substring(index);
}
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

@@ -0,0 +1,16 @@
namespace AndroidSideloader.Utilities
{
internal class UpdateGameData
{
public UpdateGameData(string gameName, string packageName, ulong installedVersionInt)
{
GameName = gameName;
Packagename = packageName;
InstalledVersionInt = installedVersionInt;
}
public string GameName { get; set; }
public string Packagename { get; set; }
public ulong InstalledVersionInt { get; set; }
}
}

25
Utilities/UploadGame.cs Normal file
View File

@@ -0,0 +1,25 @@
namespace AndroidSideloader.Utilities
{
internal class UploadGame
{
public UploadGame(string Uploadcommand, string Pckgcommand, string Uploadgamename, ulong Uploadversion, bool isUpdate)
{
this.Pckgcommand = Pckgcommand;
this.Uploadgamename = Uploadgamename;
this.Uploadversion = Uploadversion;
this.isUpdate = isUpdate;
}
public UploadGame()
{
}
public bool isUpdate { get; set; }
public string Pckgcommand { get; set; }
public string Uploadgamename { get; set; }
public ulong Uploadversion { get; set; }
}
}

220
Utilities/Zip.cs Normal file
View File

@@ -0,0 +1,220 @@
using JR.Utils.GUI.Forms;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
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}\" -bsp1";
DoExtract(args);
}
public static void ExtractFile(string sourceArchive, string destination, string 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(Path.Combine(Environment.CurrentDirectory, "7z.exe")) || !File.Exists(Path.Combine(Environment.CurrentDirectory, "7z.dll")))
{
_ = Logger.Log("Begin download 7-zip");
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,
FileName = "7z.exe",
Arguments = args,
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardError = true,
RedirectStandardOutput = true
};
_ = Logger.Log($"Extract: 7z {string.Join(" ", args.Split(' ').Where(a => !a.StartsWith("-p")))}");
// Throttle percent reports
float lastReportedPercent = -1;
// 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)
{
// 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);
}
}
}
}
}

BIN
adb.7z

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,781 +1,12 @@
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
RSL 2.1.1
+ Added optional Wake on Wifi setting so Wireless ADB will still connect as long as device is not powered off or dead.
+ RSL will now automatically choose any Oculus device over devices made by other manufacturers if more than one ADB device is present.
= Fixed Download+Install QUSettings application if QUSettings are set.
= Fixed Pull APK from device.
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmDjcnQACgkQx/Rql1Vo
czchqg//WStjc1ANHJK1P06ormJ4ArZFA70UkL0Xd7QUcwJhEiR2/uIIHpOCeCdZ
ymf6fvsqcyMkAMMq2YA1sNd5fPTFgcaqWZhkir+TywPBfrpcMJYP2lcJGXuhcmjm
GJo4uymOjci9SBrjb7o8Z5q3cxInRPLfgksgRVN+b3Y965yqETm0OcSXBrCuDyXV
SqgAJzAFNqElE91LRtz3BqOJ8eNXmEbmC9iSFnllwC9fSYwDefNkIRAVfmD5inqs
acSW/6URwF4xnF578mnHfdhKlhROqt7XJ/dqIrrh1o0/kV7VbOR4J7rq8vD1nHrZ
uOAZ+zBKnk37Px61F4XWtUmX0MDHoTuJChAZqKzdZkIUy9Dq/l5asMFufGaGLrHa
OGjuXNQOTGzSKzp6SNsrU+BkBAJDW4NBWrgacC9PqgE+uZSgedreDZb86UWtZ6ZA
MR7MFiqaKBGx0GMfbO/JWu/REa9GkdCghwbSKrnPCIK2wsY7zECFflmIaf/BQWzw
p6hicDZc5053Cu+ZHPy8AiKbv+3FB5AWiujYbl/QkBifoEL03tHepCREAHMD6MBn
OqgnRU2gwtqke2FSnThyx1qgnnXLLi2M4we171DreBziF9DE23W/hcE+RU3WtucR
QflQ1p4c5o1q8B2QDkDSjcsCfCXRnfQ52YRv0N7cdKZ9M56GAF0=
=KqGL
-----END PGP SIGNATURE-----
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
Update RSL2.1HF5
Fixed ADB path for usernames containing spaces. ADB now located at c:\RSL\VersionNumber\ADB
- -HarryEffingPotter-
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmDdNvsACgkQx/Rql1Vo
czdIWQ/+OvvchZOuwMZJvd6d/N4WmBUPKm3TzyiF5JM/STLEbLIHB1p6LQg+6J3K
3d9x3mQmu/gMHMumsfmuOuHHI7OOs9CLOVY2UFm7CqTnDf7iCxl8+6/iQw48yfj9
63qwUKZdLueRX9IqtcV2MwZJP7eKdZmtv6ALyRbjWmo2fgbm/Fs1U+TfGnlY7qLg
wIascv5DuKRyHvRzlgdn6OqmewWTxHDOTwCTjodROIK9guCELNYhEWv/TtgMldv9
gz1Lr8KZOcxNltpvstJgdbeFy61LK67su0rKZHsGMu4x0hDe4YOyjyMDtXrVJ0VH
SJBqDgSKD3HE/MNYkCWusqs7np2XKXC8hP6nm2QO9zM44d+UWDfrJbwvvw6QUoez
Iqk/PskC3sF7kWAv4O+LRmLDlrHD2RG5TkkE4ohiIi4D4VaNEx4QyVf6INN+A9xd
IxcvXC9QkFwffUD93cThEdLNRnrnzZSsUFMRh3XrvPbq2wfvWjqZX1EGfD7ug0Mf
r9uPX4RdILvsXeybefiiwLiw3UNMJtUlaKDvqIit5S78BIP6DY+dGnMMRIQJa1vd
XBPnIHyifzvl1nyYd6TWCFRuOzZLYirv4Ug6xS1/KzyURXIz2P0EEhDURJAR/q+M
ZmqSO2eQo64B7Dfa9oRLKkZRfhWFgtYA9QKm9ei7tOPbnJ+2718=
=o5Nr
-----END PGP SIGNATURE-----
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
RSL 2.1HF4
= Moved ADB files to AppData\Roaming\RSL to fix compatibility issues across the board.
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmDa9rQACgkQx/Rql1Vo
czeiYQ//X6e3DD51Kgta/omu0fVtPM4lxt+P/H5ij1xbNPmoaL7w+QvNeFqVLPs0
T8btbEkDT7pisOHQtHFKENs4G0Sck0RkI0jVNu1lYIT67tmTYyFGP4jOxopktmNE
uydmI8g+8UkuTPYIKpkYJUOrWT1u4IV0SoBsdERYU7qRZmvCqWgwqdcs4R6wAdm0
FNqWRbUo/LhFw1rQicny4vL7w6rf7PvAksWifqbl1KfBFAchf4cZ3+YKeyO4V2Ym
7LcNgVGaSuPCr5dKmVr31B0kU0XrV8KPpKwgoIOym8eH8PNuaj3elwbndqYZ95fB
xNFluF9z9qejiIkYM9UDSoeNEfeJaUwIfgb1WI4w+BdlTWaYZfsG+DF+VOBvSU/h
YP1ucGrqCu2lLxcIS5ffNd7dTjS22D2x2rErqXGl+ETgqgVHK9NU9Bd02VVyP171
Ryq/JRd9tvFknAG6YViVADJDzKdnuwFYeo/T5wgt8P7GtzVZWmZbur6crXK454IH
TkyLn9XFbJneGPxjLNm4VwCR0XWFmhAsE+CfRz52hL18RAxyeohWq7RBTt+d49t9
eOhTTzOHoWgroOVTV793r+FLdc4nYDhLXRm6I65qhvQhEPCqZDIA1xuvzPbSectb
VNNRuPmhxDgtevFFsU/sX8xOv8pKi6naW8N6jobXPavwegABCEg=
=61BS
-----END PGP SIGNATURE-----
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
RSL 2.1HF3
= Fixed QUSettings not working issue.(unused fields in QUSettings MUST have 0's in them or it will not work, reset/clear all fields button now reflect this.)
= Fixed Uninstall auto-save-backup
= Fixed Install.txt automation messages.
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmC2YLYACgkQx/Rql1Vo
czfzXxAAlgKvec94/48QX27BYxVLExEO1AukM7c3D4ONDq4xz6R7jZdoGTbSpfPg
IVbJX8ZeMVQS5yPQr3YOEYdfunU5olu2+XMx5be0uuq0VpZsHdh4Z99GDZId+YFX
W9AJ86p0l9Z6m6npJnc64Fz+3akq8bbZVNHjIpTl99ZSpNMuwYtGpKzNjDXZ9h/Q
v4xQKlCJy7pZO9XY6wEf1z0s+2dMCAGTnfVP6nSXwhx+jKHudRcIRNy7vfgke7d6
tVGRQxe0UP1fth9D80R6y+PGw318kNxqPH5b9bN5+PJrHwaHdQ7vXwrVdgVU4g12
53bGcRdNH1+N0mEXCz0VQyYmd73HVYtNYYEtocUY+I/W/sxBqbPcvVJ+XZinFfdg
BaX5I2M5Ub56wUtyzpP3OoFkIMAN6/Z9S1k/Mq2qTPH/aGtprFzWgmg32kWHi9/n
64uIf4TQlr4o6t0MrMR1pk/F38Rnow66sn5v4eOQ01c9bFOANTMxL2fQiZluSK98
36eYYdxPkSwPiRDVZTrwOOPbJfiHeKREHFcBT2C7OehJm5PkueSzW+CVnjSfH92q
yjp1+zGEaYn5pos+TeaN22uG0L3U5wbfUuCHSHWstIMEwx0aroKhRyou1MtZn40W
k5PjmtDRjt36eLH3MzSxnhwydFrC2V2rl6UbtxjYjdlqEEdFlX4=
=224y
-----END PGP SIGNATURE-----
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
!!!IMPORTANT!!!
THIS VERSION IS A WORK IN PROGRESS DUE TO LACK OF TESTERS!!!
v2.1
-Changes-
+ Backups are now stored in Documents\Rookie Backups\(BackupDate) folder.
+ 404 error fixed.
+ If installing an app that has a different patching method or signature than the previous version did user will now recieve a prompt for the option to upgrade which will backup savedata then uninstall and reinstall the app. NOTE: Not all apps allow backup of savedata.
+ Refresh updates list now loads much faster and refreshes the main update list.
+ Added Wireless ADB functionality.
+ Added Enable Wireless ADB and Remove Wireless ADB buttons to main menu.
+ Added ">" symbols on collapsible and expandable menu items to let user know they can be expanded/collapsed.
+ "Hand/Finger pointer" symbol added above clickable main menu buttons to let user know they're clickable.
+ Added logic to allow entire list to load and compare installed versions with Rclone versions before populating and added informative text of this step to the bottom/title bar.
+ Added QU Settings for their newest patch method. Can be found under Quest Options, this will allow you to change settings for any games that include "-QU" in the version/filename. Click Enable QU Settings box to reveal the options.
DETAILS: Clicking enable again will remove the settings file, if you re-enable you MUST click apply again for it to work. QU Settings are applied automatically when any games are Downloaded/Installed via Rookie as long as the custom settings been enabled and applied in the Quest Options menu.Once applied they can also be added to any already installed titles by selecting the game you wish to add them to in the menu at the top of RSL in the main window then(the same list used to uninstall games) then clicking on Install QU Setting. To remove a setting for a game select it from the same menu and click Remove QU Setting. If you click Delete Custom Settings in Quest Options it will not delete settings for any games installed while it was enabled, you must do this with the previously mentioned Remove QU Setting option. Clear Settings just clears the values entered in the fields. Settings entered into those fields will persist between instances of RSL.
+ Public key for HarryEffingPotter(Rookie contributor/VRPE dev) added to Rookie directory.
= Fixed Install.txt automation to make Manual installs automatic.
- -HarryEffingPotter-
Released: May 29th 2021
Public key for Rookie and HarryEffingPotter included in Rookie directory along with Checksum(can be run with QuickSFV.exe)
THIS SOFTWARE IS FREE TO USE, IF YOU PAID FOR IT YOU WERE RIPPED OFF. We can be found on Telegram @ t.me/VRPirates.
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEEKjgtlwMyrpVu4TGfx/Rql1VoczcFAmCyMHUACgkQx/Rql1Vo
czeJ+Q//ZBEopVVZVc/IcmYFS05hUy+xHFd8QWajSCAFsQVyCctEPEzsluOSmwM1
OQlO0awzMM4s9IWwPIk0/caML7ZgkwDlIxR7KgasgI+BirrFCz1VImILoF1CiSHV
q5Xj6GQ3NPE0xK1QDe4fCVdXX+VScqQOAnP/ohkfHfuP0ce6KKxZpH/UeafDjh+V
Birzv6a+1dMnewJHnAwn6xg5Q8jvPT4Iz++t4XBS3k/dg5bMpLg4T6dwPnuJ3zjh
muThmNzBSw1ICegO/SaF8jhgGrhwVsF7aM9pVv2fvYaFpDbuxITQOcV+jcvO6Orf
R/LTf26pdguQnuc/KLWMCthMtNr1O7q3tnVF8DGVio5yzL0lKfVkDqjb3Qp+GTYB
f/lZyrzKZyt1OaVInmxtLsj5wavQtcYRUeJGU9ABd6yb9ecmgJlP6rwPR1RMiZWS
B/nEC8td1JeTK7qhop/FA1qoQ9Z+2cSVCuILKcKj8B1ushxQzDdIDeqrwPxz98Ta
5voJKtq8wWtV8gkldVCvZRvlIi3qaIFBLutnY5AqTxmDHgaTdF1l3adauK32alyL
xUsQits39mBui6bMBw1fCfw1zu3uqh7gylZ/j0t+XLVDJtIC+UcRTjjIpRLeHjM8
O8gLNx7jFtRtm/vO1jAlS7Zu3LHIxq6I9kwOZsbwNQe7yqfUwPw=
=iJ4Y
-----END PGP SIGNATURE-----
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
!!!IMPORTANT!!!
THIS VERSION IS WORK IN PROGRESS DUE TO LACK OF TESTERS!
Due to how the rclone config works now, if you used Rookie's Sideloader with a custom config
Make sure to redownload the entire rclone folder from the config provider!
I forgot, adb usb isn't called at start so the last changelog was wrong
Also this was the first release to use GIT and sideloader had broken updates so thats why the huge ammount of commits in such a short time sorry
v2.0-WIP-SU1
Changes
= Enabled mount button
= Pushed UI Changes made by harry
Date 15/04/2021
VT: https://bit.ly/3uPda5K
MD5 Checksum: 4259AD179E8C56478828FFFC38586BE0
SHA-1 Checksum: 44C1754D0CD0B1677A73BE5A10B05E02BC85E6B7
SHA-256 Checksum: 8D6CCFB6DDA656BB3FEB1EBAB4F3BA9D9A0D431398A80F23F8C1E097DEB8B827
SHA-512 Checksum: B6DF140C00E3761698C604BCD1A8835C6B2EB5BE18477C3BB69900E2353EE42C196A475E81A95D495C346C35CD2100148FA5E5A0C03D3FEBCB4F469E7E8C6EBF
You can always get my public key from miligra.me/pgp
You can verify the signed message using kleopatra for windows or dark.fail/pgp
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEEG9eYw4mQVDjvGf4lvrtJUNxETxMFAmB3+4UACgkQvrtJUNxE
TxP75BAAknKjeZmhvXGn8ScYyRIdl/hOk0fjJ8tk4UJBFByrMqDJPyGsQW5z+bRl
RinNEmV0LWfT1I4wFoX755NXBoQfxd2ZgRo9EQ5w6AxeVoml1vL7p38NbmQZ2dEb
nXJFz0Ds+8jW+LDcCBC05rQjOoz3yiFm+lXQpQfuX0/KppJn7aFmijSZ11dFytWI
Ih8aOQVmrt7PUwIFT0ZZbHuQsFjN5cNWUK3S+s7qnjo6RLRiwmbSGO6Bt9vnAgtR
IlaRa9im4wjWnCGrXw7cLv44SkaHuolJ7fGWN5kgO3ulOsqz5/scsRG57RQjCxlQ
9wbveyROLCAbuJ4GVcgKPTimVIuZJg4rE1exV1ey3oYopjrfXOkqbeUILQFOGqIf
oQ7qEIMx8hOcT5Yz81eddSKZXOOBUgYuI91XX+ROwb4DXkAWZbqSWEZcUuKUz95P
U6mEqdtCEE3OVEzA1w/eW+o3l3m6nPc60SFV2jpNQPV/QOZYme4njOOmpvqrCqQN
4HQse91FJdkIllIL6DgfcavvpKqpstuBH3iY8DWUu4rb6HX+OimscFKmJCXNKYO3
peCKU/Rq4zAZXCxtxJRlitgPaBx/RUdQ2ZtWR+86aUlNSvE+9X0FCPY0iiqQ7/iy
2V28poXEr5Nn5zn3EfiTjBooJ9LM9Kmc3tV0uxUlbAT2MdxhNak=
=+19T
-----END PGP SIGNATURE-----
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
!!!IMPORTANT!!!
THIS VERSION IS WORK IN PROGRESS DUE TO LACK OF TESTERS!
Due to how the rclone config works now, if you used Rookie's Sideloader with a custom config
Make sure to redownload the entire rclone folder from the config provider!
v2.0-WIP
Changes
+ Added update checker for games
+ Added dependency check for spoofer
+ Games now can have images
+ Replaced the gamescombo box with a listview
+ Listview marks already installed games with green and outdated ones with yellow
+ Sideloader should be faster now
+ Reduced sideloader size
+ Installing games and copying obbs tries to wake the device first
+ Sideloader calls adb usb on start now
+ ADB Output now is a proper object containing strings for stdout and stderr, handling will be easier now
+ Changed spoof on rclone install to resign, allowing users to easily update games from now on!
//You can still use the spoof button manually
= Fixed UI not starting for some people
= Refreshing mirrors now deosn't change the selected mirror
= Cleaned & reworked code in sideloader and spoofer
= Fixed Spoofer.Init() on Non-English Windows
= Fixed crash detection
= Reworked rclone config handling
= Fixed game release not existing resulted in a crash
= Sideloader false positive detections reduced
= Small ui changes, hopefully for the better
Date 13/04/2021
VT: https://bit.ly/3a3LvGg
MD5 Checksum: 597D4AC957F69D5B1860D14D15DD3904
SHA-1 Checksum: EC78E4416D5222E507F46499B62A1D16880F84DF
SHA-256 Checksum: B68EE3B95CA531CA4678FEAAFD13F75A69D4495879536A9D04094ECFFEEC86EA
SHA-512 Checksum: D98F319986179B362D0F4C4E092FE6AF4ED8D1FD5445667F68280B43BF49C9DB7AC3B0F49690644CB46A7A391EDF36E82A8BCF7C52770498E6CACEA52D44EA4D
You can always get my public key from rookie.wtf/pgp.txt
You can verify the signed message using kleopatra for windows or dark.fail/pgp
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEEG9eYw4mQVDjvGf4lvrtJUNxETxMFAmB1rNsACgkQvrtJUNxE
TxND0BAAgq4HKuNG/wxcYfari00+Vz56BdjWawZ5OvgcB/KuZLEWFXN5IpDktZrl
S7M8xGsAffDJYa0qamnpB0aE6uBgp3RCa4oyZUUlYf5sRiVwz9lF2Ip7XWZlkkpT
3zDYC4FSnPtlMP/97/OfMfdUSO97TpI+mEM2qtknl4dQZ8DdTugRWKWq0Kb+0ZI7
mD4FpxZSyko1lzBKCvInr/5LQWPrNknRIOwzxH3T+BPr9CjhAoPduvJUT7QzZZAc
+wYMUQYIb7M6zMy7iiiVtNlyIfwW8EnJdyE4p4NejIAQTOcFyvB7XTK/z2Ut7T1c
/DIINF+Sat8wZ1mZ+c2I9+99StlwTSiYdWVZh2jW5F5wb8pWrgDrCmoG50X4gvWJ
PGcWXG14x6TTAU0b55oPUEydWXMyxg9x/8IQA4IeDZGo9HdJDFfkW4hynTO675+o
1AKr9dfdnAhgqqfw59eWtxh2epU67JAOIek7wikfOjNnVRykim4wZmHB/FC+hIuQ
ppzs2QBxV2LknV3UeOklUMvR6n8Q/N0JZNxUde6o9HCOm2EbY27Iiho9wZI0+7Bk
IL+cF4SdRdsBQHX6PLPk+1wX667A+QnwHOfkC9SXyUK+MI4CMvqZlzIitqT4QiFz
r3JeCjJjYW4jwO7gKD8OM9CUMy+6+4C2ImS5CZ3cBB3KLXR59n4=
=17aK
-----END PGP SIGNATURE-----
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
v1.17SU1
Changes
+ Added some instructions & checks for the spoofer
+ Added crash detection
= Fixed the game queue broken since 1.17
Date 11/19/2020
VT: https://bit.ly/3nDUMcx
MD5 Checksum: 6DA8ADBE4447A809C598379E73DC7F77
SHA-1 Checksum: 924220FE8877174CCB9732F1F99B759DD6749102
SHA-256 Checksum: 0405C389A7D84CAC0D9081BDE10D010300822B10EBC2FF94C501EC740C8CFA40
SHA-512 Checksum: E8C3A15257C30278CEBC46507B1F6049FFB5AC0D6E4FBB73E1776537F80CBEA95C8C81A3F3259F8AEEDE276EBF6D50F6945622403E9D80B5E9E0F47B050BBEA9
You can always get my public key from rookie.wtf/pgp.txt
You can verify the signed message using kleopatra for windows or dark.fail/pgp
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEEG9eYw4mQVDjvGf4lvrtJUNxETxMFAl+2aycACgkQvrtJUNxE
TxOlEw/6ArmiRFAjUNC2YaElJ7fFsXVzCaeJ680rs0CK4oX9P7YjZ46y2/VGDtX/
IAGvWCqHOIw2t0sk01/JwoyKZFERLDn2MRCj9FTPsrPA8yqveiIx/fof90QaN2Q9
4aV6ht+SSirQFWTXENboEbQSWxFHW/z5/Dud8gDBuCTuFuueBk+EAph5yI3dcjCK
naDE1gX+CE21ef84F3nGMspu2q/uCaIM2mvEWR2hBvWLsQpH7m5uQegXGYywe6YC
/8wEY2mEe0nayp/3NglDDarsPN8wx1U5i/kqS8wrzZZT49pRqKZ0wykWGfAPQsaO
txmfiwH+5cu3J/6PpAMVuNjhtL0dnR5MapsXT3CoRvSiRDl2urdYJUQj1CsYOEWP
Q4St6ISoUoANhVFRcM9mbq7clxdJnfBqdr1J0YrK804qWp3krDYv5hrpE3Puu/nA
TORsqWiHPgQQHANsNYsY4WkvLiYHWyB62Cm2NATdtaoVWzsrBgFk9xlp1oIQcMXo
RlRQZQ7tQ0VFFxaP7ARRaf9ay78geC3W/cr63JTn8Hz7Ul1h+ZRa7tb1KYvpCTnp
xC7vh3IyBwc4M7mt4CxsjdFnkO3iaDybFvlufua/5vMlt70HLdutHa7vMYSZ+GFr
HL+SrazP9o8ut2L85uBqCjWHPfgVaBaaQgk9FgZe0WQoS06fdeU=
=DIzT
-----END PGP SIGNATURE-----
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
v1.17
Please manually update to this version, as it fixes automatic updates that were broken since 1.15
Changes
+ Better game installation => no need for manual installation of games anymore
+ Added theme changer back and updated it
= Fixed updates
= Updated quest options
Date 11/14/2020
VT: https://bit.ly/3lwztcn
MD5 Checksum: CC7BF16A3D687C21208E9E986D720CAE
SHA-1 Checksum: CE0C71B4097068E84FA6FC65F4AFE69CBC79AA29
SHA-256 Checksum: D9393C28376DDE1D0582BCF939D1414879EDE6772CE82F83F54C0EA0F239B2F0
SHA-512 Checksum: 326C7A0177E9B8BCFA10CB3B8FAEA025F9C45C599DE5D46CA06DFEE427351F3AE88371C5A0827DEADF7D3F459052C421A145AA5581FE3C4BD2EFAD33BCFFC37B
You can always get my public key from rookie.wtf/pgp.txt
You can verify the signed message using kleopatra for windows or dark.fail/pgp
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEEG9eYw4mQVDjvGf4lvrtJUNxETxMFAl+wFCUACgkQvrtJUNxE
TxO0ThAAw6po24zaYQFa+aGrgQNuWYWRxirCkLekEgmy8XVL6Q0rgMeLeq2MrCBP
wi2eBMFlLNOXy1TXOhled89NRBiBeST2Hl4TVv0Qdavd9cSeuGD/rh/PsyxnxY2Y
RWCPeXiYOf0mN1zVylLas+2vk9o/lrNieJir1nMEvyVbUJs8lTDcBdo2vVFwHK9N
nStFPxjqRb8R6smTIsltIh4+juQQMX0GyCcEMUkb5aO6BP34Mpsoaf+9bxV/elJm
UXnFSh5pjzHhkYGKVePtoHev8aFOzLeqIrfyWRJl+7gBMgYIautaxAIE+Zsy3Sai
wxUk9HJeAYwEPhYcIJKGAy+fmeX/TePQjUqLu1Pp+sJ8orstSiMjwXr84UtvyUWZ
+gSTgsQJ1QNUlhp+eJ0D6CchkAwliUXgnpIzwyn1Jj54X3ecHNjMbc5SettlXcBG
qgy5bUx8+v4H4gHmqTyZQNv1bsvIiQlcwPPUtpSzSXrzJPfZjrqPZzUWB8/Vltqc
JIR8FxEqi7+mW6AqrhUjE74N/fFibJTEuv6RALwZIAfMYEuSeFlZlDJYZ9v3H4/X
2jd+wEksbq6uYUl2l3X/7TpMWX29paj18cLD8oTSLK/RvzLXe0hhUcvuHrMxi+14
Q+V7uZYGge4Z5PANGPhd2tSkvw4CyA25OQ868QXjqciSLTfK2SU=
=Yu78
-----END PGP SIGNATURE-----
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
v1.16
Changes
+ Speed Limiter for rclone
= Random packagenames more random now
= Bugfixes
Date 11/12/2020
VT: https://bit.ly/2K4Y5Lv
MD5 Checksum: 29978E435BF02865CE61EF6279FB42ED
SHA-1 Checksum: 2E47CFDE7549D088A102F021657E9CD6859EAC47
SHA-256 Checksum: 50057AC0DA315C492C9C45B5801AE99E65B812F5BC18E23622CCAF5FBC48760A
SHA-512 Checksum: E485ADB6088C1F7DC34B65B57A9077E876D3D576D48CCED1FCA691A113CA5F6D0F8A0D31C6C848B6719FF18B7D4C5F829B9DAA8789EA6087F8B1D41005F87A48
You can always get my public key from rookie.wtf/pgp.txt
You can verify the signed message using kleopatra for windows or dark.fail/pgp
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEEG9eYw4mQVDjvGf4lvrtJUNxETxMFAl+tHY0ACgkQvrtJUNxE
TxOpjRAAmjaIfvIP+HvfTMGKOEGoVRDIBqEyi8tzq8hn5Z3OadBVLkWT23WblQBw
Ur6BN1fuhH5B8ToGoqX/C1lrgXsvHNDB8frGUWnO1G1KJAQEGwKPRdKyK7YPlrYm
kK74CYuM8w7qjkhX3f50ECZ4Kj1WsE92mIHjfB1xLSGzN0o7Tr1EvFFDnuXrJ0I0
vEbeto0Qo19ORcoP7KFPgGuYmMYJQ2MuwYmWDWAdm4iL1Y9vEKZ4TsHG+KmYPdo8
19YoZj8PsgYxCBy9B+UZfkJnfF7gYKmiAsgnQviDc4BvZccfAVjmWcKkzJueDkvr
GgFmbaFE48svC2NU8Qd2gq6gIX0JrnKACZqopqgorl1817Z7Rsdh2iVwTMOvxbe7
a4kAtDN90KwuRlnwoxeCBdESDSrANbiNi1yaDZc5hr/xOxUsa1JKhk7/8mcdHj78
QoCRoji60Cq8XYeRNDXI32TkZd7ql9c64AjGH1uSySIPXfehOa+m5S53Jo2dRh5p
iI1LGHjTk7EDekIVRh/cPC+hohfUAKYbUcXp15cx0HndW5+galYIH/RAjF0L+Q1U
THz36HpKvLSsgkVkeVjPNJn9Ow6xgyByLTkHrS9D7Kj78FrKiAKr08+aI3+WCpdq
1G9HcH5VzDeZ7/jfVQPhsAn3dPxlTt6ReYzbeFkrO1UHFcTdl58=
=KyH4
-----END PGP SIGNATURE-----
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
v1.15HF1
Changes
= Bugfixes
Date 11/06/2020
VT: https://bit.ly/2GDYWRU
MD5 Checksum: F0D51FEC94F8D266390E61E89E6A2EE0
SHA-1 Checksum: 4FB43CFA6021B8360A23858169FAF15468B68EDF
SHA-256 Checksum: CBE414D9A283871099057EE5D8E56AF408996AE9C996C31F92C4DAE16D48B024
SHA-512 Checksum: F16A6B29ECABBB16D25B7F7FC52CB15AE4930B2B17ABAC791BF1F331270F40B047094FDEAF1EF834E72E26AA3A6762AF8BE13EBCB735EB1C1009F884E338D9DE
You can always get my public key from rookie.wtf/pgp.txt
You can verify the signed message using kleopatra for windows or dark.fail/pgp
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEEG9eYw4mQVDjvGf4lvrtJUNxETxMFAl+lfEkACgkQvrtJUNxE
TxMDDxAAg+90vrCn0PllPEezzX3AJpy7NFWhFjUlDOLzNFMPa/QdAfF6H5vhEGay
bhX2BDHKSIV86bLkyGPkGHuf3etpXvZsNAmPuDoZpZ4owd1O09DOvAHIJx8F1Eia
Jc9Zovz6lKLAeIbK9ZdChKwTG3hYLIkx3rMgtvnJkZfzUKbhdU9MP0rVEzDPkSfe
ay2CGA9xLBUhPcItUTxhANzrDpRXMGHqbi+Wrscp+KmvnjHzN3Haq2A9rsasXM5Z
RSG8ccLZiI496tJQquTvPXYcnWQQudzcf3yiicztein0xcISEat0FFIVao7TtnS4
cgitvGNtfXXauAxyzjZ7PdgrVuoAE/v4m25pzaTRy7TOfW1uTz2/6lr6tZa38+p9
Bs36KcoK64FcPWjfR7zvvmCUWT/aHlRC9FwMvBHB81WRRk6xGaKl8G2QxfKY/ATl
DNlgYXfXLzipam74XfPHiyK4Sc55WLDQxZfZfD6tqTo4u8qoDESvcEz+pbGe4Wp8
awY4FVeSP1jVQzHgWf+oMTiHm5Gb3qMLUhZ/AHidUWFLhzXmAA4YXxIHKidRjCYT
42Kzs1sjT2A5ll2V7jz4pqoRlsvRAxNZ5KgAZ2R01zo7gS1nGqjyoGq3tLWHbfJk
/kD4QlOwcv06Jvp8aHcPxpccB7KHZGoRT2PB6IQE9Gt72pmAU9c=
=f7yh
-----END PGP SIGNATURE-----
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
v1.15
Changes
+ Added a spoofer
+ Added option to auto spoof games with random package name
+ Added new username support for quest 2
= Updated drag and drop sideloading
= Fixed storage label
- Removed troubleshoot form and theme form
Date 11/04/2020
VT: https://bit.ly/3kYCQsb
MD5 Checksum: 39BAB4CEE3D0E387E770BA35F4488C2F
SHA-1 Checksum: C6F311411408BCF5A55034E7A926EA385819F348
SHA-256 Checksum: 64640374EF39DD9CFC4A613A49888E3194994AA934C5E952174A2FCF533E2D19
SHA-512 Checksum: 22769280B732821539100BFA77C67141F271DB401DB3AFB28EF285E352659F9F45A90C8ABE1C0B829770C5AE580CA76A9208505888443C22EE82AAAE7035C6F7
You can always get my public key from rookie.wtf/pgp.txt
You can verify the signed message using kleopatra for windows or dark.fail/pgp
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEEG9eYw4mQVDjvGf4lvrtJUNxETxMFAl+jJScACgkQvrtJUNxE
TxMTag//f570tZ4zxL1x1rdajdoYzPD6oaGIabRVPrqVogsFPuVF1Q3AOzr+nzQN
Tiq7yRPmByu/dtBGby2mqL1acQ/9TLYDtmnOAxQJqj4h0KhYlnzdCfqJJbTghtZX
cy44ZqgPW47P608+zC/NfePNt7Fq1rFKN3Bpo/ISoyi5/IVGqGq1Ix/D0SlhiM5X
5VkiY+w5S0rXuAyQ4g6/03AiI9fdNVkREhUh/tYvb7IkxaN9IDlltHByeuvekMjZ
KU+mwZ9habfthB31hp+bAXz82tbIzB4LJ1P8MNadIT8ZXG19zHEY/KlaDp2NjniV
h+V9AnxPHlcZk63yVPOywxpZcv81LflMgs9XItX+zKH7ibniBEu8jxc/2uAjrDF/
JuvnOUKTuH7S2iIzHhKLYZYHiOu8sIuwGC96Ep172PGCAkVVR6CtfNPMmVZRllsY
JG3Pxdrw526SAy3Vqixbbm79Fs1o77resqGM2zl6L7Sr6sH3VnDxabVXxALgo1pH
+LSu5rCLIIsCP1a4MAyy6UgO/+ZLrC1QazR4yY7SRQHAIM48XG4d6CznFCOjczhW
VvYL6gZD7X4xlNBqNMtih8JDHIQ1y7n1f075oCxfWwt02kXhDL/cn8+UZ17IS6EZ
zr/SLm1464/htCZgsUZgQrov6CpHX/xNqAubowkAVOdDREbwh2w=
=QKlt
-----END PGP SIGNATURE-----
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
v1.14SU1
Changes
+ Quota errors now will try to redownload the game from another mirror
= Only showing mirror remotes for apps
// Will fix device storage when I get my quest 2 and can debug adb
Date 10/18/2020
VT: https://bit.ly/3kcG62A
MD5 Checksum: 2A30E5F3DB785DC8104A2AA55A83CF53
SHA-1 Checksum: 8376E5971FC4F60995A1125DD9344E52135DB4F7
SHA-256 Checksum: FD5C74067BA00561A540DE5A4F49FA63B99C9520435A51487F997AEC569E9659
SHA-512 Checksum: 6188D6306CA2A3F25DE08E19D1F20046774F7AA30BD745ED24FC7E53126DF3950211B599E3D774F540A6C1DEC177A895FAE70FB564B6AE96F5F10ABDCB9021C1
You can always get my public key from rookie.wtf/pgp.txt
You can verify the signed message using kleopatra for windows or dark.fail/pgp
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEEG9eYw4mQVDjvGf4lvrtJUNxETxMFAl+MCbMACgkQvrtJUNxE
TxNMVxAAlIkoTuGTDwXRBFNanulPiYmus9UazHoVFxtG2BELk1/JLPm7dwT29FiZ
oacUhhxLAEfDm169FoPNFCH/6q74xqjACkm8FjwCoas53DekguIvrhJKA3SiBUtZ
m7+hrT3WWGxXoE3+avSFmdBWWvKVA9pNR/DHprHSoinDUvgSfwBHrWD8PtuSIlwU
/jNpzyDnmBfdT4LROq93hcfFXM+POLWs5f3rXFdT+zTZexxlRiG5h7yMmsYdwdov
4jMUSzrBpRXbmmHwDi4KLzsdOJ/5okJgRdPTZm/L1rAoJT0F7QVi2shvkoN7SIH/
Fg+k0ZrNOnlpdAJwLmJD4PkQmxv5zpGSh8w7nbQJ41HgBtTQTh3OCpRRamPzAmfo
A2n/NsYpR8J8u7kGzT1MUnUn2RL+igutSrnQl8R6yR8jFjaTt+F90pBXZ1nnY6mS
8YE9mBBK3365cYVqQzOW1TUSCSzQqwTbfKDHkKrzHQWrpQ8VNMLcJBLZwU8di+4p
u8lt1uT5MRZ+9I80YBXhwzBKkiNYOt5vHpGgt0Ib4WDS7rt/7UnzEAfl8P3VEEfX
6w1HctI2zAVWYutBMv6HL3v23cO+UT8H6Wdgxq36mlMSNiqjLIntfIRrySUOy7JV
D6LE34gzpDGsBIzrpW/Hlz4wGTi6nnY0Ke6DkA1H0UOoDN9ItI4=
=ARa/
-----END PGP SIGNATURE-----
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
v1.14
Changes
+ Added the ability to change remotes (mirrors)
+ Rclone errors are now reported properly
= Fixed some bugs
// Will fix device storage when I get my quest 2 and can debug adb
Date 10/17/2020
VT: https://bit.ly/3j8kCm8
MD5 Checksum: B862B807462CE35CC930C2B1118D5692
SHA-1 Checksum: F52C8630C89CAEDB2E90A857E494DE00B137712D
SHA-256 Checksum: 3DDFA2B4CCFC0E9F6F86E8A74E6D3511B9C643F4C4AF19D3878B8704FA360558
SHA-512 Checksum: 16A022D8A4D235D7C537D9F01FE7C5F964A67A3A8CD68B3DC17AF08D94837DAC6CE0C20557D029DC169E5616B36C2A4C36B1647CE0DF930C7694F3FEC3430E70
You can always get my public key from rookie.wtf/pgp.txt
You can verify the signed message using kleopatra for windows or dark.fail/pgp
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEEG9eYw4mQVDjvGf4lvrtJUNxETxMFAl+KrhQACgkQvrtJUNxE
TxP9MRAAjZbYWkbZPK+QMqzNqsgEARs6buzrwc24dth6QEod9Fgmdg5wdinZdG8X
MK467pd9u0JqM3u8DeKSUD0GEzJZStNKsbHfjvNAF3m8toaXLncWy7MYNN9loAra
17MvUBBwB1QgsXtQ3EXT1cBbS3YkQPg5E+ayvY9ZgyiOpDK6fs1tA24NZUWc53kY
TIl5IL3mZNfJck8Xc/QkZJmkBOA89oDToLOjNmZRXXEHNT/eTS6pEkMZonWQjGqF
D2dEfQTOuIAD93JzwcP5wVJFh7z6o65/7tfguDqBsTgoRWODaY+kQJDAxoWN9w/n
nagXg/y30EmE1mWQFUYa1n0V65OzT5+J8VF53DCHkgaejYLfi0lhy8TlWQZpmzen
WJOsP1Oj3ZZ9sErO8waajDBzDZ3kU9NvAOeOblE491w0713MYZYs5BVgoyHi24xO
obcp1gpLi326fkPqajgIFpDHJ014OnaXmoY9ynW9rlZJ4rwJ7WytRDzcpfyk9LV1
G1tlcCS4c/RmuVfZi4Jh0guvOOIpJSqdG4KUtvd548sslTMdHytt+YugbKqcBAto
nWroKcsmg0swAm7YgYmQXqcpM17JC21eUgm3Bc4ZZSDtP4g3Pm7vRO1+hwUNYBE9
QeEMXYDPRsTxdpuEFsbmS/EfnndvJvl9BL92Zt4+T4ruL+II50s=
=r+0B
-----END PGP SIGNATURE-----
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
New version v1.13 Released
Changes
+ Pgp messages will be available now for every update, so you can verify updates easily
= Fixed some bugs like the rclone suicide
- Removed china detection
Date 9/10/2020
MD5 Checksum: 3834485C5AF611523460E639969284F3
SHA-1 Checksum: DAF556D34F80FDE6A6A3D8F338381030B2B19E44
SHA-256 Checksum: 063FABA6A76A98AB862BBBF6F3348F176F66FD4932075E761A5FCAA85785EE4B
SHA-512 Checksum: CC743BD95826664535239BB665934E88A25CC948B95E4A3E8B34379C1E8DEB8FED05EAD87428F4B85D5841ED9101F38E4E3DAFB0EAF3CD2F2E4314B62356104D
You can always get my public key from rookie.wtf/pgp.txt
You can verify the signed message using kleopatra for windows or dark.fail/pgp
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEEG9eYw4mQVDjvGf4lvrtJUNxETxMFAl9aBpMACgkQvrtJUNxE
TxOJcA/+LEzXQtyBhZ0oj22PEk0yMLQYOBLYDB96jf39IqLQAzrIUL339jRcRKMv
yhFWXpvND1wrQl3/LNtFpBSkSnvqUXtxHWg6US+Ov3nnrEvYiwK8D9f1eC5Mt3Wy
4HZIyTh9HcrLsfbnP2kDlLOaMQSIH4rChezF79xZMcx7kRmQ0/hyXlmGAPyWs8t8
5aLxk5YUcfq1Pu+ncqeX+ihYVzQHi2eXaOAIR1rSCEXfHEg9tkRzWcma8tY29gQv
IgGwxF/YnAPOdBtzuakaT88a1MsP+tAcWxmsyvTQHrv1m0h3QPU1DxMFUfAlBjLM
5tHgTTjkXedRpR+Kga5CDuK7L5p1zVMRDINcUOf9MXV2fRurZ+OIHefAd+zbrd0n
vHi1D1sFzb/lT5LCa/vrfO9crRnLdK1EI/5HNeSj9iIdD2jEMbjSjo9yv7LA2MHi
4wnvguRpK5YUGqGc2yyCRxtyKAgXXxLBFNb5K453vdLtDukZIkhLuQVchsk4PutZ
1uIg7/ZmAIPxlEHaLckaV7QEFbq9h2hFWOsSjWAATyvnkp4nxyvAavW4zNQCbd3V
XJhimZkE+3SNAuZGw10nikVz7DFMlI0dyL+jmwIuODcFMZZwkEtBr3J/Ad9V1k+G
DFKYJpBgATHdRTwt7/zZwJCBq0ONheA/6+6VYj8x8Q5rqbiDDdc=
=xp7X
-----END PGP SIGNATURE-----
1.11HF1
= Fixed some crashes that occured when a unauthorized device was used with the sideloader
1.11
+ Added support for multiple devices
1.10SU1
+ Settings should now persist through updates
- Removed download button and hwid stuff
1.10
+ Added back the game size messagebox
+ Added support for unicode in adb
= Fixed restore gamedata
= Clicking the first game won't remove it since it causes problems
= Changed the sideloader icon
1.9SU2
+ Clicking a game on the listbox will now remove it from the list
1.9SU1
- Removed some code I forgot to remove when debugging
1.9
+ Added a queue for downloading games
+ Added a setting for disabling user.json push
1.8SU2
= Install game now won't throw an exception when a game isn't selected
1.8SU1
+ Added debug logs for rclone
1.8
+ Disk info updates every command that changes disk space
= Rclone now logs commands and output
= Changed backup button
1.7.5
+ Made sideloader even faster when opening
+ Added some chiptune music and the setting to stop it
= Fixed donator button broken in 1.7
1.7
+ Removed some freezes
+ Sideloader kills rclone on start and on close if it's open so you dont waste bandwith
+ Updating rclone config now does a hash check first
+ Refresh storage after game install and on game uninstall
1.6
+ Disk space label and check
+ Checks game size before download
= Download and install game button outputs adb log now
- Launch package name button and textbox
1.5
+ Added ETA, based on CURRENT DOWNLOAD SPEED NOT DELTA OF IT
+ Added DLS label instead of showing it in the toolbar
+ Added more indication of what the software is doing, in the toolbar
+ Added some tooltips
1.4
+ Donators can now change the speed of the donate button color change (even disable)
+ Auto update for the rclone config
+ Theme export and import buttons (by gotard)
1.3
+ Added progress bar logic for game downloading with rclone
+ Uninstalling apps now asks the user if it should also remove game data
= Sorted installed apps combobox
= Fixed uninstalling apps by package names lol nobody knew was broken
1.2
+ Added unicode support for rclone
+ Added some delay on game installation
= Fixed stuff
1.1
+ Added Themes
+ Added dollarvr user.json
+ Added user.json transfer on game download
+ Added user.json transfer on first run
= Fixed tab indexes
= Now both drop downs can be visible at the same time
1.0HF1
= Fixed a problem when deleting an app
1.0
+ Added games auto download
+ Added auto troubleshoot
+ Every app installed will have all perms granted
+ Displays apps for games instead of package names if you have rclone set up
+ Uninstall app now will also delete its obb folder
//For auto download you need an rclone config and rclone to be set up correctly
0.15HF1
+ Added list apps button back
= Fixed Uninstall apk and get apk
0.15
= MASSIVE UI REDESIGN
+ Added toggle for tor/clearnet
+ Added download games through tor but still waiting for someone to help me host the files
+ Added download with progress
= Changed adb sideload command to allow downgrades too
- Removed perms stuff, was too buggy
0.14HF1
+ Added donate button
= Fixed ui order
- Removed warning from bulk obb sideload button
0.14
+ Check file hash button
+ Added first run check (will maybe be used to create user.json files)
+ Added drag and drop for apk and obb files
+ Progress bar now works for every adb command
= Moved buttons
- Instructions button
- Removed run custom adb command button and form
0.13
+ Added vrmoo.cn.json
+ Added bulk obb copy
= Message Box now top most
0.12
+ Added settings form
+ Added progress bar (again)
+ Added a new user.json
= Fixed crash on some systems (PerformanceCounter)
= Fixed crash because of file name inconsistency
- Removed performance counters because they made the software not work for some people
0.11
+ Changed normal message boxes to flexible ones
+ Added changelog to update message
= Improved update message
- Removed progressbar
0.10
+ Added few tooltips
+ Sideload folder now works for all apks (recursive search)
= Obb copy and Sideload progress bar problems should be fixed
= Fixed user.json not working if the any of folder had spaces
= Switched back to Message Boxes
0.9
+ Added the buggy progress bar back, uses different "logic"
+ Added an icon (Increased the exe size by like 300% just with that)
+ Added sideload folder button
+ Added Create user.json button and form
+ Changed Message Boxes to notifications
0.8.5
+ Added auto update download
= Fixed a bug where if you didn't have the adb folder it would crash
0.8
+ Every command now shows progress on title bar
+ Automatically run Adb Devices and List Apps on form startup
+ You can now search the App List Combo Box
= Cleaned some code
0.7
= Fixed UI Freezes
+ Added Uninstall APK Button
+ Added Launch package Button
- Removed loading bar
0.6
+ Added List Apk Perms button
+ Added Change Permissions button
+ Added dinamically added checkbox for permisssions
= The software now downloads adb from master instead of v0.3 release
0.5
- Removed Flash Firmware
= Redesigned UI
= Reworked RunAdbCommand function (now it will be possible to do stuff I wasnt able to do before)
= Replaced Ui Buttons, still needs work
+ Added List apk button
+ Added List apk combo box
+ Added get apk function
= Cleaned a bit of code
0.4
+ Added auto download of adb archive
+ Added auto extraction of adb archive
0.3
+ Added new form, you can run custom adb commands now
+ Added recover and backup app data
- Removed Select APK and Select OBB Buttons
- Removed tooltips from removed buttons
+ Sideload APK and Copy Obb buttons now also make you select the file/folder
+ Improved firmware button
0.2
+ Added Flash Firmware button
+ Added few tooltips
+ Renamed buttons
0.1
+ Initial Release
HF - Hot Fix
SU - Small Update
RSL 3.0.1
- 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>

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