├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ └── melonds-issue.md
├── changelog
│ ├── gitHub.md
│ └── playStore
│ │ └── whatsnew-en-GB
├── images
│ ├── screenshot_mobile0.png
│ ├── screenshot_mobile1.png
│ ├── screenshot_mobile2.png
│ └── screenshot_mobile3.png
└── workflows
│ ├── main.yaml
│ ├── release-playstore.yaml
│ └── release.yaml
├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── app
├── .gitignore
├── CMakeLists.txt
├── build.gradle.kts
├── proguard-rules.pro
├── schemas
│ └── me.magnum.melonds.database.MelonDatabase
│ │ ├── 2.json
│ │ ├── 3.json
│ │ ├── 4.json
│ │ └── 5.json
└── src
│ ├── androidTest
│ └── java
│ │ └── me
│ │ └── magnum
│ │ └── melonds
│ │ └── database
│ │ └── migrations
│ │ └── Migration4to5Test.kt
│ ├── debug
│ └── res
│ │ └── values
│ │ └── strings.xml
│ ├── gitHub
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── me
│ │ │ └── magnum
│ │ │ └── melonds
│ │ │ ├── common
│ │ │ └── providers
│ │ │ │ └── UpdateContentProvider.kt
│ │ │ ├── di
│ │ │ └── GitHubModule.kt
│ │ │ └── github
│ │ │ ├── GitHubApi.kt
│ │ │ ├── GitHubConstants.kt
│ │ │ ├── dtos
│ │ │ ├── AssetDto.kt
│ │ │ └── ReleaseDto.kt
│ │ │ └── services
│ │ │ └── GitHubUpdateInstallManager.kt
│ └── res
│ │ └── xml
│ │ ├── pref_general_updates.xml
│ │ └── provider_paths.xml
│ ├── gitHubNightly
│ └── java
│ │ └── me
│ │ └── magnum
│ │ └── melonds
│ │ ├── di
│ │ └── GitHubNightlyModule.kt
│ │ └── github
│ │ └── repositories
│ │ └── GitHubNightlyUpdatesRepository.kt
│ ├── gitHubProd
│ └── java
│ │ └── me
│ │ └── magnum
│ │ └── melonds
│ │ ├── di
│ │ └── GitHubProdModule.kt
│ │ └── github
│ │ └── repositories
│ │ └── GitHubProdUpdatesRepository.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── cpp
│ │ ├── AndroidFrameRenderedCallback.cpp
│ │ ├── AndroidFrameRenderedCallback.h
│ │ ├── AndroidRACallback.cpp
│ │ ├── AndroidRACallback.h
│ │ ├── JniEnvHandler.cpp
│ │ ├── JniEnvHandler.h
│ │ ├── MelonDSAndroidCameraHandler.cpp
│ │ ├── MelonDSAndroidCameraHandler.h
│ │ ├── MelonDSAndroidConfiguration.cpp
│ │ ├── MelonDSAndroidConfiguration.h
│ │ ├── MelonDSAndroidInterface.cpp
│ │ ├── MelonDSAndroidInterface.h
│ │ ├── MelonDSAndroidJNI.cpp
│ │ ├── MelonDSNandJNI.cpp
│ │ ├── RAAchievementMapper.cpp
│ │ ├── RAAchievementMapper.h
│ │ ├── UriFileHandler.cpp
│ │ └── UriFileHandler.h
│ ├── ic_launcher-web.png
│ ├── java
│ │ └── me
│ │ │ └── magnum
│ │ │ └── melonds
│ │ │ ├── MelonDSAndroidInterface.kt
│ │ │ ├── MelonDSApplication.kt
│ │ │ ├── MelonDSiNand.kt
│ │ │ ├── MelonEmulator.kt
│ │ │ ├── common
│ │ │ ├── Crc32.kt
│ │ │ ├── Deletable.kt
│ │ │ ├── DirectoryAccessValidator.kt
│ │ │ ├── Permission.kt
│ │ │ ├── PermissionHandler.kt
│ │ │ ├── RetroAchievementsCallback.kt
│ │ │ ├── UriFileHandler.kt
│ │ │ ├── UriPermissionManager.kt
│ │ │ ├── camera
│ │ │ │ ├── BlackDSiCameraSource.kt
│ │ │ │ └── DSiCameraSource.kt
│ │ │ ├── cheats
│ │ │ │ ├── CheatDatabaseParser.kt
│ │ │ │ ├── CheatDatabaseParserListener.kt
│ │ │ │ ├── ProgressTrackerInputStream.kt
│ │ │ │ └── XmlCheatDatabaseParser.kt
│ │ │ ├── contracts
│ │ │ │ ├── CreateFileContract.kt
│ │ │ │ ├── DirectoryPickerContract.kt
│ │ │ │ └── FilePickerContract.kt
│ │ │ ├── network
│ │ │ │ └── MelonOkHttpInterceptor.kt
│ │ │ ├── opengl
│ │ │ │ ├── Shader.kt
│ │ │ │ ├── ShaderFactory.kt
│ │ │ │ └── ShaderProgramSource.kt
│ │ │ ├── retroachievements
│ │ │ │ ├── AndroidRAAchievementSignatureProvider.kt
│ │ │ │ └── AndroidRAUserAuthStore.kt
│ │ │ ├── romprocessors
│ │ │ │ ├── CompressedRomFileProcessor.kt
│ │ │ │ ├── NdsRomFileProcessor.kt
│ │ │ │ ├── RomFileProcessor.kt
│ │ │ │ ├── RomFileProcessorFactory.kt
│ │ │ │ ├── SevenZRomFileProcessor.kt
│ │ │ │ └── ZipRomFileProcessor.kt
│ │ │ ├── runtime
│ │ │ │ └── ScreenshotFrameBufferProvider.kt
│ │ │ ├── uridelegates
│ │ │ │ ├── CompositeUriHandler.kt
│ │ │ │ ├── ContentUriHandler.kt
│ │ │ │ ├── StandardFileUriHandler.kt
│ │ │ │ └── UriHandler.kt
│ │ │ ├── vibration
│ │ │ │ ├── Api26VibratorDelegate.kt
│ │ │ │ ├── OldVibratorDelegate.kt
│ │ │ │ └── TouchVibrator.kt
│ │ │ └── workers
│ │ │ │ ├── CheatImportWorker.kt
│ │ │ │ └── RetroAchievementsSubmissionWorker.kt
│ │ │ ├── database
│ │ │ ├── MelonDatabase.kt
│ │ │ ├── callback
│ │ │ │ └── CustomCheatCreationCallback.kt
│ │ │ ├── converters
│ │ │ │ └── InstantConverter.kt
│ │ │ ├── daos
│ │ │ │ ├── CheatDao.kt
│ │ │ │ ├── CheatDatabaseDao.kt
│ │ │ │ ├── CheatFolderDao.kt
│ │ │ │ ├── GameDao.kt
│ │ │ │ └── RAAchievementsDao.kt
│ │ │ ├── entities
│ │ │ │ ├── CheatDatabaseEntity.kt
│ │ │ │ ├── CheatEntity.kt
│ │ │ │ ├── CheatFolderEntity.kt
│ │ │ │ ├── CheatFolderWithCheats.kt
│ │ │ │ ├── CheatStatusUpdate.kt
│ │ │ │ ├── GameEntity.kt
│ │ │ │ ├── GameWithCheatCategories.kt
│ │ │ │ └── retroachievements
│ │ │ │ │ ├── RAAchievementEntity.kt
│ │ │ │ │ ├── RAGameEntity.kt
│ │ │ │ │ ├── RAGameHashEntity.kt
│ │ │ │ │ ├── RAGameSetMetadata.kt
│ │ │ │ │ ├── RAPendingAchievementSubmissionEntity.kt
│ │ │ │ │ └── RAUserAchievementEntity.kt
│ │ │ └── migrations
│ │ │ │ ├── Migration1to2.kt
│ │ │ │ └── Migration4to5.kt
│ │ │ ├── di
│ │ │ ├── AppModule.kt
│ │ │ ├── CoilModule.kt
│ │ │ ├── DatabaseModule.kt
│ │ │ ├── EmulatorRuntimeModule.kt
│ │ │ ├── MelonModule.kt
│ │ │ ├── MigrationModule.kt
│ │ │ ├── RAModule.kt
│ │ │ └── entrypoint
│ │ │ │ └── InitializerEntryPoint.kt
│ │ │ ├── domain
│ │ │ ├── model
│ │ │ │ ├── AudioBitrate.kt
│ │ │ │ ├── AudioInterpolation.kt
│ │ │ │ ├── AudioLatency.kt
│ │ │ │ ├── Background.kt
│ │ │ │ ├── Cheat.kt
│ │ │ │ ├── CheatDatabase.kt
│ │ │ │ ├── CheatFolder.kt
│ │ │ │ ├── CheatImportProgress.kt
│ │ │ │ ├── CheatInFolder.kt
│ │ │ │ ├── ConfigurationDirResult.kt
│ │ │ │ ├── ConsoleType.kt
│ │ │ │ ├── ControllerConfiguration.kt
│ │ │ │ ├── DSiWareTitle.kt
│ │ │ │ ├── DownloadProgress.kt
│ │ │ │ ├── EmulatorConfiguration.kt
│ │ │ │ ├── FirmwareColour.kt
│ │ │ │ ├── FirmwareConfiguration.kt
│ │ │ │ ├── FpsCounterPosition.kt
│ │ │ │ ├── Game.kt
│ │ │ │ ├── Input.kt
│ │ │ │ ├── InputConfig.kt
│ │ │ │ ├── MacAddress.kt
│ │ │ │ ├── MicSource.kt
│ │ │ │ ├── Point.kt
│ │ │ │ ├── Rect.kt
│ │ │ │ ├── RendererConfiguration.kt
│ │ │ │ ├── RomIconFiltering.kt
│ │ │ │ ├── RomInfo.kt
│ │ │ │ ├── RomMetadata.kt
│ │ │ │ ├── RomScanningStatus.kt
│ │ │ │ ├── RuntimeBackground.kt
│ │ │ │ ├── SaveStateLocation.kt
│ │ │ │ ├── SaveStateSlot.kt
│ │ │ │ ├── SizeUnit.kt
│ │ │ │ ├── SortingMode.kt
│ │ │ │ ├── SortingOrder.kt
│ │ │ │ ├── Version.kt
│ │ │ │ ├── VideoFiltering.kt
│ │ │ │ ├── VideoRenderer.kt
│ │ │ │ ├── appupdate
│ │ │ │ │ └── AppUpdate.kt
│ │ │ │ ├── camera
│ │ │ │ │ └── DSiCameraSourceType.kt
│ │ │ │ ├── dsinand
│ │ │ │ │ ├── DSiWareTitleFileType.kt
│ │ │ │ │ ├── ImportDSiWareTitleResult.kt
│ │ │ │ │ └── OpenDSiNandResult.kt
│ │ │ │ ├── emulator
│ │ │ │ │ ├── EmulatorSessionUpdateAction.kt
│ │ │ │ │ ├── FirmwareLaunchResult.kt
│ │ │ │ │ └── RomLaunchResult.kt
│ │ │ │ ├── layout
│ │ │ │ │ ├── BackgroundMode.kt
│ │ │ │ │ ├── LayoutComponent.kt
│ │ │ │ │ ├── LayoutConfiguration.kt
│ │ │ │ │ ├── PositionedLayoutComponent.kt
│ │ │ │ │ ├── ScreenFold.kt
│ │ │ │ │ ├── UILayout.kt
│ │ │ │ │ └── UILayoutVariant.kt
│ │ │ │ ├── render
│ │ │ │ │ └── FrameRenderEvent.kt
│ │ │ │ ├── retroachievements
│ │ │ │ │ ├── GameAchievementData.kt
│ │ │ │ │ ├── RAEvent.kt
│ │ │ │ │ ├── RAGameSummary.kt
│ │ │ │ │ ├── RASimpleAchievement.kt
│ │ │ │ │ ├── RAUserAchievement.kt
│ │ │ │ │ └── exception
│ │ │ │ │ │ └── RAGameNotExist.kt
│ │ │ │ ├── rom
│ │ │ │ │ ├── Rom.kt
│ │ │ │ │ └── config
│ │ │ │ │ │ ├── RomConfig.kt
│ │ │ │ │ │ ├── RomGbaSlotConfig.kt
│ │ │ │ │ │ ├── RuntimeConsoleType.kt
│ │ │ │ │ │ ├── RuntimeEnum.kt
│ │ │ │ │ │ └── RuntimeMicSource.kt
│ │ │ │ └── ui
│ │ │ │ │ └── Orientation.kt
│ │ │ ├── repositories
│ │ │ │ ├── BackgroundRepository.kt
│ │ │ │ ├── CheatsRepository.kt
│ │ │ │ ├── DSiWareMetadataRepository.kt
│ │ │ │ ├── LayoutsRepository.kt
│ │ │ │ ├── RetroAchievementsRepository.kt
│ │ │ │ ├── RomsRepository.kt
│ │ │ │ ├── SaveStatesRepository.kt
│ │ │ │ ├── SettingsRepository.kt
│ │ │ │ └── UpdatesRepository.kt
│ │ │ └── services
│ │ │ │ ├── ConfigurationDirectoryVerifier.kt
│ │ │ │ ├── DSiNandManager.kt
│ │ │ │ ├── EmulatorManager.kt
│ │ │ │ └── UpdateInstallManager.kt
│ │ │ ├── extensions
│ │ │ ├── ActivityExtensions.kt
│ │ │ ├── ContextExtensions.kt
│ │ │ ├── DisposableExtensions.kt
│ │ │ ├── DocumentFileExtensions.kt
│ │ │ ├── FragmentViewBindingDelegate.kt
│ │ │ ├── ListExtensions.kt
│ │ │ ├── ParcelableExtensions.kt
│ │ │ ├── PreferenceExtensions.kt
│ │ │ ├── StringExtensions.kt
│ │ │ ├── ViewExtensions.kt
│ │ │ └── WindowExtensions.kt
│ │ │ ├── impl
│ │ │ ├── AndroidDSiNandManager.kt
│ │ │ ├── AndroidRetroAchievementsRepository.kt
│ │ │ ├── BackgroundThumbnailProvider.kt
│ │ │ ├── DefaultLayoutProvider.kt
│ │ │ ├── FileSystemConfigurationDirectoryVerifier.kt
│ │ │ ├── FileSystemRomsRepository.kt
│ │ │ ├── FileSystemSaveStatesRepository.kt
│ │ │ ├── InternalBackgroundsRepository.kt
│ │ │ ├── InternalLayoutsRepository.kt
│ │ │ ├── NdsRomCache.kt
│ │ │ ├── NusDSiWareMetadataRepository.kt
│ │ │ ├── RomIconProvider.kt
│ │ │ ├── RoomCheatsRepository.kt
│ │ │ ├── SaveStateScreenshotProvider.kt
│ │ │ ├── ScreenUnitsConverter.kt
│ │ │ ├── SharedPreferencesSettingsRepository.kt
│ │ │ ├── XmlCheatDatabaseSAXHandler.kt
│ │ │ ├── camera
│ │ │ │ ├── CameraBuffers.kt
│ │ │ │ ├── DSiCameraSourceMultiplexer.kt
│ │ │ │ ├── PhysicalDSiCameraSource.kt
│ │ │ │ └── StaticImageDSiCameraSource.kt
│ │ │ ├── dtos
│ │ │ │ ├── input
│ │ │ │ │ ├── ControllerConfigurationDto.kt
│ │ │ │ │ └── InputConfigDto.kt
│ │ │ │ ├── layout
│ │ │ │ │ ├── LayoutConfigurationDto.kt
│ │ │ │ │ ├── PointDto.kt
│ │ │ │ │ ├── PositionedLayoutComponentDto.kt
│ │ │ │ │ ├── RectDto.kt
│ │ │ │ │ ├── ScreenFoldDto.kt
│ │ │ │ │ ├── UILayoutDto.kt
│ │ │ │ │ └── UILayoutVariantDto.kt
│ │ │ │ └── rom
│ │ │ │ │ ├── RomConfigDto.kt
│ │ │ │ │ ├── RomDto.kt
│ │ │ │ │ └── RomGbaSlotConfigDto.kt
│ │ │ ├── emulator
│ │ │ │ ├── AndroidEmulatorManager.kt
│ │ │ │ ├── EmulatorSession.kt
│ │ │ │ ├── LifecycleOwnerProvider.kt
│ │ │ │ ├── SramLoadException.kt
│ │ │ │ └── SramProvider.kt
│ │ │ ├── image
│ │ │ │ ├── BitmapFactoryBitmapLoader.kt
│ │ │ │ ├── BitmapLoader.kt
│ │ │ │ ├── CoilBackgroundThumbnailFetcher.kt
│ │ │ │ └── ImageDecoderBitmapLoader.kt
│ │ │ ├── layout
│ │ │ │ └── UILayoutProvider.kt
│ │ │ ├── mappers
│ │ │ │ └── retroachievements
│ │ │ │ │ └── RAAchievementMapper.kt
│ │ │ ├── retroachievements
│ │ │ │ └── NoCacheRAAchievementsDao.kt
│ │ │ └── romprocessors
│ │ │ │ ├── Api24RomFileProcessorFactory.kt
│ │ │ │ └── BaseRomFileProcessorFactory.kt
│ │ │ ├── initializer
│ │ │ └── CoilInitializer.kt
│ │ │ ├── migrations
│ │ │ ├── Migration.kt
│ │ │ ├── Migration14to15.kt
│ │ │ ├── Migration16to17.kt
│ │ │ ├── Migration20to21.kt
│ │ │ ├── Migration21to22.kt
│ │ │ ├── Migration24to25.kt
│ │ │ ├── Migration25to26.kt
│ │ │ ├── Migration30to31.kt
│ │ │ ├── Migration31to32.kt
│ │ │ ├── Migration33to34.kt
│ │ │ ├── Migration6to7.kt
│ │ │ ├── Migration7to8.kt
│ │ │ ├── Migrator.kt
│ │ │ ├── helper
│ │ │ │ └── GenericJsonArrayMigrationHelper.kt
│ │ │ └── legacy
│ │ │ │ ├── Rom21.kt
│ │ │ │ ├── Rom22.kt
│ │ │ │ ├── RomConfig1.kt
│ │ │ │ ├── RomConfigDto25.kt
│ │ │ │ ├── RomConfigDto31.kt
│ │ │ │ ├── RomDto25.kt
│ │ │ │ ├── RomDto31.kt
│ │ │ │ ├── RomGbaSlotConfigDto31.kt
│ │ │ │ ├── input
│ │ │ │ ├── ControllerConfigurationDto33.kt
│ │ │ │ └── InputConfigDto33.kt
│ │ │ │ └── layout
│ │ │ │ ├── LayoutConfiguration25.kt
│ │ │ │ ├── LayoutConfigurationDto31.kt
│ │ │ │ ├── PositionedLayoutComponent25.kt
│ │ │ │ ├── Rect25.kt
│ │ │ │ └── UILayout25.kt
│ │ │ ├── parcelables
│ │ │ ├── BackgroundParcelable.kt
│ │ │ ├── RomConfigParcelable.kt
│ │ │ ├── RomGbaSlotConfigParcelable.kt
│ │ │ ├── RomInfoParcelable.kt
│ │ │ ├── RomParcelable.kt
│ │ │ └── cheat
│ │ │ │ ├── CheatFolderParcelable.kt
│ │ │ │ ├── CheatParcelable.kt
│ │ │ │ └── GameParcelable.kt
│ │ │ ├── ui
│ │ │ ├── Theme.kt
│ │ │ ├── backgrounds
│ │ │ │ ├── BackgroundsActivity.kt
│ │ │ │ ├── BackgroundsNavigation.kt
│ │ │ │ ├── BackgroundsViewModel.kt
│ │ │ │ └── ui
│ │ │ │ │ ├── BackgroundItem.kt
│ │ │ │ │ ├── BackgroundListScreen.kt
│ │ │ │ │ └── BackgroundPreviewScreen.kt
│ │ │ ├── cheats
│ │ │ │ ├── CheatsActivity.kt
│ │ │ │ ├── CheatsNavigation.kt
│ │ │ │ ├── CheatsViewModel.kt
│ │ │ │ ├── model
│ │ │ │ │ ├── CheatFormDialogState.kt
│ │ │ │ │ ├── CheatSubmissionForm.kt
│ │ │ │ │ ├── CheatsScreenUiState.kt
│ │ │ │ │ ├── DeletedCheat.kt
│ │ │ │ │ └── OpenScreenEvent.kt
│ │ │ │ └── ui
│ │ │ │ │ ├── CheatListScreen.kt
│ │ │ │ │ ├── CheatsScreen.kt
│ │ │ │ │ ├── EnabledCheatsListScreen.kt
│ │ │ │ │ ├── FolderListScreen.kt
│ │ │ │ │ ├── GameListScreen.kt
│ │ │ │ │ ├── LoadingScreen.kt
│ │ │ │ │ ├── cheatform
│ │ │ │ │ ├── CheatForm.kt
│ │ │ │ │ └── CheatFormState.kt
│ │ │ │ │ └── item
│ │ │ │ │ ├── CheatInFolderItem.kt
│ │ │ │ │ ├── CheatItem.kt
│ │ │ │ │ ├── FolderItem.kt
│ │ │ │ │ └── GameItem.kt
│ │ │ ├── common
│ │ │ │ ├── Colors.kt
│ │ │ │ ├── FullScreen.kt
│ │ │ │ ├── LayoutComponentView.kt
│ │ │ │ ├── LayoutComponentViewBuilder.kt
│ │ │ │ ├── LayoutComponentViewBuilderFactory.kt
│ │ │ │ ├── LayoutView.kt
│ │ │ │ ├── LoadingDialog.kt
│ │ │ │ ├── MelonPreviewSet.kt
│ │ │ │ ├── ModifierExtensions.kt
│ │ │ │ ├── MultiActionFloatingActionButton.kt
│ │ │ │ ├── TextInputDialog.kt
│ │ │ │ ├── component
│ │ │ │ │ ├── dialog
│ │ │ │ │ │ ├── BaseDialog.kt
│ │ │ │ │ │ └── TextInputDialog.kt
│ │ │ │ │ └── text
│ │ │ │ │ │ ├── CaptionText.kt
│ │ │ │ │ │ └── OutlinedTextFieldWithError.kt
│ │ │ │ ├── componentbuilders
│ │ │ │ │ ├── BottomScreenLayoutComponentViewBuilder.kt
│ │ │ │ │ ├── ButtonsLayoutComponentViewBuilder.kt
│ │ │ │ │ ├── DpadLayoutComponentViewBuilder.kt
│ │ │ │ │ ├── EditorBackgroundLayoutComponentViewBuilder.kt
│ │ │ │ │ ├── ScreenLayoutComponentViewBuilder.kt
│ │ │ │ │ ├── SingleButtonLayoutComponentViewBuilder.kt
│ │ │ │ │ └── TopScreenLayoutComponentViewBuilder.kt
│ │ │ │ ├── preference
│ │ │ │ │ ├── ActionLauncherPreference.kt
│ │ │ │ │ ├── SingleChoicePreference.kt
│ │ │ │ │ └── SwitchPreference.kt
│ │ │ │ └── viewmodel
│ │ │ │ │ └── RetroAchievementsViewModel.kt
│ │ │ ├── dsiwaremanager
│ │ │ │ ├── DSiWareManagerActivity.kt
│ │ │ │ ├── DSiWareManagerViewModel.kt
│ │ │ │ ├── DSiWareRomListViewModel.kt
│ │ │ │ ├── model
│ │ │ │ │ ├── DSiWareItemDropdownMenu.kt
│ │ │ │ │ ├── DSiWareManagerUiState.kt
│ │ │ │ │ ├── DSiWareMangerRomListUiState.kt
│ │ │ │ │ └── ImportExportDSiWareTitleFileEvent.kt
│ │ │ │ └── ui
│ │ │ │ │ ├── DSiWareItem.kt
│ │ │ │ │ ├── DSiWareManagerScreen.kt
│ │ │ │ │ ├── DSiWareRomListDialog.kt
│ │ │ │ │ ├── DSiWareTitleFileActions.kt
│ │ │ │ │ └── RomItem.kt
│ │ │ ├── emulator
│ │ │ │ ├── DSRenderer.kt
│ │ │ │ ├── EmulatorActivity.kt
│ │ │ │ ├── EmulatorFrameRenderedListener.kt
│ │ │ │ ├── EmulatorRetroAchievementsViewModel.kt
│ │ │ │ ├── EmulatorViewModel.kt
│ │ │ │ ├── PauseMenuOption.kt
│ │ │ │ ├── RuntimeLayoutComponentViewBuilderFactory.kt
│ │ │ │ ├── RuntimeLayoutView.kt
│ │ │ │ ├── component
│ │ │ │ │ └── EmulatorOverlayTracker.kt
│ │ │ │ ├── exceptions
│ │ │ │ │ ├── RomLoadException.kt
│ │ │ │ │ └── SaveSlotLoadException.kt
│ │ │ │ ├── firmware
│ │ │ │ │ └── FirmwarePauseMenuOption.kt
│ │ │ │ ├── input
│ │ │ │ │ ├── BaseInputHandler.kt
│ │ │ │ │ ├── ButtonsInputHandler.kt
│ │ │ │ │ ├── DpadInputHandler.kt
│ │ │ │ │ ├── FeedbackInputHandler.kt
│ │ │ │ │ ├── FrontendInputHandler.kt
│ │ │ │ │ ├── IInputListener.kt
│ │ │ │ │ ├── INativeInputListener.kt
│ │ │ │ │ ├── InputProcessor.kt
│ │ │ │ │ ├── MelonTouchHandler.kt
│ │ │ │ │ ├── MultiButtonInputHandler.kt
│ │ │ │ │ ├── SingleButtonInputHandler.kt
│ │ │ │ │ ├── TouchscreenInputHandler.kt
│ │ │ │ │ ├── componentbuilder
│ │ │ │ │ │ ├── RuntimeScreenLayoutComponentViewBuilder.kt
│ │ │ │ │ │ └── ToggleableSingleButtonLayoutComponentViewBuilder.kt
│ │ │ │ │ └── view
│ │ │ │ │ │ └── ToggleableImageView.kt
│ │ │ │ ├── model
│ │ │ │ │ ├── EmulatorOverlay.kt
│ │ │ │ │ ├── EmulatorState.kt
│ │ │ │ │ ├── EmulatorUiEvent.kt
│ │ │ │ │ ├── PauseMenu.kt
│ │ │ │ │ ├── PopupEvent.kt
│ │ │ │ │ ├── RAIntegrationEvent.kt
│ │ │ │ │ ├── RuntimeInputLayoutConfiguration.kt
│ │ │ │ │ ├── RuntimeRendererConfiguration.kt
│ │ │ │ │ └── ToastEvent.kt
│ │ │ │ ├── rewind
│ │ │ │ │ ├── EdgeSpacingDecorator.kt
│ │ │ │ │ ├── RewindSaveStateAdapter.kt
│ │ │ │ │ └── model
│ │ │ │ │ │ ├── RewindSaveState.kt
│ │ │ │ │ │ └── RewindWindow.kt
│ │ │ │ ├── rom
│ │ │ │ │ ├── RomPauseMenuOption.kt
│ │ │ │ │ └── SaveStateListAdapter.kt
│ │ │ │ └── ui
│ │ │ │ │ ├── AchievementList.kt
│ │ │ │ │ ├── AchievementListDialog.kt
│ │ │ │ │ ├── AchievementPopupUi.kt
│ │ │ │ │ └── RAIntegrationEventUi.kt
│ │ │ ├── inputsetup
│ │ │ │ ├── InputSetupActivity.kt
│ │ │ │ ├── InputSetupViewModel.kt
│ │ │ │ └── ui
│ │ │ │ │ └── InputSetupScreen.kt
│ │ │ ├── layouteditor
│ │ │ │ ├── EditorLayoutComponentViewBuilderFactory.kt
│ │ │ │ ├── LayoutBackgroundDialog.kt
│ │ │ │ ├── LayoutEditorActivity.kt
│ │ │ │ ├── LayoutEditorView.kt
│ │ │ │ ├── LayoutEditorViewModel.kt
│ │ │ │ ├── LayoutPropertiesDialog.kt
│ │ │ │ └── model
│ │ │ │ │ ├── CurrentLayoutState.kt
│ │ │ │ │ └── LayoutBackgroundProperties.kt
│ │ │ ├── layouts
│ │ │ │ ├── BaseLayoutsFragment.kt
│ │ │ │ ├── BaseLayoutsViewModel.kt
│ │ │ │ ├── LayoutListActivity.kt
│ │ │ │ ├── LayoutSelectorActivity.kt
│ │ │ │ ├── LayoutSelectorViewModel.kt
│ │ │ │ ├── LayoutsViewModel.kt
│ │ │ │ └── fragments
│ │ │ │ │ ├── LayoutListFragment.kt
│ │ │ │ │ └── LayoutSelectorFragment.kt
│ │ │ ├── romdetails
│ │ │ │ ├── RomDetailsActivity.kt
│ │ │ │ ├── RomDetailsRetroAchievementsViewModel.kt
│ │ │ │ ├── RomDetailsUiMapper.kt
│ │ │ │ ├── RomDetailsViewModel.kt
│ │ │ │ ├── model
│ │ │ │ │ ├── RomAchievementsSummary.kt
│ │ │ │ │ ├── RomConfigUiModel.kt
│ │ │ │ │ ├── RomConfigUiState.kt
│ │ │ │ │ ├── RomConfigUpdateEvent.kt
│ │ │ │ │ ├── RomDetailsTab.kt
│ │ │ │ │ ├── RomGbaSlotConfigUiModel.kt
│ │ │ │ │ └── RomRetroAchievementsUiState.kt
│ │ │ │ └── ui
│ │ │ │ │ ├── RetroAchievementsLoginDialog.kt
│ │ │ │ │ ├── RomAchievementUi.kt
│ │ │ │ │ ├── RomConfigUi.kt
│ │ │ │ │ ├── RomDetailsScreen.kt
│ │ │ │ │ ├── RomHeaderUi.kt
│ │ │ │ │ ├── RomRetroAchievementsUi.kt
│ │ │ │ │ └── preview
│ │ │ │ │ └── RAAchievementMock.kt
│ │ │ ├── romlist
│ │ │ │ ├── NoRomSearchDirectoriesFragment.kt
│ │ │ │ ├── RomIcon.kt
│ │ │ │ ├── RomListActivity.kt
│ │ │ │ ├── RomListFragment.kt
│ │ │ │ ├── RomListViewModel.kt
│ │ │ │ └── UpdatesViewModel.kt
│ │ │ ├── settings
│ │ │ │ ├── CheatsImportProgressDialog.kt
│ │ │ │ ├── FileStatusPopup.kt
│ │ │ │ ├── PreferenceFragmentHelper.kt
│ │ │ │ ├── PreferenceFragmentTitleProvider.kt
│ │ │ │ ├── RetroAchievementsSettingsViewModel.kt
│ │ │ │ ├── SettingsActivity.kt
│ │ │ │ ├── SettingsViewModel.kt
│ │ │ │ ├── fragments
│ │ │ │ │ ├── AudioPreferencesFragment.kt
│ │ │ │ │ ├── CheatsPreferencesFragment.kt
│ │ │ │ │ ├── CustomFirmwarePreferencesFragment.kt
│ │ │ │ │ ├── FirmwarePreferencesFragment.kt
│ │ │ │ │ ├── GeneralPreferencesFragment.kt
│ │ │ │ │ ├── InputPreferencesFragment.kt
│ │ │ │ │ ├── MainPreferencesFragment.kt
│ │ │ │ │ ├── RetroAchievementsPreferencesFragment.kt
│ │ │ │ │ ├── RewindPreferencesFragment.kt
│ │ │ │ │ ├── RomsPreferencesFragment.kt
│ │ │ │ │ ├── SaveFilesPreferencesFragment.kt
│ │ │ │ │ ├── SystemPreferencesFragment.kt
│ │ │ │ │ └── VideoPreferencesFragment.kt
│ │ │ │ ├── model
│ │ │ │ │ └── RetroAchievementsAccountState.kt
│ │ │ │ └── preferences
│ │ │ │ │ ├── BiosDirectoryPickerPreference.kt
│ │ │ │ │ ├── FirmwareBirthdayPreference.kt
│ │ │ │ │ ├── FirmwareColourPickerPreference.kt
│ │ │ │ │ ├── MacAddressPreference.kt
│ │ │ │ │ └── StoragePickerPreference.kt
│ │ │ ├── shortcutsetup
│ │ │ │ ├── DSFirmwareShortcutSetupActivity.kt
│ │ │ │ ├── DSiFirmwareShortcutSetupActivity.kt
│ │ │ │ ├── FirmwareShortcutSetupActivity.kt
│ │ │ │ └── ShortcutSetupActivity.kt
│ │ │ └── theme
│ │ │ │ ├── Colors.kt
│ │ │ │ ├── MelonTheme.kt
│ │ │ │ └── Typography.kt
│ │ │ └── utils
│ │ │ ├── BitmapRegionDecoderCompat.kt
│ │ │ ├── BitmapUtils.kt
│ │ │ ├── CompositeOnPreferenceChangeListener.kt
│ │ │ ├── DsScreenshotConverter.kt
│ │ │ ├── EnumUtils.kt
│ │ │ ├── FileUtils.kt
│ │ │ ├── InputUtils.kt
│ │ │ ├── PackageManagerCompat.kt
│ │ │ ├── RomProcessor.kt
│ │ │ ├── SharedFlow.kt
│ │ │ ├── SimpleDiffCallback.kt
│ │ │ ├── SingleLiveEvent.kt
│ │ │ ├── SizeUtils.kt
│ │ │ ├── UriTypeHierarchyAdapter.kt
│ │ │ └── WindowManagerCompat.kt
│ └── res
│ │ ├── anim
│ │ ├── fragment_translate_enter_pop.xml
│ │ ├── fragment_translate_enter_push.xml
│ │ ├── fragment_translate_exit_pop.xml
│ │ ├── fragment_translate_exit_push.xml
│ │ └── rotate.xml
│ │ ├── drawable-night
│ │ └── logo_dsiware.png
│ │ ├── drawable-xhdpi
│ │ └── logo_splash.png
│ │ ├── drawable
│ │ ├── background_bottom_screen.xml
│ │ ├── background_rewind_save_state_focused.xml
│ │ ├── background_top_screen.xml
│ │ ├── background_uiview.xml
│ │ ├── button_fast_forward.png
│ │ ├── button_fast_forward_disabled.png
│ │ ├── button_l.png
│ │ ├── button_microphone.png
│ │ ├── button_microphone_disabled.png
│ │ ├── button_pause.png
│ │ ├── button_quick_load.png
│ │ ├── button_quick_save.png
│ │ ├── button_r.png
│ │ ├── button_reset.png
│ │ ├── button_rewind.png
│ │ ├── button_select.png
│ │ ├── button_start.png
│ │ ├── button_swap_screens.png
│ │ ├── button_toggle_lid.png
│ │ ├── buttons.png
│ │ ├── drawable_ripple.xml
│ │ ├── drawable_ripple_small.xml
│ │ ├── ic_add.xml
│ │ ├── ic_audio.xml
│ │ ├── ic_block.xml
│ │ ├── ic_cheat.xml
│ │ ├── ic_clear.xml
│ │ ├── ic_clock.xml
│ │ ├── ic_completed.png
│ │ ├── ic_file.xml
│ │ ├── ic_firmware.xml
│ │ ├── ic_folder.xml
│ │ ├── ic_gba_cart.xml
│ │ ├── ic_input.xml
│ │ ├── ic_link.xml
│ │ ├── ic_melon_monochrome.xml
│ │ ├── ic_melon_small.png
│ │ ├── ic_menu.xml
│ │ ├── ic_points.png
│ │ ├── ic_refresh.xml
│ │ ├── ic_search.xml
│ │ ├── ic_settings.xml
│ │ ├── ic_sort.xml
│ │ ├── ic_sort_alpha.xml
│ │ ├── ic_status_error.xml
│ │ ├── ic_status_ok.xml
│ │ ├── ic_status_warn.xml
│ │ ├── ic_touch_disabled.png
│ │ ├── ic_touch_enabled.png
│ │ ├── ic_trophy.xml
│ │ ├── ic_video.xml
│ │ ├── keypad.png
│ │ ├── logo_dsiware.png
│ │ ├── selector_rewind_save_state.xml
│ │ └── selector_rewind_save_state_time_text.xml
│ │ ├── layout
│ │ ├── activity_emulator.xml
│ │ ├── activity_layout_editor.xml
│ │ ├── activity_layouts.xml
│ │ ├── activity_rom_list.xml
│ │ ├── activity_settings.xml
│ │ ├── activity_shortcut_setup.xml
│ │ ├── dialog_cheats_import_progress.xml
│ │ ├── dialog_config_files.xml
│ │ ├── dialog_firmware_birthday.xml
│ │ ├── dialog_firmware_colour_picker.xml
│ │ ├── dialog_layout_backgrounds.xml
│ │ ├── dialog_layout_properties.xml
│ │ ├── dialog_layout_update_download_progress.xml
│ │ ├── dialog_loading.xml
│ │ ├── dialog_mac_address_editor.xml
│ │ ├── dialog_retroachievements_login.xml
│ │ ├── dialog_text_input.xml
│ │ ├── fragment_layout_list.xml
│ │ ├── fragment_no_directories.xml
│ │ ├── item_file_status.xml
│ │ ├── item_layout.xml
│ │ ├── item_rewind_save_state.xml
│ │ ├── item_rom_base.xml
│ │ ├── item_rom_configurable.xml
│ │ ├── item_rom_simple.xml
│ │ ├── item_save_state_slot.xml
│ │ ├── preference_directory_picker_status.xml
│ │ ├── preference_firmware_colour_picker_colour.xml
│ │ └── rom_list_fragment.xml
│ │ ├── menu
│ │ ├── layout_item_menu.xml
│ │ ├── layouts_menu.xml
│ │ └── rom_list_menu.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ ├── ic_platform_ds.xml
│ │ └── ic_platform_dsi.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ ├── ic_platform_ds_foreground.png
│ │ └── ic_platform_dsi_foreground.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ ├── ic_platform_ds_foreground.png
│ │ └── ic_platform_dsi_foreground.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ ├── ic_platform_ds_foreground.png
│ │ └── ic_platform_dsi_foreground.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ ├── ic_platform_ds_foreground.png
│ │ └── ic_platform_dsi_foreground.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ ├── ic_platform_ds_foreground.png
│ │ └── ic_platform_dsi_foreground.png
│ │ ├── values-b+es
│ │ └── strings.xml
│ │ ├── values-b+fr
│ │ └── strings.xml
│ │ ├── values-b+id
│ │ └── strings.xml
│ │ ├── values-b+it
│ │ └── strings.xml
│ │ ├── values-b+pt+BR
│ │ └── strings.xml
│ │ ├── values-b+ru
│ │ └── strings.xml
│ │ ├── values-night
│ │ ├── colors.xml
│ │ └── values.xml
│ │ ├── values-v29
│ │ └── strings_theme.xml
│ │ ├── values
│ │ ├── arrays.xml
│ │ ├── attrs_preferences.xml
│ │ ├── colors.xml
│ │ ├── dimens_input.xml
│ │ ├── strings.xml
│ │ ├── strings_theme.xml
│ │ ├── styles.xml
│ │ └── values.xml
│ │ └── xml
│ │ ├── game_mode_config.xml
│ │ ├── network_security_config.xml
│ │ ├── pref_audio.xml
│ │ ├── pref_cheats.xml
│ │ ├── pref_custom_firmware.xml
│ │ ├── pref_general.xml
│ │ ├── pref_general_updates.xml
│ │ ├── pref_input.xml
│ │ ├── pref_internal_firmware_settings.xml
│ │ ├── pref_main.xml
│ │ ├── pref_retroachievements.xml
│ │ ├── pref_rewind.xml
│ │ ├── pref_roms.xml
│ │ ├── pref_save_files.xml
│ │ ├── pref_system.xml
│ │ ├── pref_video.xml
│ │ └── scene_activity_emulator.xml
│ ├── nightly
│ └── res
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_foreground.png
│ │ └── values
│ │ └── strings.xml
│ ├── playStore
│ └── java
│ │ └── me
│ │ └── magnum
│ │ └── melonds
│ │ ├── di
│ │ └── PlayStoreModule.kt
│ │ ├── playstore
│ │ └── PlayStoreUpdatesRepository.kt
│ │ └── services
│ │ └── PlayStoreUpdateInstallManager.kt
│ └── test
│ └── java
│ └── me
│ └── magnum
│ └── melonds
│ └── VersionTest.kt
├── build.gradle.kts
├── buildSrc
├── .gitignore
├── build.gradle.kts
└── src
│ └── main
│ └── kotlin
│ └── AppConfig.kt
├── common
├── .gitignore
├── build.gradle.kts
└── src
│ └── main
│ └── java
│ └── me
│ └── magnum
│ └── melonds
│ └── common
│ └── SuspendRunCatching.kt
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── masterswitch
├── .gitignore
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── smp
│ │ └── masterswitchpreference
│ │ ├── MasterSwitchExplanationText.kt
│ │ ├── MasterSwitchPreference.kt
│ │ ├── MasterSwitchPreferenceAttrs.kt
│ │ ├── MasterSwitchPreferenceFragment.kt
│ │ └── MasterSwitchSwitchPreference.kt
│ └── res
│ ├── values
│ ├── attrs.xml
│ └── colors.xml
│ └── xml
│ ├── blank_preference_screen.xml
│ └── explanation_preference_screen.xml
├── rcheevos-api
├── .gitignore
├── build.gradle.kts
└── src
│ └── main
│ └── java
│ └── me
│ └── magnum
│ └── rcheevosapi
│ ├── RAAchievementSignatureProvider.kt
│ ├── RAApi.kt
│ ├── RAUserAuthStore.kt
│ ├── dto
│ ├── AchievementDto.kt
│ ├── GameDto.kt
│ ├── GamePatchDto.kt
│ ├── HashLibraryDto.kt
│ ├── UserLoginDto.kt
│ ├── UserUnlocksDto.kt
│ └── mapper
│ │ ├── AchievementDtoMapper.kt
│ │ └── GameDtoMapper.kt
│ ├── exception
│ ├── UnsuccessfulRequestException.kt
│ └── UserNotAuthenticatedException.kt
│ └── model
│ ├── RAAchievement.kt
│ ├── RAGame.kt
│ ├── RAGameId.kt
│ └── RAUserAuth.kt
└── settings.gradle.kts
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: rafaelvcaetano
4 |
--------------------------------------------------------------------------------
/.github/changelog/gitHub.md:
--------------------------------------------------------------------------------
1 | **Changelog:**
2 | Beta 1.10.0
3 | * Add OpenGL renderer, with resolution scaling up to 8x native
4 | * Add support for manual cheat input
5 | * Add button to toggle microphone input (thanks @ricnava00)
6 | * Add visual indicators for toggle buttons
7 | * Add support for memory expansion GBA slot
8 | * Allow DSiWare title data to be imported/exported
9 | * Improve foldable support
10 | * Add Italian translation (thanks @alessandrosimonelli and @jincio92)
11 | * Fix crash when importing cheat databases
12 | * Minor fixes and improvements
--------------------------------------------------------------------------------
/.github/changelog/playStore/whatsnew-en-GB:
--------------------------------------------------------------------------------
1 | Beta 1.10.0
2 | • Add OpenGL renderer, with resolution scaling up to 8x native
3 | • Add support for manual cheat input
4 | • Add button to toggle microphone input
5 | • Add visual indicators for toggle buttons
6 | • Add support for memory expansion GBA slot
7 | • Allow DSiWare title data to be imported/exported
8 | • Improve foldable support
9 | • Add Italian translation
10 | • Fix crash when importing cheat databases
11 | • Minor fixes and improvements
--------------------------------------------------------------------------------
/.github/images/screenshot_mobile0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/.github/images/screenshot_mobile0.png
--------------------------------------------------------------------------------
/.github/images/screenshot_mobile1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/.github/images/screenshot_mobile1.png
--------------------------------------------------------------------------------
/.github/images/screenshot_mobile2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/.github/images/screenshot_mobile2.png
--------------------------------------------------------------------------------
/.github/images/screenshot_mobile3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/.github/images/screenshot_mobile3.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | .cxx
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 | .kotlin
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "melonDS-android-lib"]
2 | path = melonDS-android-lib
3 | url = https://github.com/rafaelvcaetano/melonDS-android-lib.git
4 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /release
--------------------------------------------------------------------------------
/app/src/debug/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | melonDS Dev
4 |
--------------------------------------------------------------------------------
/app/src/gitHub/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/gitHub/java/me/magnum/melonds/github/GitHubApi.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.github
2 |
3 | import me.magnum.melonds.github.dtos.ReleaseDto
4 | import retrofit2.http.GET
5 |
6 | interface GitHubApi {
7 | @GET("/repos/rafaelvcaetano/melonDS-android/releases/latest")
8 | suspend fun getLatestRelease(): ReleaseDto
9 |
10 | @GET("/repos/rafaelvcaetano/melonDS-android/releases/tags/nightly-release")
11 | suspend fun getLatestNightlyRelease(): ReleaseDto
12 | }
--------------------------------------------------------------------------------
/app/src/gitHub/java/me/magnum/melonds/github/GitHubConstants.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.github
2 |
3 | const val APK_CONTENT_TYPE = "application/vnd.android.package-archive"
4 |
5 | const val PREF_KEY_GITHUB_CHECK_FOR_UPDATES = "github_check_for_updates"
--------------------------------------------------------------------------------
/app/src/gitHub/java/me/magnum/melonds/github/dtos/AssetDto.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.github.dtos
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class AssetDto(
8 | @SerialName("id") val id: Long,
9 | @SerialName("browser_download_url") val url: String,
10 | @SerialName("name") val name: String,
11 | @SerialName("size") val size: Long,
12 | @SerialName("content_type") val contentType: String
13 | )
--------------------------------------------------------------------------------
/app/src/gitHub/java/me/magnum/melonds/github/dtos/ReleaseDto.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.github.dtos
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class ReleaseDto(
8 | @SerialName("tag_name") val tagName: String,
9 | @SerialName("name") val name: String,
10 | @SerialName("body") val body: String,
11 | @SerialName("created_at") val createdAt: String,
12 | @SerialName("assets") val assets: List
13 | )
--------------------------------------------------------------------------------
/app/src/gitHub/res/xml/pref_general_updates.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
--------------------------------------------------------------------------------
/app/src/gitHub/res/xml/provider_paths.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
9 |
--------------------------------------------------------------------------------
/app/src/gitHubNightly/java/me/magnum/melonds/di/GitHubNightlyModule.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.di
2 |
3 | import android.content.Context
4 | import dagger.Module
5 | import dagger.Provides
6 | import dagger.hilt.InstallIn
7 | import dagger.hilt.android.qualifiers.ApplicationContext
8 | import dagger.hilt.components.SingletonComponent
9 | import me.magnum.melonds.domain.repositories.UpdatesRepository
10 | import me.magnum.melonds.github.GitHubApi
11 | import me.magnum.melonds.github.repositories.GitHubNightlyUpdatesRepository
12 | import javax.inject.Singleton
13 |
14 | @Module
15 | @InstallIn(SingletonComponent::class)
16 | object GitHubNightlyModule {
17 |
18 | @Provides
19 | @Singleton
20 | fun provideUpdatesRepository(@ApplicationContext context: Context, gitHubApi: GitHubApi): UpdatesRepository {
21 | val gitHubPreferences = context.getSharedPreferences("preferences-github", Context.MODE_PRIVATE)
22 | return GitHubNightlyUpdatesRepository(gitHubApi, gitHubPreferences)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/gitHubProd/java/me/magnum/melonds/di/GitHubProdModule.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.di
2 |
3 | import android.content.Context
4 | import dagger.Module
5 | import dagger.Provides
6 | import dagger.hilt.InstallIn
7 | import dagger.hilt.android.qualifiers.ApplicationContext
8 | import dagger.hilt.components.SingletonComponent
9 | import me.magnum.melonds.domain.repositories.UpdatesRepository
10 | import me.magnum.melonds.github.GitHubApi
11 | import me.magnum.melonds.github.repositories.GitHubProdUpdatesRepository
12 | import javax.inject.Singleton
13 |
14 | @Module
15 | @InstallIn(SingletonComponent::class)
16 | object GitHubProdModule {
17 |
18 | @Provides
19 | @Singleton
20 | fun provideUpdatesRepository(@ApplicationContext context: Context, gitHubApi: GitHubApi): UpdatesRepository {
21 | val gitHubPreferences = context.getSharedPreferences("preferences-github", Context.MODE_PRIVATE)
22 | return GitHubProdUpdatesRepository(context, gitHubApi, gitHubPreferences)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/cpp/AndroidFrameRenderedCallback.cpp:
--------------------------------------------------------------------------------
1 | #include "AndroidFrameRenderedCallback.h"
2 |
3 | AndroidFrameRenderedCallback::AndroidFrameRenderedCallback(JniEnvHandler* jniEnvHandler, jobject androidFrameRenderedListener)
4 | {
5 | this->jniEnvHandler = jniEnvHandler;
6 | this->androidFrameRenderedListener = androidFrameRenderedListener;
7 | }
8 |
9 | void AndroidFrameRenderedCallback::onFrameRendered(int textureId)
10 | {
11 | JNIEnv* env = this->jniEnvHandler->getCurrentThreadEnv();
12 |
13 | jclass listenerClass = env->GetObjectClass(this->androidFrameRenderedListener);
14 | jmethodID onFrameRenderedMethod = env->GetMethodID(listenerClass, "onFrameRendered", "(I)V");
15 | env->CallVoidMethod(this->androidFrameRenderedListener, onFrameRenderedMethod, textureId);
16 | }
--------------------------------------------------------------------------------
/app/src/main/cpp/AndroidFrameRenderedCallback.h:
--------------------------------------------------------------------------------
1 | #ifndef ANDROIDFRAMERENDEREDCALLBACK_H
2 | #define ANDROIDFRAMERENDEREDCALLBACK_H
3 |
4 | #include "JniEnvHandler.h"
5 | #include
6 | #include
7 |
8 | class AndroidFrameRenderedCallback : public FrameRenderedCallback
9 | {
10 | private:
11 | JniEnvHandler* jniEnvHandler;
12 | jobject androidFrameRenderedListener;
13 |
14 | public:
15 | AndroidFrameRenderedCallback(JniEnvHandler* jniEnvHandler, jobject androidFrameRenderedListener);
16 | void onFrameRendered(int textureId);
17 | };
18 |
19 |
20 | #endif //ANDROIDFRAMERENDEREDCALLBACK_H
21 |
--------------------------------------------------------------------------------
/app/src/main/cpp/AndroidRACallback.h:
--------------------------------------------------------------------------------
1 | #ifndef ANDROIDRACALLBACK_H
2 | #define ANDROIDRACALLBACK_H
3 |
4 | #include "JniEnvHandler.h"
5 | #include
6 | #include
7 |
8 | class AndroidRACallback : public RetroAchievements::RACallback
9 | {
10 | private:
11 | JniEnvHandler* jniEnvHandler;
12 | jobject callback;
13 |
14 | public:
15 | AndroidRACallback(JniEnvHandler* jniEnvHandler, jobject callback);
16 | void onAchievementPrimed(long achievementId);
17 | void onAchievementTriggered(long achievementId);
18 | void onAchievementUnprimed(long achievementId);
19 | };
20 |
21 | #endif //ANDROIDRACALLBACK_H
22 |
--------------------------------------------------------------------------------
/app/src/main/cpp/JniEnvHandler.h:
--------------------------------------------------------------------------------
1 | #ifndef MELONDS_ANDROID_JNIENVHANDLER_H
2 | #define MELONDS_ANDROID_JNIENVHANDLER_H
3 |
4 | #include
5 |
6 | class JniEnvHandler {
7 | private:
8 | JavaVM* vm;
9 |
10 | public:
11 | JniEnvHandler(JavaVM* vm);
12 | JNIEnv* getCurrentThreadEnv();
13 | };
14 |
15 |
16 | #endif //MELONDS_ANDROID_JNIENVHANDLER_H
17 |
--------------------------------------------------------------------------------
/app/src/main/cpp/MelonDSAndroidCameraHandler.h:
--------------------------------------------------------------------------------
1 | #ifndef MELONDSANDROIDCAMERAHANDLER_H
2 | #define MELONDSANDROIDCAMERAHANDLER_H
3 |
4 | #include
5 | #include
6 | #include "JniEnvHandler.h"
7 |
8 | class MelonDSAndroidCameraHandler : public MelonDSAndroid::AndroidCameraHandler {
9 | private:
10 | const int BUFFER_SIZE = 640 * 480 * 2;
11 |
12 | JniEnvHandler* jniEnvHandler;
13 | jobject cameraManager;
14 |
15 | public:
16 | MelonDSAndroidCameraHandler(JniEnvHandler* jniEnvHandler, jobject cameraManager);
17 | void startCamera(int camera);
18 | void stopCamera(int camera);
19 | void captureFrame(int camera, u32* frameBuffer, int width, int height, bool isYuv);
20 | virtual ~MelonDSAndroidCameraHandler();
21 | };
22 |
23 | #endif //MELONDSANDROIDCAMERAHANDLER_H
24 |
--------------------------------------------------------------------------------
/app/src/main/cpp/MelonDSAndroidConfiguration.h:
--------------------------------------------------------------------------------
1 | #ifndef MELONDSANDROIDCONFIGURATION_H
2 | #define MELONDSANDROIDCONFIGURATION_H
3 |
4 | #include "MelonDS.h"
5 |
6 | namespace MelonDSAndroidConfiguration {
7 | MelonDSAndroid::EmulatorConfiguration buildEmulatorConfiguration(JNIEnv* env, jobject emulatorConfiguration);
8 | MelonDSAndroid::FirmwareConfiguration buildFirmwareConfiguration(JNIEnv* env, jobject firmwareConfiguration);
9 | GPU::RenderSettings buildRenderSettings(JNIEnv* env, jobject renderSettings);
10 | }
11 |
12 | #endif //MELONDSANDROIDCONFIGURATION_H
13 |
--------------------------------------------------------------------------------
/app/src/main/cpp/MelonDSAndroidInterface.h:
--------------------------------------------------------------------------------
1 | #ifndef MELONDSANDROIDINTERFACE_H
2 | #define MELONDSANDROIDINTERFACE_H
3 |
4 | #include "JniEnvHandler.h"
5 |
6 | extern JniEnvHandler* jniEnvHandler;
7 |
8 | #endif //MELONDSANDROIDINTERFACE_H
9 |
--------------------------------------------------------------------------------
/app/src/main/cpp/RAAchievementMapper.h:
--------------------------------------------------------------------------------
1 | #ifndef RAACHIEVEMENTMAPPER_H
2 | #define RAACHIEVEMENTMAPPER_H
3 |
4 | #include
5 | #include
6 | #include "retroachievements/RAAchievement.h"
7 |
8 | void mapAchievementsFromJava(JNIEnv *env, jobjectArray javaAchievements, std::list &outputList);
9 |
10 | #endif //RAACHIEVEMENTMAPPER_H
11 |
--------------------------------------------------------------------------------
/app/src/main/cpp/UriFileHandler.cpp:
--------------------------------------------------------------------------------
1 | #include "UriFileHandler.h"
2 |
3 | UriFileHandler::UriFileHandler(JniEnvHandler* jniEnvHandler, jobject uriFileHandler)
4 | {
5 | this->jniEnvHandler = jniEnvHandler;
6 | this->uriFileHandler = uriFileHandler;
7 | }
8 |
9 | FILE* UriFileHandler::open(const char* path, const char* mode)
10 | {
11 | JNIEnv* env = this->jniEnvHandler->getCurrentThreadEnv();
12 |
13 | jstring pathString = env->NewStringUTF(path);
14 | jstring modeString = env->NewStringUTF(mode);
15 | jclass handlerClass = env->GetObjectClass(this->uriFileHandler);
16 | jmethodID openMethod = env->GetMethodID(handlerClass, "open", "(Ljava/lang/String;Ljava/lang/String;)I");
17 | jint fileDescriptor = env->CallIntMethod(this->uriFileHandler, openMethod, pathString, modeString);
18 |
19 | if (fileDescriptor == -1) {
20 | return nullptr;
21 | } else {
22 | return fdopen(fileDescriptor, mode);
23 | }
24 | }
25 |
26 | UriFileHandler::~UriFileHandler()
27 | {
28 | }
--------------------------------------------------------------------------------
/app/src/main/cpp/UriFileHandler.h:
--------------------------------------------------------------------------------
1 | #ifndef MELONDS_ANDROID_URIFILEHANDLER_H
2 | #define MELONDS_ANDROID_URIFILEHANDLER_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include "JniEnvHandler.h"
8 |
9 | class UriFileHandler : public MelonDSAndroid::AndroidFileHandler {
10 | private:
11 | JniEnvHandler* jniEnvHandler;
12 | jobject uriFileHandler;
13 |
14 | public:
15 | UriFileHandler(JniEnvHandler* jniEnvHandler, jobject uriFileHandler);
16 | FILE* open(const char* path, const char* mode);
17 | virtual ~UriFileHandler();
18 | };
19 |
20 | #endif //MELONDS_ANDROID_URIFILEHANDLER_H
21 |
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/MelonDSAndroidInterface.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds
2 |
3 | import me.magnum.melonds.common.UriFileHandler
4 |
5 | object MelonDSAndroidInterface {
6 | external fun setup(uriFileHandler: UriFileHandler)
7 | external fun cleanup()
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/MelonDSiNand.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds
2 |
3 | import me.magnum.melonds.domain.model.DSiWareTitle
4 | import me.magnum.melonds.domain.model.EmulatorConfiguration
5 |
6 | object MelonDSiNand {
7 | external fun openNand(emulatorConfiguration: EmulatorConfiguration): Int
8 | external fun listTitles(): ArrayList
9 | external fun importTitle(titleUri: String, tmdMetadata: ByteArray): Int
10 | external fun deleteTitle(titleId: Int)
11 | external fun importTitleFile(titleId: Int, fileType: Int, fileUri: String): Boolean
12 | external fun exportTitleFile(titleId: Int, fileType: Int, fileUri: String): Boolean
13 | external fun closeNand()
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/common/Deletable.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.common
2 |
3 | /**
4 | * Represents an entity that can be deleted but may be kept in memory. This can be useful, for example, to support soft-deleting of entities.
5 | */
6 | data class Deletable(val data: T, val isDeleted: Boolean)
7 |
8 | fun List>.filterNotDeleted(): List {
9 | return mapNotNull {
10 | if (it.isDeleted) {
11 | null
12 | } else {
13 | it.data
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/common/Permission.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.common
2 |
3 | import android.content.Intent
4 |
5 | enum class Permission {
6 | READ,
7 | WRITE,
8 | READ_WRITE;
9 |
10 | fun toFlags(): Int {
11 | return when (this) {
12 | READ -> Intent.FLAG_GRANT_READ_URI_PERMISSION
13 | WRITE -> Intent.FLAG_GRANT_WRITE_URI_PERMISSION
14 | READ_WRITE -> Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/common/RetroAchievementsCallback.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.common
2 |
3 | interface RetroAchievementsCallback {
4 | fun onAchievementPrimed(achievementId: Long)
5 | fun onAchievementTriggered(achievementId: Long)
6 | fun onAchievementUnprimed(achievementId: Long)
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/common/UriPermissionManager.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.common
2 |
3 | import android.content.Context
4 | import android.net.Uri
5 |
6 | class UriPermissionManager(private val context: Context) {
7 |
8 | fun persistDirectoryPermissions(directoryUri: Uri, permission: Permission) {
9 | val flags = permission.toFlags()
10 | context.contentResolver.takePersistableUriPermission(directoryUri, flags)
11 | }
12 |
13 | fun persistFilePermissions(fileUri: Uri, permission: Permission) {
14 | val flags = permission.toFlags()
15 | context.contentResolver.takePersistableUriPermission(fileUri, flags)
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/common/camera/BlackDSiCameraSource.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.common.camera
2 |
3 | class BlackDSiCameraSource : DSiCameraSource {
4 |
5 | override fun startCamera(camera: CameraType) {
6 | }
7 |
8 | override fun stopCamera(camera: CameraType) {
9 | }
10 |
11 | override fun captureFrame(camera: CameraType, buffer: ByteArray, width: Int, height: Int, isYuv: Boolean) {
12 | for (i in buffer.indices step 2) {
13 | // Use 0 for luminance (Y) and 127 for color (U and V)
14 | buffer[i] = 0
15 | buffer[i + 1] = 127
16 | }
17 | }
18 |
19 | override fun dispose() {
20 | }
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/common/camera/DSiCameraSource.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.common.camera
2 |
3 | typealias CameraType = Int
4 |
5 | interface DSiCameraSource {
6 |
7 | companion object {
8 | const val BackCamera: CameraType = 0
9 | const val FrontCamera: CameraType = 1
10 | }
11 |
12 | fun startCamera(camera: CameraType)
13 | fun stopCamera(camera: CameraType)
14 | fun captureFrame(camera: CameraType, buffer: ByteArray, width: Int, height: Int, isYuv: Boolean)
15 | fun dispose()
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/common/cheats/CheatDatabaseParser.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.common.cheats
2 |
3 | interface CheatDatabaseParser {
4 | fun parseCheatDatabase(databaseStream: ProgressTrackerInputStream, parseListener: CheatDatabaseParserListener)
5 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/common/cheats/CheatDatabaseParserListener.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.common.cheats
2 |
3 | import me.magnum.melonds.domain.model.CheatDatabase
4 | import me.magnum.melonds.domain.model.Game
5 |
6 | interface CheatDatabaseParserListener {
7 | fun onDatabaseParseStart(databaseName: String): CheatDatabase
8 | fun onGameParseStart(gameName: String)
9 | fun onGameParsed(game: Game)
10 | fun onParseComplete()
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/common/cheats/ProgressTrackerInputStream.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.common.cheats
2 |
3 | import java.io.FilterInputStream
4 | import java.io.InputStream
5 |
6 | class ProgressTrackerInputStream(inputStream: InputStream?) : FilterInputStream(inputStream) {
7 | var totalReadBytes = 0
8 | private set
9 |
10 | override fun read(b: ByteArray?, off: Int, len: Int): Int {
11 | val readBytes = super.read(b, off, len)
12 | totalReadBytes += readBytes
13 |
14 | return readBytes
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/common/contracts/CreateFileContract.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.common.contracts
2 |
3 | import android.app.Activity
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.net.Uri
7 | import androidx.activity.result.contract.ActivityResultContract
8 |
9 | class CreateFileContract : ActivityResultContract() {
10 | override fun createIntent(context: Context, input: String): Intent {
11 | return Intent(Intent.ACTION_CREATE_DOCUMENT)
12 | .putExtra(Intent.EXTRA_TITLE, input)
13 | .addCategory(Intent.CATEGORY_OPENABLE)
14 | .setType("application/octet-stream")
15 | }
16 |
17 | override fun parseResult(resultCode: Int, intent: Intent?): Uri? {
18 | return if (intent == null || resultCode != Activity.RESULT_OK) {
19 | null
20 | } else {
21 | intent.data
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/common/retroachievements/AndroidRAAchievementSignatureProvider.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.common.retroachievements
2 |
3 | import me.magnum.rcheevosapi.RAAchievementSignatureProvider
4 | import me.magnum.rcheevosapi.model.RAUserAuth
5 | import java.math.BigInteger
6 | import java.security.MessageDigest
7 |
8 | class AndroidRAAchievementSignatureProvider : RAAchievementSignatureProvider {
9 |
10 | override fun provideAchievementSignature(achievementId: Long, userAuth: RAUserAuth, forHardcoreMode: Boolean): String {
11 | val md5Digest = MessageDigest.getInstance("MD5")
12 | md5Digest.update(achievementId.toString().toByteArray())
13 | md5Digest.update(userAuth.username.toByteArray())
14 | md5Digest.update((if (forHardcoreMode) "1" else "0").toByteArray())
15 |
16 | return BigInteger(1, md5Digest.digest()).toString(16).padStart(32, '0')
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/common/romprocessors/RomFileProcessor.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.common.romprocessors
2 |
3 | import android.graphics.Bitmap
4 | import android.net.Uri
5 | import io.reactivex.Single
6 | import me.magnum.melonds.domain.model.rom.Rom
7 | import me.magnum.melonds.domain.model.RomInfo
8 |
9 | interface RomFileProcessor {
10 | fun getRomFromUri(romUri: Uri, parentUri: Uri): Rom?
11 | fun getRomIcon(rom: Rom): Bitmap?
12 | fun getRomInfo(rom: Rom): RomInfo?
13 | fun getRealRomUri(rom: Rom): Single
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/common/romprocessors/RomFileProcessorFactory.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.common.romprocessors
2 |
3 | import android.net.Uri
4 | import androidx.documentfile.provider.DocumentFile
5 |
6 | interface RomFileProcessorFactory {
7 | fun getFileRomProcessorForDocument(romDocument: DocumentFile): RomFileProcessor?
8 | fun getFileRomProcessorForDocument(romUri: Uri): RomFileProcessor?
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/common/uridelegates/ContentUriHandler.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.common.uridelegates
2 |
3 | import android.content.Context
4 | import android.net.Uri
5 | import androidx.documentfile.provider.DocumentFile
6 |
7 | class ContentUriHandler(private val context: Context) : UriHandler {
8 | override fun fileExists(uri: Uri): Boolean {
9 | return getUriDocument(uri)?.exists() == true
10 | }
11 |
12 | override fun createFileDocument(uri: Uri): DocumentFile? {
13 | // We can't reliably create a document from a target URI. We need the parent tree URI and document name at least.
14 | return null
15 | }
16 |
17 | override fun getUriDocument(uri: Uri): DocumentFile? {
18 | return DocumentFile.fromSingleUri(context, uri)
19 | }
20 |
21 | override fun getUriTreeDocument(uri: Uri): DocumentFile? {
22 | return DocumentFile.fromTreeUri(context, uri)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/common/uridelegates/UriHandler.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.common.uridelegates
2 |
3 | import android.net.Uri
4 | import androidx.documentfile.provider.DocumentFile
5 |
6 | interface UriHandler {
7 | fun fileExists(uri: Uri): Boolean
8 | fun createFileDocument(uri: Uri): DocumentFile?
9 | fun getUriDocument(uri: Uri): DocumentFile?
10 | fun getUriTreeDocument(uri: Uri): DocumentFile?
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/common/vibration/Api26VibratorDelegate.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.common.vibration
2 |
3 | import android.os.Build
4 | import android.os.VibrationEffect
5 | import android.os.Vibrator
6 | import androidx.annotation.RequiresApi
7 |
8 | @RequiresApi(Build.VERSION_CODES.O)
9 | class Api26VibratorDelegate(private val vibrator: Vibrator) : TouchVibrator.VibratorDelegate {
10 | override fun supportsVibration(): Boolean {
11 | return vibrator.hasVibrator()
12 | }
13 |
14 | override fun supportsVibrationAmplitude(): Boolean {
15 | return vibrator.hasAmplitudeControl()
16 | }
17 |
18 | override fun vibrate(duration: Int, amplitude: Int) {
19 | val effect = VibrationEffect.createOneShot(duration.toLong(), amplitude)
20 | vibrator.vibrate(effect)
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/common/vibration/OldVibratorDelegate.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.common.vibration
2 |
3 | import android.os.Vibrator
4 |
5 | @Suppress("DEPRECATION")
6 | class OldVibratorDelegate(private val vibrator: Vibrator) : TouchVibrator.VibratorDelegate {
7 | override fun supportsVibration(): Boolean {
8 | return vibrator.hasVibrator()
9 | }
10 |
11 | override fun supportsVibrationAmplitude(): Boolean {
12 | return false
13 | }
14 |
15 | override fun vibrate(duration: Int, amplitude: Int) {
16 | vibrator.vibrate(duration.toLong())
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/database/callback/CustomCheatCreationCallback.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.database.callback
2 |
3 | import android.content.ContentValues
4 | import android.database.sqlite.SQLiteDatabase
5 | import androidx.room.RoomDatabase
6 | import androidx.sqlite.db.SupportSQLiteDatabase
7 | import me.magnum.melonds.database.entities.CheatDatabaseEntity
8 |
9 | class CustomCheatCreationCallback : RoomDatabase.Callback() {
10 |
11 | override fun onCreate(db: SupportSQLiteDatabase) {
12 | val contentValues = ContentValues().apply {
13 | put("id", CheatDatabaseEntity.CUSTOM_CHEATS_DATABASE_ID)
14 | put("name", CheatDatabaseEntity.CUSTOM_CHEATS_DATABASE_NAME)
15 | }
16 |
17 | db.insert("cheat_database", SQLiteDatabase.CONFLICT_REPLACE, contentValues)
18 | }
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/database/converters/InstantConverter.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.database.converters
2 |
3 | import androidx.room.TypeConverter
4 | import java.time.Instant
5 |
6 | class InstantConverter {
7 |
8 | @TypeConverter
9 | fun instantToTimestamp(instant: Instant?): Long? {
10 | return instant?.toEpochMilli()
11 | }
12 |
13 | @TypeConverter
14 | fun timestampToInstant(timestamp: Long?): Instant? {
15 | return timestamp?.let {
16 | Instant.ofEpochMilli(it)
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/database/daos/CheatDatabaseDao.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.database.daos
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Insert
5 | import androidx.room.Query
6 | import me.magnum.melonds.database.entities.CheatDatabaseEntity
7 |
8 | @Dao
9 | interface CheatDatabaseDao {
10 | @Insert
11 | suspend fun insertCheatDatabase(database: CheatDatabaseEntity): Long
12 |
13 | @Query("DELETE FROM cheat_database WHERE name = :databaseName")
14 | suspend fun deleteCheatDatabase(databaseName: String)
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/database/daos/CheatFolderDao.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.database.daos
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Insert
5 | import androidx.room.Query
6 | import me.magnum.melonds.database.entities.CheatFolderEntity
7 |
8 | @Dao
9 | interface CheatFolderDao {
10 | @Insert
11 | suspend fun insertCheatFolder(cheatFolder: CheatFolderEntity): Long
12 |
13 | @Insert
14 | suspend fun insertCheatFolders(cheatFolders: List): List
15 |
16 | @Query("DELETE FROM cheat_folder WHERE id NOT IN (SELECT DISTINCT cheat_folder_id FROM cheat)")
17 | suspend fun deleteEmptyFolders()
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/database/entities/CheatDatabaseEntity.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.database.entities
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.Index
6 | import androidx.room.PrimaryKey
7 |
8 | @Entity(
9 | tableName = "cheat_database",
10 | indices = [
11 | Index(value = ["name"], unique = true)
12 | ]
13 | )
14 | data class CheatDatabaseEntity(
15 | @ColumnInfo(name = "id") @PrimaryKey(autoGenerate = true) val id: Long?,
16 | @ColumnInfo(name = "name") val name: String,
17 | ) {
18 |
19 | companion object {
20 | // The ID of the database that holds cheats manually created by the user. Its 0 because SQLite starts autoincrement keys from 1
21 | const val CUSTOM_CHEATS_DATABASE_ID = 0L
22 | const val CUSTOM_CHEATS_DATABASE_NAME = "__custom_cheat_database"
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/database/entities/CheatFolderEntity.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.database.entities
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.ForeignKey
6 | import androidx.room.PrimaryKey
7 |
8 | @Entity(
9 | tableName = "cheat_folder",
10 | foreignKeys = [
11 | ForeignKey(
12 | entity = GameEntity::class,
13 | parentColumns = ["id"],
14 | childColumns = ["game_id"],
15 | onDelete = ForeignKey.CASCADE
16 | )
17 | ]
18 | )
19 | data class CheatFolderEntity(
20 | @PrimaryKey(autoGenerate = true) val id: Long?,
21 | @ColumnInfo(name = "game_id", index = true) val gameId: Long,
22 | @ColumnInfo(name = "name") val name: String
23 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/database/entities/CheatFolderWithCheats.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.database.entities
2 |
3 | import androidx.room.Embedded
4 | import androidx.room.Relation
5 |
6 | class CheatFolderWithCheats {
7 | @Embedded
8 | lateinit var cheatFolder: CheatFolderEntity
9 |
10 | @Relation(
11 | parentColumn = "id",
12 | entityColumn = "cheat_folder_id"
13 | )
14 | lateinit var cheats: List
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/database/entities/CheatStatusUpdate.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.database.entities
2 |
3 | data class CheatStatusUpdate(
4 | val id: Long,
5 | val enabled: Boolean
6 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/database/entities/GameEntity.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.database.entities
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.Index
6 | import androidx.room.PrimaryKey
7 |
8 | @Entity(
9 | tableName = "game",
10 | indices = [
11 | Index(
12 | value = ["game_code", "game_checksum"],
13 | name = "game_code_checksum_index",
14 | unique = true,
15 | ),
16 | ]
17 | )
18 | data class GameEntity(
19 | @PrimaryKey(autoGenerate = true) val id: Long?,
20 | @ColumnInfo(name = "name") val name: String,
21 | @ColumnInfo(name = "game_code") val gameCode: String,
22 | @ColumnInfo(name = "game_checksum") val gameChecksum: String,
23 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/database/entities/GameWithCheatCategories.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.database.entities
2 |
3 | import androidx.room.Embedded
4 | import androidx.room.Relation
5 |
6 | class GameWithCheatCategories {
7 | @Embedded
8 | lateinit var game: GameEntity
9 |
10 | @Relation(
11 | entity = CheatFolderEntity::class,
12 | parentColumn = "id",
13 | entityColumn = "game_id"
14 | )
15 | lateinit var cheatFolders: List
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/database/entities/retroachievements/RAGameEntity.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.database.entities.retroachievements
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 |
7 | @Entity(tableName = "ra_game")
8 | data class RAGameEntity(
9 | @PrimaryKey @ColumnInfo(name = "game_id") val gameId: Long,
10 | @ColumnInfo(name = "rich_presence_patch") val richPresencePatch: String?,
11 | @ColumnInfo(name = "icon") val icon: String,
12 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/database/entities/retroachievements/RAGameHashEntity.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.database.entities.retroachievements
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 |
7 | @Entity(tableName = "ra_game_hash_library")
8 | data class RAGameHashEntity(
9 | @PrimaryKey @ColumnInfo(name = "game_hash") val gameHash: String,
10 | @ColumnInfo(name = "game_id") val gameId: Long,
11 | )
12 |
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/database/entities/retroachievements/RAGameSetMetadata.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.database.entities.retroachievements
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 | import java.time.Instant
7 |
8 | @Entity(tableName = "ra_game_set_metadata")
9 | data class RAGameSetMetadata(
10 | @PrimaryKey @ColumnInfo(name = "game_id") val gameId: Long,
11 | @ColumnInfo(name = "last_achievement_set_updated") val lastAchievementSetUpdated: Instant?,
12 | @ColumnInfo(name = "last_user_data_updated") val lastSoftcoreUserDataUpdated: Instant?,
13 | @ColumnInfo(name = "last_hardcore_user_data_updated") val lastHardcoreUserDataUpdated: Instant?,
14 | )
15 |
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/database/entities/retroachievements/RAPendingAchievementSubmissionEntity.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.database.entities.retroachievements
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 |
6 | @Entity(
7 | tableName = "ra_pending_achievement_award",
8 | primaryKeys = ["achievement_id", "for_hardcore_mode"],
9 | )
10 | data class RAPendingAchievementSubmissionEntity(
11 | @ColumnInfo(name = "achievement_id") val achievementId: Long,
12 | @ColumnInfo(name = "game_id") val gameId: Long,
13 | @ColumnInfo(name = "for_hardcore_mode") val forHardcoreMode: Boolean,
14 | )
15 |
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/database/entities/retroachievements/RAUserAchievementEntity.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.database.entities.retroachievements
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 |
6 | @Entity(
7 | tableName = "ra_user_achievement",
8 | primaryKeys = ["game_id", "achievement_id", "is_hardcore"],
9 | )
10 | data class RAUserAchievementEntity(
11 | @ColumnInfo(name = "game_id") val gameId: Long,
12 | @ColumnInfo(name = "achievement_id") val achievementId: Long,
13 | @ColumnInfo(name = "is_unlocked") val isUnlocked: Boolean,
14 | @ColumnInfo(name = "is_hardcore") val isHardcore: Boolean,
15 | )
16 |
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/database/migrations/Migration1to2.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.database.migrations
2 |
3 | import androidx.room.migration.Migration
4 | import androidx.sqlite.db.SupportSQLiteDatabase
5 |
6 | class Migration1to2 : Migration(1, 2) {
7 | override fun migrate(db: SupportSQLiteDatabase) {
8 | db.execSQL("ALTER TABLE game ADD COLUMN game_checksum TEXT")
9 | db.execSQL("CREATE INDEX index_game_game_checksum ON game(game_checksum)")
10 | }
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/di/entrypoint/InitializerEntryPoint.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.di.entrypoint
2 |
3 | import android.content.Context
4 | import dagger.hilt.EntryPoint
5 | import dagger.hilt.InstallIn
6 | import dagger.hilt.android.EntryPointAccessors
7 | import dagger.hilt.components.SingletonComponent
8 | import me.magnum.melonds.initializer.CoilInitializer
9 |
10 | @EntryPoint
11 | @InstallIn(SingletonComponent::class)
12 | interface InitializerEntryPoint {
13 |
14 | fun inject(coilInitializer: CoilInitializer)
15 |
16 | companion object {
17 | fun resolve(context: Context): InitializerEntryPoint {
18 | val applicationContext = context.applicationContext ?: throw IllegalStateException()
19 | return EntryPointAccessors.fromApplication(
20 | context = applicationContext,
21 | entryPoint = InitializerEntryPoint::class.java
22 | )
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/AudioBitrate.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | enum class AudioBitrate(val bitrateValue: Int) {
4 | AUTO(0),
5 | BIT10(1),
6 | BIT16(2)
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/AudioInterpolation.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | enum class AudioInterpolation(val interpolationValue: Int) {
4 | NONE(0),
5 | LINEAR(1),
6 | COSINE(2),
7 | CUBIC(3)
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/AudioLatency.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | enum class AudioLatency(val latencyValue: Int) {
4 | LOW(0),
5 | MEDIUM(1),
6 | HIGH(2)
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/Background.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | import android.net.Uri
4 | import java.util.UUID
5 |
6 | data class Background(val id: UUID?, val name: String, val uri: Uri)
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/Cheat.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | data class Cheat(
4 | val id: Long?,
5 | val cheatDatabaseId: Long,
6 | val name: String,
7 | val description: String?,
8 | val code: String,
9 | val enabled: Boolean,
10 | ) {
11 |
12 | fun isValid(): Boolean {
13 | // A cheat code can only have 128 parts (512 bytes). Since each part has 8 characters, we can add 1 to the length (to ensure that each part has a matching space
14 | // separator) and divide by 9 (part length + space separator) to calculate the total number of parts in the cheat
15 | return ((code.length + 1) / 9) <= 128
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/CheatDatabase.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | data class CheatDatabase(
4 | val id: Long?,
5 | val name: String,
6 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/CheatFolder.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | data class CheatFolder(val id: Long?, val name: String, val cheats: List)
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/CheatImportProgress.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | data class CheatImportProgress(val status: CheatImportStatus, val progress: Float, val ongoingItemName: String?) {
4 | enum class CheatImportStatus {
5 | NOT_IMPORTING,
6 | STARTING,
7 | ONGOING,
8 | FINISHED,
9 | FAILED
10 | }
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/CheatInFolder.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | data class CheatInFolder(val cheat: Cheat, val folderName: String)
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/ConfigurationDirResult.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | data class ConfigurationDirResult(
4 | val consoleType: ConsoleType,
5 | val status: Status,
6 | val requiredFiles: Array,
7 | val fileResults: Array>
8 | ) {
9 | enum class Status {
10 | UNSET, INVALID, VALID
11 | }
12 |
13 | enum class FileStatus {
14 | PRESENT, MISSING, INVALID
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/ConsoleType.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | enum class ConsoleType(val consoleType: Int) {
4 | DS(0),
5 | DSi(1)
6 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/DSiWareTitle.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | class DSiWareTitle(
4 | val name: String,
5 | val producer: String,
6 | val titleId: Long,
7 | val icon: ByteArray,
8 | val publicSavSize: Long,
9 | val privateSavSize: Long,
10 | val appFlags: Int,
11 | ) {
12 |
13 | fun hasPublicSavFile() = publicSavSize != 0L
14 |
15 | fun hasPrivateSavFile() = privateSavSize != 0L
16 |
17 | fun hasBannerSavFile() = (appFlags and (0x04)) != 0
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/DownloadProgress.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | sealed class DownloadProgress {
4 | class DownloadUpdate(val totalSize: Long, val downloadedBytes: Long) : DownloadProgress()
5 | object DownloadComplete : DownloadProgress()
6 | object DownloadFailed : DownloadProgress()
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/FirmwareColour.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | enum class FirmwareColour {
4 | GRAY,
5 | BROWN,
6 | RED,
7 | PINK,
8 | ORANGE,
9 | YELLOW,
10 | LIME,
11 | GREEN,
12 | DARK_GREEN,
13 | TURQUOISE,
14 | LIGHT_BLUE,
15 | BLUE,
16 | DARK_BLUE,
17 | PURPLE,
18 | VIOLET,
19 | FUCHSIA
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/FirmwareConfiguration.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | data class FirmwareConfiguration(
4 | val nickname: String,
5 | val message: String,
6 | val language: Int,
7 | val favouriteColour: Int,
8 | val birthdayMonth: Int,
9 | val birthdayDay: Int,
10 | val randomizeMacAddress: Boolean,
11 | val internalMacAddress: String?
12 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/FpsCounterPosition.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | enum class FpsCounterPosition {
4 | HIDDEN,
5 | TOP_LEFT,
6 | TOP_CENTER,
7 | TOP_RIGHT,
8 | BOTTOM_LEFT,
9 | BOTTOM_CENTER,
10 | BOTTOM_RIGHT
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/Game.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | data class Game(val id: Long?, val name: String, val gameCode: String, val gameChecksum: String, val cheats: List)
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/InputConfig.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | data class InputConfig(val input: Input, val assignment: Assignment = Assignment.None) {
4 |
5 | sealed class Assignment(open val deviceId: Int?) {
6 | data object None : Assignment(null)
7 | data class Key(override val deviceId: Int?, val keyCode: Int) : Assignment(deviceId)
8 | data class Axis(override val deviceId: Int?, val axisCode: Int, val direction: Direction) : Assignment(deviceId) {
9 | enum class Direction {
10 | POSITIVE,
11 | NEGATIVE,
12 | }
13 | }
14 | }
15 |
16 | fun hasKeyAssigned(): Boolean {
17 | return assignment != Assignment.None
18 | }
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/MicSource.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | enum class MicSource(val sourceValue: Int) {
4 | NONE(0),
5 | BLOW(1),
6 | DEVICE(2)
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/Point.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | data class Point(var x: Int, var y: Int) {
4 | constructor() : this(0, 0)
5 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/Rect.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | data class Rect(val x: Int, val y: Int, val width: Int, val height: Int) {
4 |
5 | val bottom get() = y + height
6 |
7 | val right get() = x + width
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/RendererConfiguration.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | data class RendererConfiguration(
4 | val renderer: VideoRenderer,
5 | val videoFiltering: VideoFiltering,
6 | val threadedRendering: Boolean,
7 | private val internalResolutionScaling: Int,
8 | ) {
9 |
10 | val resolutionScaling get() = when (renderer) {
11 | VideoRenderer.SOFTWARE -> 1
12 | VideoRenderer.OPENGL -> internalResolutionScaling
13 | }
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/RomIconFiltering.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | enum class RomIconFiltering {
4 | NONE,
5 | LINEAR
6 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/RomInfo.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | /**
4 | * @param gameTitle The short ROM title as present in the ROM header
5 | * @param gameName The ROM name as displayed in the system
6 | */
7 | data class RomInfo(
8 | val gameCode: String,
9 | val headerChecksum: UInt,
10 | val gameTitle: String,
11 | val gameName: String,
12 | ) {
13 | fun headerChecksumString(): String {
14 | return headerChecksum.toString(16).padStart(8, '0').uppercase()
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/RomMetadata.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | data class RomMetadata(
4 | val romTitle: String,
5 | val developerName: String,
6 | val isDSiWareTitle: Boolean,
7 | val retroAchievementsHash: String,
8 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/RomScanningStatus.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | enum class RomScanningStatus {
4 | SCANNING,
5 | NOT_SCANNING
6 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/RuntimeBackground.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | import me.magnum.melonds.domain.model.layout.BackgroundMode
4 |
5 | data class RuntimeBackground(val background: Background?, val mode: BackgroundMode) {
6 |
7 | companion object {
8 | val None = RuntimeBackground(null, BackgroundMode.STRETCH)
9 | }
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/SaveStateLocation.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | enum class SaveStateLocation {
4 | SAVE_DIR,
5 | ROM_DIR,
6 | INTERNAL_DIR
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/SaveStateSlot.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | import android.net.Uri
4 | import java.util.*
5 |
6 | data class SaveStateSlot(val slot: Int, val exists: Boolean, val lastUsedDate: Date?, val screenshot: Uri?) {
7 |
8 | companion object {
9 | const val QUICK_SAVE_SLOT = 0
10 | }
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/SortingMode.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | enum class SortingMode(val defaultOrder: SortingOrder) {
4 | ALPHABETICALLY(SortingOrder.ASCENDING),
5 | RECENTLY_PLAYED(SortingOrder.DESCENDING)
6 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/SortingOrder.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | enum class SortingOrder {
4 | ASCENDING,
5 | DESCENDING
6 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/VideoFiltering.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | enum class VideoFiltering {
4 | NONE,
5 | LINEAR,
6 | XBR2,
7 | HQ2X,
8 | HQ4X,
9 | QUILEZ,
10 | LCD,
11 | SCANLINES
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/VideoRenderer.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model
2 |
3 | enum class VideoRenderer(val renderer: Int) {
4 | SOFTWARE(0),
5 | OPENGL(1),
6 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/appupdate/AppUpdate.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.appupdate
2 |
3 | import android.net.Uri
4 | import me.magnum.melonds.domain.model.Version
5 | import java.time.Instant
6 |
7 | data class AppUpdate(
8 | val type: Type,
9 | val id: Long,
10 | val downloadUri: Uri,
11 | val newVersion: Version,
12 | val description: String,
13 | val binarySize: Long,
14 | val updateDate: Instant,
15 | ) {
16 |
17 | enum class Type {
18 | PRODUCTION,
19 | NIGHTLY,
20 | }
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/camera/DSiCameraSourceType.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.camera
2 |
3 | enum class DSiCameraSourceType {
4 | BLACK_SCREEN,
5 | PHYSICAL_CAMERAS,
6 | STATIC_IMAGE,
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/dsinand/DSiWareTitleFileType.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.dsinand
2 |
3 | enum class DSiWareTitleFileType(val fileName: String) {
4 | PUBLIC_SAV("public.sav"),
5 | PRIVATE_SAV("private.sav"),
6 | BANNER_SAV("banner.sav"),
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/dsinand/ImportDSiWareTitleResult.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.dsinand
2 |
3 | enum class ImportDSiWareTitleResult {
4 | SUCCESS,
5 | NAND_NOT_OPEN,
6 | ERROR_OPENING_FILE,
7 | NOT_DSIWARE_TITLE,
8 | TITLE_ALREADY_IMPORTED,
9 | INSATLL_FAILED,
10 | METADATA_FETCH_FAILED,
11 | UNKNOWN,
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/dsinand/OpenDSiNandResult.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.dsinand
2 |
3 | enum class OpenDSiNandResult {
4 | SUCCESS,
5 | NAND_ALREADY_OPEN,
6 | BIOS7_NOT_FOUND,
7 | NAND_OPEN_FAILED,
8 | INVALID_DSI_SETUP,
9 | UNKNOWN,
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/emulator/EmulatorSessionUpdateAction.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.emulator
2 |
3 | sealed class EmulatorSessionUpdateAction {
4 | data object EnableRetroAchievements : EmulatorSessionUpdateAction()
5 | data object DisableRetroAchievements : EmulatorSessionUpdateAction()
6 | data object NotifyRetroAchievementsModeSwitch : EmulatorSessionUpdateAction()
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/emulator/FirmwareLaunchResult.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.emulator
2 |
3 | import me.magnum.melonds.MelonEmulator
4 |
5 | sealed class FirmwareLaunchResult {
6 | data class LaunchFailed(val reason: MelonEmulator.FirmwareLoadResult) : FirmwareLaunchResult()
7 | object LaunchSuccessful : FirmwareLaunchResult()
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/emulator/RomLaunchResult.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.emulator
2 |
3 | import me.magnum.melonds.MelonEmulator
4 |
5 | sealed class RomLaunchResult {
6 | data object LaunchFailedRomNotFound : RomLaunchResult()
7 | data class LaunchFailedSramProblem(val reason: Exception) : RomLaunchResult()
8 | data class LaunchFailed(val reason: MelonEmulator.LoadResult) : RomLaunchResult()
9 | data class LaunchSuccessful(val isGbaLoadSuccessful: Boolean) : RomLaunchResult()
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/layout/BackgroundMode.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.layout
2 |
3 | enum class BackgroundMode {
4 | STRETCH,
5 | FIT_CENTER,
6 | FIT_TOP,
7 | FIT_LEFT,
8 | FIT_BOTTOM,
9 | FIT_RIGHT;
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/layout/LayoutComponent.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.layout
2 |
3 | enum class LayoutComponent {
4 | TOP_SCREEN,
5 | BOTTOM_SCREEN,
6 | DPAD,
7 | BUTTONS,
8 | BUTTON_START,
9 | BUTTON_SELECT,
10 | BUTTON_L,
11 | BUTTON_R,
12 | BUTTON_HINGE,
13 | BUTTON_FAST_FORWARD_TOGGLE,
14 | BUTTON_TOGGLE_SOFT_INPUT,
15 | BUTTON_RESET,
16 | BUTTON_PAUSE,
17 | BUTTON_SWAP_SCREENS,
18 | BUTTON_QUICK_SAVE,
19 | BUTTON_QUICK_LOAD,
20 | BUTTON_REWIND,
21 | BUTTON_MICROPHONE_TOGGLE;
22 |
23 | fun isScreen(): Boolean {
24 | return this == TOP_SCREEN || this == BOTTOM_SCREEN
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/layout/PositionedLayoutComponent.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.layout
2 |
3 | import me.magnum.melonds.domain.model.Rect
4 |
5 | data class PositionedLayoutComponent(val rect: Rect, val component: LayoutComponent) {
6 | fun isScreen(): Boolean {
7 | return component.isScreen()
8 | }
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/layout/ScreenFold.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.layout
2 |
3 | import me.magnum.melonds.domain.model.Rect
4 | import me.magnum.melonds.domain.model.ui.Orientation
5 |
6 | data class ScreenFold(
7 | val orientation: Orientation,
8 | val type: FoldType,
9 | val foldBounds: Rect,
10 | ) {
11 |
12 | enum class FoldType {
13 | SEAMLESS,
14 | GAP,
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/layout/UILayout.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.layout
2 |
3 | import java.util.UUID
4 |
5 | data class UILayout(
6 | val backgroundId: UUID?,
7 | val backgroundMode: BackgroundMode,
8 | val components: List?,
9 | ) {
10 | // Empty constructor allow parsing after new data is added to the class
11 | constructor() : this(null)
12 |
13 | constructor(components: List?): this(null, BackgroundMode.FIT_CENTER, components)
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/layout/UILayoutVariant.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.layout
2 |
3 | import me.magnum.melonds.domain.model.Point
4 | import me.magnum.melonds.domain.model.ui.Orientation
5 |
6 | data class UILayoutVariant(
7 | val uiSize: Point,
8 | val orientation: Orientation,
9 | val folds: List,
10 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/render/FrameRenderEvent.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.render
2 |
3 | data class FrameRenderEvent(
4 | val textureId: Int,
5 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/retroachievements/RAEvent.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.retroachievements
2 |
3 | sealed class RAEvent {
4 | data class OnAchievementPrimed(val achievementId: Long) : RAEvent()
5 | data class OnAchievementUnPrimed(val achievementId: Long) : RAEvent()
6 | data class OnAchievementTriggered(val achievementId: Long) : RAEvent()
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/retroachievements/RAGameSummary.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.retroachievements
2 |
3 | import java.net.URL
4 |
5 | data class RAGameSummary(
6 | val icon: URL,
7 | val richPresencePatch: String?,
8 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/retroachievements/RASimpleAchievement.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.retroachievements
2 |
3 | data class RASimpleAchievement(
4 | val id: Long,
5 | val memoryAddress: String,
6 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/retroachievements/RAUserAchievement.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.retroachievements
2 |
3 | import me.magnum.rcheevosapi.model.RAAchievement
4 |
5 | data class RAUserAchievement(
6 | val achievement: RAAchievement,
7 | val isUnlocked: Boolean,
8 | val forHardcoreMode: Boolean,
9 | ) {
10 |
11 | /**
12 | * Returns the number of points that this achievement is worth for the user in the current state. This depends on the actual number of points that the achievements awards,
13 | * whether the user has unlocked it or not and if it was unlocked in hardcore or softcore mode.
14 | */
15 | fun userPointsWorth(): Int {
16 | return if (isUnlocked) {
17 | achievement.points
18 | } else {
19 | 0
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/retroachievements/exception/RAGameNotExist.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.retroachievements.exception
2 |
3 | class RAGameNotExist(gameHash: String) : Exception("There is no game for hash $gameHash")
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/rom/Rom.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.rom
2 |
3 | import android.net.Uri
4 | import me.magnum.melonds.domain.model.rom.config.RomConfig
5 | import java.util.*
6 |
7 | data class Rom(
8 | val name: String,
9 | val developerName: String,
10 | val fileName: String,
11 | val uri: Uri,
12 | val parentTreeUri: Uri,
13 | var config: RomConfig,
14 | var lastPlayed: Date? = null,
15 | val isDsiWareTitle: Boolean,
16 | val retroAchievementsHash: String,
17 | ) {
18 |
19 | fun hasSameFileAsRom(other: Rom): Boolean {
20 | return uri == other.uri
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/rom/config/RomConfig.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.rom.config
2 |
3 | import java.util.*
4 |
5 | data class RomConfig(
6 | var runtimeConsoleType: RuntimeConsoleType = RuntimeConsoleType.DEFAULT,
7 | var runtimeMicSource: RuntimeMicSource = RuntimeMicSource.DEFAULT,
8 | var layoutId: UUID? = null,
9 | val gbaSlotConfig: RomGbaSlotConfig = RomGbaSlotConfig.None,
10 | )
11 |
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/rom/config/RomGbaSlotConfig.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.rom.config
2 |
3 | import android.net.Uri
4 |
5 | sealed class RomGbaSlotConfig {
6 | data object None : RomGbaSlotConfig()
7 | data class GbaRom(val romPath: Uri?, val savePath: Uri?) : RomGbaSlotConfig()
8 | data object MemoryExpansion : RomGbaSlotConfig()
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/rom/config/RuntimeConsoleType.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.rom.config
2 |
3 | import me.magnum.melonds.domain.model.ConsoleType
4 |
5 | enum class RuntimeConsoleType(val targetConsoleType: ConsoleType?) : RuntimeEnum {
6 | DEFAULT(null),
7 | DS(ConsoleType.DS),
8 | DSi(ConsoleType.DSi);
9 |
10 | override fun getDefault() = DEFAULT
11 | override fun getValue() = targetConsoleType!!
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/rom/config/RuntimeEnum.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.rom.config
2 |
3 | interface RuntimeEnum {
4 | fun getDefault(): T
5 | fun getValue(): U
6 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/rom/config/RuntimeMicSource.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.rom.config
2 |
3 | import me.magnum.melonds.domain.model.MicSource
4 |
5 | enum class RuntimeMicSource(val micSource: MicSource?) : RuntimeEnum {
6 | DEFAULT(null),
7 | NONE(MicSource.NONE),
8 | BLOW(MicSource.BLOW),
9 | DEVICE(MicSource.DEVICE);
10 |
11 | override fun getDefault() = DEFAULT
12 | override fun getValue() = micSource!!
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/model/ui/Orientation.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.model.ui
2 |
3 | enum class Orientation {
4 | PORTRAIT,
5 | LANDSCAPE
6 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/repositories/BackgroundRepository.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.repositories
2 |
3 | import kotlinx.coroutines.flow.Flow
4 | import me.magnum.melonds.domain.model.Background
5 | import java.util.UUID
6 |
7 | interface BackgroundRepository {
8 | fun getBackgrounds(): Flow>
9 | suspend fun getBackground(id: UUID): Background?
10 | suspend fun addBackground(background: Background)
11 | suspend fun deleteBackground(background: Background)
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/repositories/DSiWareMetadataRepository.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.repositories
2 |
3 | interface DSiWareMetadataRepository {
4 | suspend fun getDSiWareTitleMetadata(categoryId: UInt, titleId: UInt): ByteArray
5 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/repositories/LayoutsRepository.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.repositories
2 |
3 | import kotlinx.coroutines.flow.Flow
4 | import me.magnum.melonds.domain.model.layout.LayoutConfiguration
5 | import java.util.UUID
6 |
7 | interface LayoutsRepository {
8 | fun getLayouts(): Flow>
9 | suspend fun getLayout(id: UUID): LayoutConfiguration?
10 | suspend fun deleteLayout(layout: LayoutConfiguration)
11 | fun getGlobalLayoutPlaceholder(): LayoutConfiguration
12 | fun observeLayout(id: UUID): Flow
13 | suspend fun saveLayout(layout: LayoutConfiguration)
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/repositories/RomsRepository.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.repositories
2 |
3 | import android.net.Uri
4 | import kotlinx.coroutines.flow.Flow
5 | import kotlinx.coroutines.flow.StateFlow
6 | import me.magnum.melonds.domain.model.rom.Rom
7 | import me.magnum.melonds.domain.model.rom.config.RomConfig
8 | import me.magnum.melonds.domain.model.RomScanningStatus
9 | import java.util.*
10 |
11 | interface RomsRepository {
12 | fun getRoms(): Flow>
13 | fun getRomScanningStatus(): StateFlow
14 | suspend fun getRomAtPath(path: String): Rom?
15 | suspend fun getRomAtUri(uri: Uri): Rom?
16 |
17 | fun updateRomConfig(rom: Rom, romConfig: RomConfig)
18 | fun setRomLastPlayed(rom: Rom, lastPlayed: Date)
19 | fun rescanRoms()
20 | fun invalidateRoms()
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/repositories/SaveStatesRepository.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.repositories
2 |
3 | import android.graphics.Bitmap
4 | import android.net.Uri
5 | import me.magnum.melonds.domain.model.rom.Rom
6 | import me.magnum.melonds.domain.model.SaveStateSlot
7 |
8 | interface SaveStatesRepository {
9 | fun getRomSaveStates(rom: Rom): List
10 | fun getRomQuickSaveStateSlot(rom: Rom): SaveStateSlot
11 | fun getRomSaveStateUri(rom: Rom, saveState: SaveStateSlot): Uri
12 | fun setRomSaveStateScreenshot(rom: Rom, saveState: SaveStateSlot, screenshot: Bitmap)
13 | fun deleteRomSaveState(rom: Rom, saveState: SaveStateSlot)
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/repositories/UpdatesRepository.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.repositories
2 |
3 | import me.magnum.melonds.domain.model.appupdate.AppUpdate
4 |
5 | interface UpdatesRepository {
6 | suspend fun checkNewUpdate(): Result
7 | fun skipUpdate(update: AppUpdate)
8 | fun notifyUpdateDownloaded(update: AppUpdate)
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/services/DSiNandManager.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.services
2 |
3 | import android.net.Uri
4 | import me.magnum.melonds.domain.model.DSiWareTitle
5 | import me.magnum.melonds.domain.model.dsinand.ImportDSiWareTitleResult
6 | import me.magnum.melonds.domain.model.dsinand.OpenDSiNandResult
7 | import me.magnum.melonds.domain.model.dsinand.DSiWareTitleFileType
8 |
9 | interface DSiNandManager {
10 | suspend fun openNand(): OpenDSiNandResult
11 | suspend fun listTitles(): List
12 | suspend fun importTitle(titleUri: Uri): ImportDSiWareTitleResult
13 | suspend fun deleteTitle(title: DSiWareTitle)
14 | suspend fun importTitleFile(title: DSiWareTitle, fileType: DSiWareTitleFileType, fileUri: Uri): Boolean
15 | suspend fun exportTitleFile(title: DSiWareTitle, fileType: DSiWareTitleFileType, fileUri: Uri): Boolean
16 | fun closeNand()
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/domain/services/UpdateInstallManager.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.domain.services
2 |
3 | import kotlinx.coroutines.flow.Flow
4 | import me.magnum.melonds.domain.model.DownloadProgress
5 | import me.magnum.melonds.domain.model.appupdate.AppUpdate
6 |
7 | interface UpdateInstallManager {
8 | fun downloadAndInstallUpdate(update: AppUpdate): Flow
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/extensions/ContextExtensions.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.extensions
2 |
3 | import android.Manifest
4 | import android.content.Context
5 | import android.content.pm.PackageManager
6 | import android.os.PowerManager
7 | import androidx.annotation.RequiresApi
8 | import androidx.core.content.ContextCompat
9 |
10 | @RequiresApi(33)
11 | fun Context.isNotificationPermissionGranted(): Boolean {
12 | return ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED
13 | }
14 |
15 | fun Context.isMicrophonePermissionGranted(): Boolean {
16 | return ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED
17 | }
18 |
19 | fun Context.isSustainedPerformanceModeAvailable(): Boolean {
20 | val powerManager = this.getSystemService(Context.POWER_SERVICE) as PowerManager
21 | return powerManager.isSustainedPerformanceModeSupported
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/extensions/DisposableExtensions.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.extensions
2 |
3 | import io.reactivex.disposables.CompositeDisposable
4 | import io.reactivex.disposables.Disposable
5 |
6 | fun Disposable.addTo(compositeDisposable: CompositeDisposable) {
7 | compositeDisposable.add(this)
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/extensions/DocumentFileExtensions.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.extensions
2 |
3 | import androidx.documentfile.provider.DocumentFile
4 |
5 | val DocumentFile.nameWithoutExtension get() = name?.substringBeforeLast('.')
6 |
7 | val DocumentFile.extension get() = name?.substringAfterLast('.')
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/extensions/ListExtensions.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.extensions
2 |
3 | fun MutableList.removeFirst(predicate: (T) -> Boolean): T? {
4 | val itemIndex = indexOfFirst(predicate)
5 | return if (itemIndex >= 0) {
6 | removeAt(itemIndex)
7 | } else {
8 | null
9 | }
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/extensions/PreferenceExtensions.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.extensions
2 |
3 | import androidx.preference.Preference
4 | import me.magnum.melonds.utils.CompositeOnPreferenceChangeListener
5 |
6 | fun Preference.addOnPreferenceChangeListener(listener: Preference.OnPreferenceChangeListener) {
7 | val currentListener = onPreferenceChangeListener
8 | if (currentListener is CompositeOnPreferenceChangeListener) {
9 | currentListener.addOnPreferenceChangeListener(listener)
10 | } else {
11 | onPreferenceChangeListener = CompositeOnPreferenceChangeListener().apply {
12 | if (currentListener != null) {
13 | addOnPreferenceChangeListener(currentListener)
14 | }
15 | addOnPreferenceChangeListener(listener)
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/extensions/StringExtensions.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.extensions
2 |
3 | fun String.isBlank(): Boolean {
4 | // Replace null terminators with another whitespace character
5 | val replacedString = this.replace('\u0000', '\u0009')
6 | return (replacedString as CharSequence).isBlank()
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/extensions/WindowExtensions.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.extensions
2 |
3 | import android.view.Window
4 | import androidx.core.view.WindowCompat
5 | import androidx.core.view.WindowInsetsControllerCompat
6 |
7 | inline val Window.insetsControllerCompat: WindowInsetsControllerCompat?
8 | get() = WindowCompat.getInsetsController(this, decorView)
9 |
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/impl/ScreenUnitsConverter.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.impl
2 |
3 | import android.content.Context
4 | import android.util.DisplayMetrics
5 |
6 | class ScreenUnitsConverter(private val context: Context) {
7 | fun pixelsToDp(pixels: Float): Float {
8 | return pixels / (context.resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
9 | }
10 |
11 | fun dpToPixels(dp: Float): Float {
12 | return dp * (context.resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
13 | }
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/impl/camera/CameraBuffers.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.impl.camera
2 |
3 | class CameraBuffers {
4 | private val cameraBuffers = Array(2) {
5 | // YUV 422 format. Byte structure: YUYV YUYV (4 bytes per 2 pixels)
6 | ByteArray(640 * 480 * 2)
7 | }
8 | private var activeBufferIndex = 0
9 |
10 | fun getFrontBuffer(): ByteArray {
11 | return cameraBuffers[activeBufferIndex]
12 | }
13 |
14 | fun getBackBuffer(): ByteArray {
15 | return cameraBuffers[(activeBufferIndex + 1) % cameraBuffers.size]
16 | }
17 |
18 | fun swapBuffers() {
19 | activeBufferIndex = (activeBufferIndex + 1) % cameraBuffers.size
20 | }
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/impl/dtos/input/ControllerConfigurationDto.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.impl.dtos.input
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 | import me.magnum.melonds.domain.model.ControllerConfiguration
6 |
7 | @Serializable
8 | data class ControllerConfigurationDto(
9 | @SerialName("inputMapper") val inputMapper: List,
10 | ) {
11 |
12 | companion object {
13 | fun fromControllerConfiguration(controllerConfiguration: ControllerConfiguration): ControllerConfigurationDto {
14 | val inputMapping = controllerConfiguration.inputMapper.map { InputConfigDto.fromInputConfig(it) }
15 | return ControllerConfigurationDto(inputMapping)
16 | }
17 | }
18 |
19 | fun toControllerConfiguration(): ControllerConfiguration {
20 | return ControllerConfiguration(inputMapper.map { it.toInputConfig() })
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/impl/dtos/layout/PointDto.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.impl.dtos.layout
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import me.magnum.melonds.domain.model.Point
5 |
6 | data class PointDto(
7 | @SerializedName("x")
8 | val x: Int,
9 | @SerializedName("y")
10 | val y: Int,
11 | ) {
12 |
13 | fun toModel(): Point {
14 | return Point(x, y)
15 | }
16 |
17 | companion object {
18 | fun fromModel(point: Point): PointDto {
19 | return PointDto(point.x, point.y)
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/impl/dtos/layout/RectDto.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.impl.dtos.layout
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import me.magnum.melonds.domain.model.Rect
5 |
6 | data class RectDto(
7 | @SerializedName("x")
8 | val x: Int,
9 | @SerializedName("y")
10 | val y: Int,
11 | @SerializedName("width")
12 | val width: Int,
13 | @SerializedName("height")
14 | val height: Int
15 | ) {
16 |
17 | companion object {
18 | fun fromModel(rect: Rect): RectDto {
19 | return RectDto(rect.x, rect.y, rect.width, rect.height)
20 | }
21 | }
22 |
23 | fun toModel(): Rect {
24 | return Rect(x, y, width, height)
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/impl/dtos/layout/ScreenFoldDto.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.impl.dtos.layout
2 |
3 | import me.magnum.melonds.domain.model.layout.ScreenFold
4 | import me.magnum.melonds.utils.enumValueOfIgnoreCase
5 |
6 | data class ScreenFoldDto(
7 | val orientation: String,
8 | val type: String,
9 | val foldBounds: RectDto,
10 | ) {
11 |
12 | fun toModel(): ScreenFold {
13 | return ScreenFold(
14 | enumValueOfIgnoreCase(orientation),
15 | enumValueOfIgnoreCase(type),
16 | foldBounds.toModel(),
17 | )
18 | }
19 |
20 | companion object {
21 | fun fromModel(fold: ScreenFold): ScreenFoldDto {
22 | return ScreenFoldDto(
23 | fold.orientation.name,
24 | fold.type.name,
25 | RectDto.fromModel(fold.foldBounds),
26 | )
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/impl/emulator/LifecycleOwnerProvider.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.impl.emulator
2 |
3 | import androidx.lifecycle.Lifecycle
4 | import androidx.lifecycle.LifecycleEventObserver
5 | import androidx.lifecycle.LifecycleOwner
6 |
7 | class LifecycleOwnerProvider {
8 |
9 | private var currentLifecycleOwner: LifecycleOwner? = null
10 |
11 | fun getCurrentLifecycleOwner(): LifecycleOwner? {
12 | return currentLifecycleOwner
13 | }
14 |
15 | fun setCurrentLifecycleOwner(lifecycleOwner: LifecycleOwner) {
16 | currentLifecycleOwner = lifecycleOwner
17 | lifecycleOwner.lifecycle.addObserver(object : LifecycleEventObserver {
18 | override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
19 | if (event == Lifecycle.Event.ON_DESTROY) {
20 | currentLifecycleOwner = null
21 | }
22 | }
23 | })
24 | }
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/impl/emulator/SramLoadException.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.impl.emulator
2 |
3 | class SramLoadException(message: String) : Exception(message)
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/impl/image/BitmapLoader.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.impl.image
2 |
3 | import android.graphics.Bitmap
4 | import android.net.Uri
5 |
6 | interface BitmapLoader {
7 | fun loadAsBitmap(imageUri: Uri): Bitmap?
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/initializer/CoilInitializer.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.initializer
2 |
3 | import android.content.Context
4 | import androidx.startup.Initializer
5 | import coil.Coil
6 | import coil.ImageLoader
7 | import me.magnum.melonds.di.entrypoint.InitializerEntryPoint
8 | import javax.inject.Inject
9 |
10 | class CoilInitializer : Initializer {
11 |
12 | @Inject
13 | lateinit var imageLoader: ImageLoader
14 |
15 | override fun create(context: Context) {
16 | InitializerEntryPoint.resolve(context).inject(this)
17 | Coil.setImageLoader(imageLoader)
18 | }
19 |
20 | override fun dependencies(): MutableList>> = mutableListOf()
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/migrations/Migration.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.migrations
2 |
3 | interface Migration {
4 | val from: Int
5 | val to: Int
6 |
7 | fun migrate()
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/migrations/Migration14to15.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.migrations
2 |
3 | import me.magnum.melonds.impl.RomIconProvider
4 |
5 | class Migration14to15(private val romIconProvider: RomIconProvider) : Migration {
6 | override val from = 14
7 | override val to = 15
8 |
9 | override fun migrate() {
10 | // Delete cached icons since the icon generation method has changed
11 | romIconProvider.clearIconCache()
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/migrations/Migration16to17.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.migrations
2 |
3 | import me.magnum.melonds.domain.repositories.RomsRepository
4 |
5 | class Migration16to17(private val romsRepository: RomsRepository) : Migration {
6 | override val from = 16
7 | override val to = 17
8 |
9 | override fun migrate() {
10 | romsRepository.invalidateRoms()
11 | }
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/migrations/Migration6to7.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.migrations
2 |
3 | import android.content.SharedPreferences
4 | import androidx.core.content.edit
5 |
6 | class Migration6to7(private val sharedPreferences: SharedPreferences) : Migration {
7 | override val from: Int
8 | get() = 6
9 | override val to: Int
10 | get() = 7
11 |
12 | override fun migrate() {
13 | // Dir preferences now have a different format
14 | sharedPreferences.edit {
15 | putStringSet("bios_dir", emptySet())
16 | putStringSet("rom_search_dirs", emptySet())
17 | putStringSet("sram_dir", emptySet())
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/migrations/Migration7to8.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.migrations
2 |
3 | import android.content.Context
4 |
5 | class Migration7to8(private val context: Context) : Migration {
6 | override val from: Int
7 | get() = 7
8 | override val to: Int
9 | get() = 8
10 |
11 | override fun migrate() {
12 | // Delete cached icons since the directory is now different
13 | context.externalCacheDir?.let { cacheDir ->
14 | cacheDir.listFiles()?.forEach { it.delete() }
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/migrations/legacy/Rom21.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.migrations.legacy
2 |
3 | import android.net.Uri
4 | import java.util.*
5 |
6 | /**
7 | * ROM model used until app version 21.
8 | */
9 | data class Rom21(
10 | val name: String,
11 | val uri: Uri,
12 | val parentTreeUri: Uri,
13 | var config: RomConfig1,
14 | var lastPlayed: Date? = null,
15 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/migrations/legacy/Rom22.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.migrations.legacy
2 |
3 | import android.net.Uri
4 | import com.google.gson.annotations.SerializedName
5 | import java.util.*
6 |
7 | /**
8 | * ROM model used from app version 22.
9 | */
10 | data class Rom22(
11 | @SerializedName("a")
12 | val name: String,
13 | @SerializedName("b")
14 | val fileName: String,
15 | @SerializedName("c")
16 | val uri: Uri,
17 | @SerializedName("d")
18 | val parentTreeUri: Uri,
19 | @SerializedName("e")
20 | val config: RomConfig1,
21 | @SerializedName("f")
22 | val lastPlayed: Date? = null,
23 | @SerializedName("g")
24 | val isDsiWareTitle: Boolean,
25 | )
26 |
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/migrations/legacy/RomConfig1.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.migrations.legacy
2 |
3 | import android.net.Uri
4 | import com.google.gson.annotations.SerializedName
5 | import me.magnum.melonds.domain.model.rom.config.RuntimeConsoleType
6 | import me.magnum.melonds.domain.model.rom.config.RuntimeMicSource
7 | import java.util.*
8 |
9 | data class RomConfig1(
10 | @SerializedName("a")
11 | val runtimeConsoleType: RuntimeConsoleType,
12 | @SerializedName("b")
13 | val runtimeMicSource: RuntimeMicSource,
14 | @SerializedName("c")
15 | val layoutId: UUID?,
16 | @SerializedName("d")
17 | val loadGbaCart: Boolean,
18 | @SerializedName("e")
19 | val gbaCartPath: Uri?,
20 | @SerializedName("f")
21 | val gbaSavePath: Uri?,
22 | )
23 |
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/migrations/legacy/RomConfigDto25.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.migrations.legacy
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import me.magnum.melonds.domain.model.rom.config.RuntimeConsoleType
5 | import me.magnum.melonds.domain.model.rom.config.RuntimeMicSource
6 |
7 | data class RomConfigDto25(
8 | @SerializedName("runtimeConsoleType")
9 | val runtimeConsoleType: RuntimeConsoleType,
10 | @SerializedName("runtimeMicSource")
11 | val runtimeMicSource: RuntimeMicSource,
12 | @SerializedName("layoutId")
13 | val layoutId: String?,
14 | @SerializedName("loadGbaCart")
15 | val loadGbaCart: Boolean,
16 | @SerializedName("gbaCartPath")
17 | val gbaCartPath: String?,
18 | @SerializedName("gbaSavePath")
19 | val gbaSavePath: String?,
20 | )
21 |
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/migrations/legacy/RomConfigDto31.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.migrations.legacy
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import me.magnum.melonds.domain.model.rom.config.RuntimeConsoleType
5 | import me.magnum.melonds.domain.model.rom.config.RuntimeMicSource
6 |
7 | data class RomConfigDto31(
8 | @SerializedName("runtimeConsoleType")
9 | val runtimeConsoleType: RuntimeConsoleType,
10 | @SerializedName("runtimeMicSource")
11 | val runtimeMicSource: RuntimeMicSource,
12 | @SerializedName("layoutId")
13 | val layoutId: String?,
14 | @SerializedName("gbaSlotConfig")
15 | val gbaSlotConfig: RomGbaSlotConfigDto31,
16 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/migrations/legacy/RomDto25.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.migrations.legacy
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import java.util.*
5 |
6 | /**
7 | * ROM DTO used from app version 25.
8 | */
9 | data class RomDto25(
10 | @SerializedName("name")
11 | val name: String,
12 | @SerializedName("developerName")
13 | val developerName: String,
14 | @SerializedName("fileName")
15 | val fileName: String,
16 | @SerializedName("uri")
17 | val uri: String,
18 | @SerializedName("parentTreeUri")
19 | val parentTreeUri: String,
20 | @SerializedName("config")
21 | var config: RomConfigDto25,
22 | @SerializedName("lastPlayed")
23 | var lastPlayed: Date? = null,
24 | @SerializedName("isDsiWareTitle")
25 | val isDsiWareTitle: Boolean,
26 | @SerializedName("retroAchievementsHash")
27 | val retroAchievementsHash: String,
28 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/migrations/legacy/RomDto31.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.migrations.legacy
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import java.util.Date
5 |
6 | /**
7 | * ROM DTO used from app version 27.
8 | */
9 | data class RomDto31(
10 | @SerializedName("name")
11 | val name: String,
12 | @SerializedName("developerName")
13 | val developerName: String,
14 | @SerializedName("fileName")
15 | val fileName: String,
16 | @SerializedName("uri")
17 | val uri: String,
18 | @SerializedName("parentTreeUri")
19 | val parentTreeUri: String,
20 | @SerializedName("config")
21 | var config: RomConfigDto31,
22 | @SerializedName("lastPlayed")
23 | var lastPlayed: Date? = null,
24 | @SerializedName("isDsiWareTitle")
25 | val isDsiWareTitle: Boolean,
26 | @SerializedName("retroAchievementsHash")
27 | val retroAchievementsHash: String,
28 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/migrations/legacy/RomGbaSlotConfigDto31.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.migrations.legacy
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 | class RomGbaSlotConfigDto31(
6 | @SerializedName("type")
7 | val type: Type,
8 | @SerializedName("gbaRomPath")
9 | val gbaRomPath: String?,
10 | @SerializedName("gbaSavePath")
11 | val gbaSavePath: String?,
12 | ) {
13 |
14 | enum class Type {
15 | None, GbaRom, MemoryExpansion
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/migrations/legacy/input/ControllerConfigurationDto33.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.migrations.legacy.input
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class ControllerConfigurationDto33(
8 | @SerialName("a") val inputMapper: List,
9 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/migrations/legacy/input/InputConfigDto33.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.migrations.legacy.input
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 | import me.magnum.melonds.domain.model.Input
6 |
7 | @Serializable
8 | data class InputConfigDto33(
9 | @SerialName("a") val input: Input,
10 | @SerialName("b") val key: Int,
11 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/migrations/legacy/layout/LayoutConfiguration25.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.migrations.legacy.layout
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 | /**
6 | * LayoutConfiguration model used until app version 25.
7 | */
8 | data class LayoutConfiguration25(
9 | @SerializedName("a")
10 | val id: String?,
11 | @SerializedName("b")
12 | val name: String?,
13 | @SerializedName("c")
14 | val type: String,
15 | @SerializedName("d")
16 | val orientation: String,
17 | @SerializedName("e")
18 | val useCustomOpacity: Boolean,
19 | @SerializedName("f")
20 | val opacity: Int,
21 | @SerializedName("g")
22 | val portraitLayout: UILayout25,
23 | @SerializedName("h")
24 | val landscapeLayout: UILayout25,
25 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/migrations/legacy/layout/LayoutConfigurationDto31.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.migrations.legacy.layout
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import me.magnum.melonds.impl.dtos.layout.UILayoutDto
5 |
6 | data class LayoutConfigurationDto31(
7 | @SerializedName("id")
8 | val id: String?,
9 | @SerializedName("name")
10 | val name: String?,
11 | @SerializedName("type")
12 | val type: String,
13 | @SerializedName("orientation")
14 | val orientation: String,
15 | @SerializedName("useCustomOpacity")
16 | val useCustomOpacity: Boolean,
17 | @SerializedName("opacity")
18 | val opacity: Int,
19 | @SerializedName("portraitLayout")
20 | val portraitLayout: UILayoutDto,
21 | @SerializedName("landscapeLayout")
22 | val landscapeLayout: UILayoutDto
23 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/migrations/legacy/layout/PositionedLayoutComponent25.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.migrations.legacy.layout
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 | /**
6 | * PositionedLayoutComponent model used until app version 25.
7 | */
8 | data class PositionedLayoutComponent25(
9 | @SerializedName("a")
10 | val rect: Rect25,
11 | @SerializedName("b")
12 | val component: String,
13 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/migrations/legacy/layout/Rect25.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.migrations.legacy.layout
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 | /**
6 | * Rect model used until app version 25.
7 | */
8 | data class Rect25(
9 | @SerializedName("a")
10 | val x: Int,
11 | @SerializedName("b")
12 | val y: Int,
13 | @SerializedName("c")
14 | val width: Int,
15 | @SerializedName("d")
16 | val height: Int,
17 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/migrations/legacy/layout/UILayout25.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.migrations.legacy.layout
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 | /**
6 | * UILayout model used until app version 25.
7 | */
8 | data class UILayout25(
9 | @SerializedName("a")
10 | val backgroundId: String?,
11 | @SerializedName("b")
12 | val backgroundMode: String,
13 | @SerializedName("c")
14 | val components: List,
15 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/parcelables/RomInfoParcelable.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.parcelables
2 |
3 | import android.os.Parcelable
4 | import kotlinx.parcelize.Parcelize
5 | import me.magnum.melonds.domain.model.RomInfo
6 |
7 | @Parcelize
8 | class RomInfoParcelable(
9 | private val gameCode: String,
10 | private val headerChecksum: Int,
11 | private val gameTitle: String,
12 | private val gameName: String,
13 | ) : Parcelable {
14 | companion object {
15 | fun fromRomInfo(romInfo: RomInfo): RomInfoParcelable {
16 | return RomInfoParcelable(romInfo.gameCode, romInfo.headerChecksum.toInt(), romInfo.gameTitle, romInfo.gameName)
17 | }
18 | }
19 |
20 | fun toRomInfo(): RomInfo {
21 | return RomInfo(gameCode, headerChecksum.toUInt(), gameTitle, gameName)
22 | }
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/parcelables/cheat/CheatFolderParcelable.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.parcelables.cheat
2 |
3 | import android.os.Parcelable
4 | import kotlinx.parcelize.Parcelize
5 | import me.magnum.melonds.domain.model.CheatFolder
6 |
7 | @Parcelize
8 | class CheatFolderParcelable(
9 | val id: Long?,
10 | val name: String,
11 | val cheats: List,
12 | ) : Parcelable {
13 |
14 | fun toCheatFolder(): CheatFolder {
15 | return CheatFolder(
16 | id = id,
17 | name = name,
18 | cheats = cheats.map { it.toCheat() }
19 | )
20 | }
21 |
22 | companion object {
23 | fun fromCheatFolder(cheatFolder: CheatFolder): CheatFolderParcelable {
24 | return CheatFolderParcelable(
25 | id = cheatFolder.id,
26 | name = cheatFolder.name,
27 | cheats = cheatFolder.cheats.map { CheatParcelable.fromCheat(it) }
28 | )
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/Theme.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui
2 |
3 | import androidx.appcompat.app.AppCompatDelegate
4 |
5 | enum class Theme(val nightMode: Int) {
6 | LIGHT(AppCompatDelegate.MODE_NIGHT_NO),
7 | DARK(AppCompatDelegate.MODE_NIGHT_YES),
8 | SYSTEM(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/cheats/CheatsNavigation.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.cheats
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | sealed class CheatsNavigation {
6 | @Serializable
7 | data object Loading : CheatsNavigation()
8 | @Serializable
9 | data object GameList : CheatsNavigation()
10 | @Serializable
11 | data class GameFolders(val gameName: String?) : CheatsNavigation()
12 | @Serializable
13 | data class FolderCheats(val folderName: String?) : CheatsNavigation()
14 | @Serializable
15 | data object EnabledCheats : CheatsNavigation()
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/cheats/model/CheatSubmissionForm.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.cheats.model
2 |
3 | data class CheatSubmissionForm(
4 | val name: String,
5 | val description: String,
6 | val code: String,
7 | ) {
8 |
9 | fun isValid(): Boolean {
10 | return name.isNotBlank() && code.isNotBlank()
11 | }
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/cheats/model/CheatsScreenUiState.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.cheats.model
2 |
3 | sealed class CheatsScreenUiState {
4 | class Loading : CheatsScreenUiState()
5 | data class Ready(val data: T) : CheatsScreenUiState()
6 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/cheats/model/DeletedCheat.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.cheats.model
2 |
3 | import me.magnum.melonds.domain.model.Cheat
4 | import me.magnum.melonds.domain.model.CheatFolder
5 |
6 | data class DeletedCheat(val cheat: Cheat, val folder: CheatFolder)
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/cheats/model/OpenScreenEvent.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.cheats.model
2 |
3 | data class OpenScreenEvent(val newTitle: String?)
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/cheats/ui/LoadingScreen.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.cheats.ui
2 |
3 | import androidx.compose.foundation.layout.Box
4 | import androidx.compose.material.CircularProgressIndicator
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.Alignment
7 | import androidx.compose.ui.Modifier
8 |
9 | @Composable
10 | fun LoadingScreen(modifier: Modifier) {
11 | Box(modifier) {
12 | CircularProgressIndicator(Modifier.align(Alignment.Center))
13 | }
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/common/LayoutComponentViewBuilder.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.common
2 |
3 | import android.content.Context
4 | import android.view.View
5 |
6 | abstract class LayoutComponentViewBuilder {
7 | abstract fun build(context: Context): View
8 | abstract fun getAspectRatio(): Float
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/common/LayoutComponentViewBuilderFactory.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.common
2 |
3 | import me.magnum.melonds.domain.model.layout.LayoutComponent
4 |
5 | interface LayoutComponentViewBuilderFactory {
6 | fun getLayoutComponentViewBuilder(layoutComponent: LayoutComponent): LayoutComponentViewBuilder
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/common/LoadingDialog.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.common
2 |
3 | import android.app.Dialog
4 | import android.content.Context
5 | import android.graphics.Color
6 | import android.graphics.drawable.ColorDrawable
7 | import android.os.Bundle
8 | import me.magnum.melonds.R
9 |
10 | class LoadingDialog(context: Context) : Dialog(context) {
11 |
12 | override fun onCreate(savedInstanceState: Bundle?) {
13 | super.onCreate(savedInstanceState)
14 | setContentView(R.layout.dialog_loading)
15 | setCancelable(false)
16 | setCanceledOnTouchOutside(false)
17 | window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
18 | }
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/common/MelonPreviewSet.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.common
2 |
3 | import android.content.res.Configuration.UI_MODE_NIGHT_YES
4 | import androidx.compose.ui.tooling.preview.Preview
5 |
6 | @Preview(name = "Light", showBackground = true)
7 | @Preview(name = "Dark", showBackground = true, uiMode = UI_MODE_NIGHT_YES)
8 | annotation class MelonPreviewSet
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/common/componentbuilders/BottomScreenLayoutComponentViewBuilder.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.common.componentbuilders
2 |
3 | import android.content.Context
4 | import androidx.core.content.ContextCompat
5 | import me.magnum.melonds.R
6 |
7 | class BottomScreenLayoutComponentViewBuilder : ScreenLayoutComponentViewBuilder() {
8 | override fun getBackgroundDrawable(context: Context) = ContextCompat.getDrawable(context, R.drawable.background_bottom_screen)
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/common/componentbuilders/ButtonsLayoutComponentViewBuilder.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.common.componentbuilders
2 |
3 | import android.content.Context
4 | import android.view.View
5 | import android.widget.ImageView
6 | import me.magnum.melonds.R
7 | import me.magnum.melonds.ui.common.LayoutComponentViewBuilder
8 |
9 | class ButtonsLayoutComponentViewBuilder : LayoutComponentViewBuilder() {
10 | override fun build(context: Context): View {
11 | return ImageView(context).apply {
12 | setImageResource(R.drawable.buttons)
13 | }
14 | }
15 |
16 | override fun getAspectRatio() = 1f
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/common/componentbuilders/DpadLayoutComponentViewBuilder.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.common.componentbuilders
2 |
3 | import android.content.Context
4 | import android.view.View
5 | import android.widget.ImageView
6 | import me.magnum.melonds.R
7 | import me.magnum.melonds.ui.common.LayoutComponentViewBuilder
8 |
9 | class DpadLayoutComponentViewBuilder : LayoutComponentViewBuilder() {
10 | override fun build(context: Context): View {
11 | return ImageView(context).apply {
12 | setImageResource(R.drawable.keypad)
13 | }
14 | }
15 |
16 | override fun getAspectRatio() = 1f
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/common/componentbuilders/EditorBackgroundLayoutComponentViewBuilder.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.common.componentbuilders
2 |
3 | import android.content.Context
4 | import android.view.View
5 | import androidx.core.content.ContextCompat
6 | import me.magnum.melonds.R
7 | import me.magnum.melonds.ui.common.LayoutComponentViewBuilder
8 |
9 | class EditorBackgroundLayoutComponentViewBuilder(private val baseBuilder: LayoutComponentViewBuilder) : LayoutComponentViewBuilder() {
10 | override fun build(context: Context): View {
11 | return baseBuilder.build(context).apply {
12 | background = ContextCompat.getDrawable(context, R.drawable.background_uiview)
13 | }
14 | }
15 |
16 | override fun getAspectRatio() = baseBuilder.getAspectRatio()
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/common/componentbuilders/ScreenLayoutComponentViewBuilder.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.common.componentbuilders
2 |
3 | import android.content.Context
4 | import android.graphics.drawable.Drawable
5 | import android.view.View
6 | import me.magnum.melonds.ui.common.LayoutComponentViewBuilder
7 |
8 | abstract class ScreenLayoutComponentViewBuilder : LayoutComponentViewBuilder() {
9 | override fun build(context: Context): View {
10 | return View(context).apply {
11 | background = getBackgroundDrawable(context)
12 | }
13 | }
14 |
15 | override fun getAspectRatio() = 256f / 192
16 |
17 | protected abstract fun getBackgroundDrawable(context: Context): Drawable?
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/common/componentbuilders/TopScreenLayoutComponentViewBuilder.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.common.componentbuilders
2 |
3 | import android.content.Context
4 | import androidx.core.content.ContextCompat
5 | import me.magnum.melonds.R
6 |
7 | class TopScreenLayoutComponentViewBuilder : ScreenLayoutComponentViewBuilder() {
8 | override fun getBackgroundDrawable(context: Context) = ContextCompat.getDrawable(context, R.drawable.background_top_screen)
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/dsiwaremanager/model/DSiWareItemDropdownMenu.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.dsiwaremanager.model
2 |
3 | enum class DSiWareItemDropdownMenu {
4 | NONE,
5 | MAIN,
6 | IMPORT,
7 | EXPORT,
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/dsiwaremanager/model/DSiWareManagerUiState.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.dsiwaremanager.model
2 |
3 | import me.magnum.melonds.domain.model.ConfigurationDirResult
4 | import me.magnum.melonds.domain.model.DSiWareTitle
5 |
6 | sealed class DSiWareManagerUiState {
7 | data class DSiSetupInvalid(val status: ConfigurationDirResult.Status) : DSiWareManagerUiState()
8 | object Loading : DSiWareManagerUiState()
9 | data class Ready(val titles: List) : DSiWareManagerUiState()
10 | object Error : DSiWareManagerUiState()
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/dsiwaremanager/model/DSiWareMangerRomListUiState.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.dsiwaremanager.model
2 |
3 | import me.magnum.melonds.domain.model.rom.Rom
4 |
5 | sealed class DSiWareMangerRomListUiState {
6 | object Loading : DSiWareMangerRomListUiState()
7 | class Loaded(val roms: List) : DSiWareMangerRomListUiState()
8 | object Empty : DSiWareMangerRomListUiState()
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/dsiwaremanager/model/ImportExportDSiWareTitleFileEvent.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.dsiwaremanager.model
2 |
3 | sealed class ImportExportDSiWareTitleFileEvent {
4 | data class ImportSuccess(val fileName: String) : ImportExportDSiWareTitleFileEvent()
5 | data object ImportError : ImportExportDSiWareTitleFileEvent()
6 | data class ExportSuccess(val fileName: String) : ImportExportDSiWareTitleFileEvent()
7 | data object ExportError : ImportExportDSiWareTitleFileEvent()
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/EmulatorFrameRenderedListener.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator
2 |
3 | fun interface EmulatorFrameRenderedListener {
4 | fun onFrameRendered(textureId: Int)
5 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/PauseMenuOption.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator
2 |
3 | interface PauseMenuOption {
4 | val textResource: Int
5 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/component/EmulatorOverlayTracker.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator.component
2 |
3 | import me.magnum.melonds.ui.emulator.model.EmulatorOverlay
4 |
5 | class EmulatorOverlayTracker(
6 | private val onOverlaysCleared: () -> Unit,
7 | private val onOverlaysPresent: () -> Unit,
8 | ) {
9 |
10 | private val activeOverlays = mutableListOf()
11 |
12 | fun addActiveOverlay(overlay: EmulatorOverlay) {
13 | activeOverlays.add(overlay)
14 | if (activeOverlays.size == 1) {
15 | onOverlaysPresent()
16 | }
17 | }
18 |
19 | fun removeActiveOverlay(overlay: EmulatorOverlay) {
20 | activeOverlays.remove(overlay)
21 | if (activeOverlays.isEmpty()) {
22 | onOverlaysCleared()
23 | }
24 | }
25 |
26 | fun hasActiveOverlays(): Boolean {
27 | return activeOverlays.isNotEmpty()
28 | }
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/exceptions/RomLoadException.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator.exceptions
2 |
3 | class RomLoadException(message: String) : Exception(message)
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/exceptions/SaveSlotLoadException.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator.exceptions
2 |
3 | class SaveSlotLoadException(message: String) : Exception(message)
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/firmware/FirmwarePauseMenuOption.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator.firmware
2 |
3 | import me.magnum.melonds.R
4 | import me.magnum.melonds.ui.emulator.PauseMenuOption
5 |
6 | enum class FirmwarePauseMenuOption(override val textResource: Int) : PauseMenuOption {
7 | SETTINGS(R.string.settings),
8 | RESET(R.string.reset),
9 | EXIT(R.string.exit)
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/input/BaseInputHandler.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator.input
2 |
3 | import android.view.View.OnTouchListener
4 |
5 | abstract class BaseInputHandler(protected var inputListener: IInputListener) : OnTouchListener
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/input/ButtonsInputHandler.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator.input
2 |
3 | import me.magnum.melonds.common.vibration.TouchVibrator
4 | import me.magnum.melonds.domain.model.Input
5 |
6 | class ButtonsInputHandler(inputListener: IInputListener, enableHapticFeedback: Boolean, touchVibrator: TouchVibrator) : MultiButtonInputHandler(inputListener, enableHapticFeedback, touchVibrator) {
7 | override fun getTopInput() = Input.X
8 | override fun getLeftInput() = Input.Y
9 | override fun getBottomInput() = Input.B
10 | override fun getRightInput() = Input.A
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/input/DpadInputHandler.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator.input
2 |
3 | import me.magnum.melonds.common.vibration.TouchVibrator
4 | import me.magnum.melonds.domain.model.Input
5 |
6 | class DpadInputHandler(inputListener: IInputListener, enableHapticFeedback: Boolean, touchVibrator: TouchVibrator) : MultiButtonInputHandler(inputListener, enableHapticFeedback, touchVibrator) {
7 | override fun getTopInput() = Input.UP
8 | override fun getLeftInput() = Input.LEFT
9 | override fun getBottomInput() = Input.DOWN
10 | override fun getRightInput() = Input.RIGHT
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/input/IInputListener.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator.input
2 |
3 | import me.magnum.melonds.domain.model.Input
4 | import me.magnum.melonds.domain.model.Point
5 |
6 | interface IInputListener {
7 | fun onKeyPress(key: Input)
8 | fun onKeyReleased(key: Input)
9 | fun onTouch(point: Point)
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/input/INativeInputListener.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator.input
2 |
3 | import android.view.KeyEvent
4 | import android.view.MotionEvent
5 |
6 | interface INativeInputListener {
7 | fun onKeyEvent(keyEvent: KeyEvent): Boolean
8 | fun onMotionEvent(motionEvent: MotionEvent): Boolean
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/input/componentbuilder/RuntimeScreenLayoutComponentViewBuilder.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator.input.componentbuilder
2 |
3 | import android.content.Context
4 | import android.graphics.drawable.Drawable
5 | import me.magnum.melonds.ui.common.componentbuilders.ScreenLayoutComponentViewBuilder
6 |
7 | class RuntimeScreenLayoutComponentViewBuilder : ScreenLayoutComponentViewBuilder() {
8 | override fun getBackgroundDrawable(context: Context): Drawable? = null
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/input/view/ToggleableImageView.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator.input.view
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import androidx.appcompat.widget.AppCompatImageView
6 |
7 | class ToggleableImageView @JvmOverloads constructor(
8 | context: Context, attrs: AttributeSet? = null
9 | ) : AppCompatImageView(context, attrs) {
10 |
11 | var enabledDrawable: Int? = null
12 | var disabledDrawable: Int? = null
13 |
14 | fun setToggleState(enabled: Boolean) {
15 | val drawable = if (enabled) enabledDrawable else disabledDrawable
16 | if (drawable != null) {
17 | setImageResource(drawable)
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/model/EmulatorOverlay.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator.model
2 |
3 | enum class EmulatorOverlay {
4 | PAUSE_MENU,
5 | REWIND_WINDOW,
6 | SAVE_STATES_DIALOG,
7 | ROM_LOAD_ERROR_DIALOG,
8 | FIRMWARE_LOAD_ERROR_DIALOG,
9 | ROM_NOT_FOUND_DIALOG,
10 | SWITCH_NEW_ROM_DIALOG,
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/model/EmulatorState.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator.model
2 |
3 | import me.magnum.melonds.MelonEmulator
4 | import me.magnum.melonds.domain.model.ConsoleType
5 | import me.magnum.melonds.domain.model.rom.Rom
6 |
7 | sealed class EmulatorState {
8 | data object Uninitialized : EmulatorState()
9 | data object LoadingRom : EmulatorState()
10 | data object LoadingFirmware : EmulatorState()
11 | data class RunningRom(val rom: Rom) : EmulatorState()
12 | data class RunningFirmware(val console: ConsoleType) : EmulatorState()
13 | data object RomLoadError : EmulatorState()
14 | data class FirmwareLoadError(val reason: MelonEmulator.FirmwareLoadResult) : EmulatorState()
15 | data class RomNotFoundError(val romPath: String) : EmulatorState()
16 |
17 | fun isRunning() = this is RunningRom || this is RunningFirmware
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/model/PauseMenu.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator.model
2 |
3 | import me.magnum.melonds.ui.emulator.PauseMenuOption
4 |
5 | data class PauseMenu(val options: List)
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/model/PopupEvent.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator.model
2 |
3 | import me.magnum.rcheevosapi.model.RAAchievement
4 |
5 | sealed class PopupEvent {
6 | data class AchievementUnlockPopup(val achievement: RAAchievement) : PopupEvent()
7 | data class RAIntegrationPopup(val event: RAIntegrationEvent) : PopupEvent()
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/model/RAIntegrationEvent.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator.model
2 |
3 | import java.net.URL
4 |
5 | sealed class RAIntegrationEvent(open val icon: URL?) {
6 | data class Loaded(override val icon: URL?, val unlockedAchievements: Int, val totalAchievements: Int) : RAIntegrationEvent(icon)
7 | data class Failed(override val icon: URL?) : RAIntegrationEvent(icon)
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/model/RuntimeInputLayoutConfiguration.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator.model
2 |
3 | import me.magnum.melonds.domain.model.layout.LayoutConfiguration
4 | import me.magnum.melonds.domain.model.layout.UILayout
5 |
6 | data class RuntimeInputLayoutConfiguration(
7 | val showSoftInput: Boolean,
8 | val softInputOpacity: Int,
9 | val isHapticFeedbackEnabled: Boolean,
10 | val layoutOrientation: LayoutConfiguration.LayoutOrientation,
11 | val layout: UILayout,
12 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/model/RuntimeRendererConfiguration.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator.model
2 |
3 | import me.magnum.melonds.domain.model.VideoFiltering
4 |
5 | data class RuntimeRendererConfiguration(
6 | val videoFiltering: VideoFiltering,
7 | val resolutionScaling: Int,
8 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/model/ToastEvent.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator.model
2 |
3 | sealed class ToastEvent {
4 | data object GbaLoadFailed : ToastEvent()
5 | data object RewindNotEnabled : ToastEvent()
6 | data object RewindNotAvailableWhileRAHardcoreModeEnabled : ToastEvent()
7 | data object StateSaveFailed : ToastEvent()
8 | data object StateLoadFailed : ToastEvent()
9 | data object StateStateDoesNotExist : ToastEvent()
10 | data object QuickSaveSuccessful : ToastEvent()
11 | data object QuickLoadSuccessful : ToastEvent()
12 | data object CannotUseSaveStatesWhenRAHardcoreIsEnabled : ToastEvent()
13 | data object CannotSaveStateWhenRunningFirmware : ToastEvent()
14 | data object CannotLoadStateWhenRunningFirmware : ToastEvent()
15 | data object CannotSwitchRetroAchievementsMode : ToastEvent()
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/rewind/model/RewindSaveState.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator.rewind.model
2 |
3 | import android.graphics.Bitmap
4 | import me.magnum.melonds.utils.DsScreenshotConverter
5 | import java.nio.ByteBuffer
6 |
7 | class RewindSaveState(
8 | val buffer: ByteBuffer,
9 | val screenshotBuffer: ByteBuffer,
10 | val frame: Int,
11 | ) {
12 | val screenshot: Bitmap get() = DsScreenshotConverter.fromByteBufferToBitmap(screenshotBuffer)
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/rewind/model/RewindWindow.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator.rewind.model
2 |
3 | import java.time.Duration
4 | import java.util.*
5 |
6 | class RewindWindow(
7 | val currentEmulationFrame: Int,
8 | val rewindStates: ArrayList
9 | ) {
10 |
11 | companion object {
12 | private const val FRAMES_PER_SECOND = 60
13 | }
14 |
15 | fun getDeltaFromEmulationTimeToRewindState(state: RewindSaveState): Duration {
16 | val elapsedFrames = currentEmulationFrame - state.frame
17 | val elapsedMillis = elapsedFrames.toFloat() / FRAMES_PER_SECOND * 1000
18 | return Duration.ofMillis(elapsedMillis.toLong())
19 | }
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/emulator/rom/RomPauseMenuOption.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.emulator.rom
2 |
3 | import me.magnum.melonds.R
4 | import me.magnum.melonds.ui.emulator.PauseMenuOption
5 |
6 | enum class RomPauseMenuOption(override val textResource: Int) : PauseMenuOption {
7 | SETTINGS(R.string.settings),
8 | SAVE_STATE(R.string.save_state),
9 | LOAD_STATE(R.string.load_state),
10 | REWIND(R.string.rewind),
11 | CHEATS(R.string.cheats),
12 | VIEW_ACHIEVEMENTS(R.string.achievements),
13 | RESET(R.string.reset),
14 | EXIT(R.string.exit)
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/layouteditor/model/CurrentLayoutState.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.layouteditor.model
2 |
3 | import me.magnum.melonds.domain.model.layout.LayoutConfiguration
4 | import me.magnum.melonds.domain.model.layout.UILayout
5 |
6 | data class CurrentLayoutState(
7 | val layout: UILayout,
8 | val orientation: LayoutConfiguration.LayoutOrientation,
9 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/layouteditor/model/LayoutBackgroundProperties.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.layouteditor.model
2 |
3 | import me.magnum.melonds.domain.model.layout.BackgroundMode
4 | import java.util.UUID
5 |
6 | data class LayoutBackgroundProperties(
7 | val backgroundId: UUID?,
8 | val backgroundMode: BackgroundMode,
9 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/layouts/fragments/LayoutListFragment.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.layouts.fragments
2 |
3 | import androidx.fragment.app.activityViewModels
4 | import dagger.hilt.android.AndroidEntryPoint
5 | import me.magnum.melonds.domain.model.layout.LayoutConfiguration
6 | import me.magnum.melonds.ui.layouts.BaseLayoutsFragment
7 | import me.magnum.melonds.ui.layouts.BaseLayoutsViewModel
8 | import me.magnum.melonds.ui.layouts.LayoutsViewModel
9 | import java.util.*
10 |
11 | @AndroidEntryPoint
12 | class LayoutListFragment : BaseLayoutsFragment() {
13 | override fun getFragmentViewModel(): BaseLayoutsViewModel {
14 | return activityViewModels().value
15 | }
16 |
17 | override fun getFallbackLayoutId(): UUID {
18 | return LayoutConfiguration.DEFAULT_ID
19 | }
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/layouts/fragments/LayoutSelectorFragment.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.layouts.fragments
2 |
3 | import androidx.fragment.app.activityViewModels
4 | import dagger.hilt.android.AndroidEntryPoint
5 | import me.magnum.melonds.ui.layouts.BaseLayoutsFragment
6 | import me.magnum.melonds.ui.layouts.BaseLayoutsViewModel
7 | import me.magnum.melonds.ui.layouts.LayoutSelectorViewModel
8 | import java.util.*
9 |
10 | @AndroidEntryPoint
11 | class LayoutSelectorFragment : BaseLayoutsFragment() {
12 | override fun getFragmentViewModel(): BaseLayoutsViewModel {
13 | return activityViewModels().value
14 | }
15 |
16 | override fun getFallbackLayoutId(): UUID? {
17 | return null
18 | }
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/romdetails/model/RomAchievementsSummary.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.romdetails.model
2 |
3 | data class RomAchievementsSummary(
4 | val forHardcoreMode: Boolean,
5 | val totalAchievements: Int,
6 | val completedAchievements: Int,
7 | val totalPoints: Int,
8 | ) {
9 |
10 | val completedPercentage get() = (completedAchievements / totalAchievements.toFloat() * 100).toInt()
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/romdetails/model/RomConfigUiModel.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.romdetails.model
2 |
3 | import me.magnum.melonds.domain.model.rom.config.RuntimeConsoleType
4 | import me.magnum.melonds.domain.model.rom.config.RuntimeMicSource
5 | import java.util.UUID
6 |
7 | data class RomConfigUiModel(
8 | val runtimeConsoleType: RuntimeConsoleType = RuntimeConsoleType.DEFAULT,
9 | val runtimeMicSource: RuntimeMicSource = RuntimeMicSource.DEFAULT,
10 | val layoutId: UUID? = null,
11 | val layoutName: String? = null,
12 | val gbaSlotConfig: RomGbaSlotConfigUiModel = RomGbaSlotConfigUiModel()
13 | )
14 |
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/romdetails/model/RomConfigUiState.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.romdetails.model
2 |
3 | sealed class RomConfigUiState {
4 | object Loading : RomConfigUiState()
5 | data class Ready(val romConfigUiModel: RomConfigUiModel) : RomConfigUiState()
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/romdetails/model/RomConfigUpdateEvent.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.romdetails.model
2 |
3 | import android.net.Uri
4 | import me.magnum.melonds.domain.model.rom.config.RuntimeConsoleType
5 | import me.magnum.melonds.domain.model.rom.config.RuntimeMicSource
6 | import java.util.UUID
7 |
8 | sealed class RomConfigUpdateEvent {
9 | data class RuntimeConsoleUpdate(val newRuntimeConsole: RuntimeConsoleType) : RomConfigUpdateEvent()
10 | data class RuntimeMicSourceUpdate(val newRuntimeMicSource: RuntimeMicSource) : RomConfigUpdateEvent()
11 | data class LayoutUpdate(val newLayoutId: UUID?) : RomConfigUpdateEvent()
12 | data class GbaSlotTypeUpdated(val type: RomGbaSlotConfigUiModel.Type) : RomConfigUpdateEvent()
13 | data class GbaRomPathUpdate(val gbaRomPath: Uri?) : RomConfigUpdateEvent()
14 | data class GbaSavePathUpdate(val gbaSavePath: Uri?) : RomConfigUpdateEvent()
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/romdetails/model/RomDetailsTab.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.romdetails.model
2 |
3 | enum class RomDetailsTab(val tabIndex: Int) {
4 | CONFIG(0),
5 | RETRO_ACHIEVEMENTS(1),
6 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/romdetails/model/RomGbaSlotConfigUiModel.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.romdetails.model
2 |
3 | data class RomGbaSlotConfigUiModel(
4 | val type: Type = Type.None,
5 | val gbaRomPath: String? = null,
6 | val gbaSavePath: String? = null,
7 | ) {
8 |
9 | enum class Type {
10 | None, GbaRom, MemoryExpansion
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/romdetails/model/RomRetroAchievementsUiState.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.romdetails.model
2 |
3 | import me.magnum.melonds.domain.model.retroachievements.RAUserAchievement
4 |
5 | sealed class RomRetroAchievementsUiState {
6 | object LoggedOut : RomRetroAchievementsUiState()
7 | object Loading : RomRetroAchievementsUiState()
8 | data class Ready(val achievements: List, val summary: RomAchievementsSummary) : RomRetroAchievementsUiState()
9 | object LoginError : RomRetroAchievementsUiState()
10 | object AchievementLoadError : RomRetroAchievementsUiState()
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/romlist/RomIcon.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.romlist
2 |
3 | import android.graphics.Bitmap
4 | import me.magnum.melonds.domain.model.RomIconFiltering
5 |
6 | data class RomIcon(val bitmap: Bitmap?, val filtering: RomIconFiltering)
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/settings/PreferenceFragmentTitleProvider.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.settings
2 |
3 | interface PreferenceFragmentTitleProvider {
4 | fun getTitle(): String
5 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/settings/fragments/MainPreferencesFragment.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.settings.fragments
2 |
3 | import android.os.Bundle
4 | import androidx.preference.PreferenceFragmentCompat
5 | import dagger.hilt.android.AndroidEntryPoint
6 | import me.magnum.melonds.R
7 | import me.magnum.melonds.ui.settings.PreferenceFragmentTitleProvider
8 |
9 | @AndroidEntryPoint
10 | class MainPreferencesFragment : PreferenceFragmentCompat(), PreferenceFragmentTitleProvider {
11 |
12 | override fun getTitle() = getString(R.string.settings)
13 |
14 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
15 | setPreferencesFromResource(R.xml.pref_main, rootKey)
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/settings/model/RetroAchievementsAccountState.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.settings.model
2 |
3 | sealed class RetroAchievementsAccountState {
4 | object Unknown : RetroAchievementsAccountState()
5 | object LoggedOut : RetroAchievementsAccountState()
6 | data class LoggedIn(val accountName: String) : RetroAchievementsAccountState()
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/shortcutsetup/DSFirmwareShortcutSetupActivity.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.shortcutsetup
2 |
3 | import me.magnum.melonds.domain.model.ConsoleType
4 |
5 | class DSFirmwareShortcutSetupActivity : FirmwareShortcutSetupActivity() {
6 | override fun getConsoleType(): ConsoleType {
7 | return ConsoleType.DS
8 | }
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/shortcutsetup/DSiFirmwareShortcutSetupActivity.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.shortcutsetup
2 |
3 | import me.magnum.melonds.domain.model.ConsoleType
4 |
5 | class DSiFirmwareShortcutSetupActivity : FirmwareShortcutSetupActivity() {
6 | override fun getConsoleType(): ConsoleType {
7 | return ConsoleType.DSi
8 | }
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/theme/MelonTheme.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.theme
2 |
3 | import androidx.compose.foundation.isSystemInDarkTheme
4 | import androidx.compose.material.MaterialTheme
5 | import androidx.compose.runtime.Composable
6 |
7 | @Composable
8 | fun MelonTheme(
9 | isDarkTheme: Boolean = isSystemInDarkTheme(),
10 | content: @Composable () -> Unit
11 | ) {
12 | val colors = if (isDarkTheme) DarkMelonColors else LightMelonColors
13 |
14 | MaterialTheme(
15 | colors = colors,
16 | typography = MelonTypography,
17 | ) {
18 | content()
19 | }
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/ui/theme/Typography.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.ui.theme
2 |
3 | import androidx.compose.material.MaterialTheme
4 | import androidx.compose.material.Typography
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.text.font.FontWeight
7 |
8 | val MelonTypography @Composable get() = Typography(
9 | button = MaterialTheme.typography.button.copy(fontWeight = FontWeight.Bold),
10 | )
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/utils/BitmapRegionDecoderCompat.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.utils
2 |
3 | import android.graphics.BitmapRegionDecoder
4 | import android.os.Build
5 | import java.io.InputStream
6 |
7 | object BitmapRegionDecoderCompat {
8 | fun newInstance(inputStream: InputStream): BitmapRegionDecoder? {
9 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
10 | BitmapRegionDecoder.newInstance(inputStream)
11 | } else {
12 | BitmapRegionDecoder.newInstance(inputStream, true)
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/utils/CompositeOnPreferenceChangeListener.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.utils
2 |
3 | import androidx.preference.Preference
4 |
5 | class CompositeOnPreferenceChangeListener : Preference.OnPreferenceChangeListener {
6 | private val listeners = mutableListOf()
7 |
8 | override fun onPreferenceChange(preference: Preference, newValue: Any?): Boolean {
9 | val allListenersReturnedTrue = listeners.all { it.onPreferenceChange(preference, newValue) }
10 | return allListenersReturnedTrue
11 | }
12 |
13 | fun addOnPreferenceChangeListener(listener: Preference.OnPreferenceChangeListener) {
14 | listeners.add(listener)
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/utils/EnumUtils.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.utils
2 |
3 | fun > findEnumValueIgnoreCase(enumValues: Array, value: String): T {
4 | enumValues.forEach {
5 | if (it.name.equals(value, true))
6 | return it
7 | }
8 | throw IllegalArgumentException("Value $value does not represent an enum entry")
9 | }
10 |
11 | inline fun enumValueOfIgnoreCase(value: String): T where T : Enum {
12 | return findEnumValueIgnoreCase(enumValues(), value)
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/utils/PackageManagerCompat.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.utils
2 |
3 | import android.content.pm.PackageInfo
4 | import android.content.pm.PackageManager
5 | import android.os.Build
6 |
7 | object PackageManagerCompat {
8 |
9 | fun getPackageInfo(packageManager: PackageManager, packageName: String, flags: Int): PackageInfo {
10 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
11 | packageManager.getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(flags.toLong()))
12 | } else {
13 | packageManager.getPackageInfo(packageName, flags)
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/utils/SharedFlow.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.utils
2 |
3 | import kotlinx.coroutines.channels.BufferOverflow
4 | import kotlinx.coroutines.flow.MutableSharedFlow
5 |
6 | /**
7 | * Creates a [MutableSharedFlow] that holds a single value and has no initial value.
8 | */
9 | @Suppress("FunctionName")
10 | fun SubjectSharedFlow() = MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
11 |
12 | /**
13 | * Creates a [MutableSharedFlow] that doesn't hold any value. Suitable to create flows used to fire events.
14 | */
15 | @Suppress("FunctionName")
16 | fun EventSharedFlow() = MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/utils/SimpleDiffCallback.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.utils
2 |
3 | import androidx.recyclerview.widget.DiffUtil
4 |
5 | abstract class SimpleDiffCallback(private val oldList: List, private val newList: List) : DiffUtil.Callback() {
6 | override fun getOldListSize() = oldList.size
7 |
8 | override fun getNewListSize() = newList.size
9 |
10 | override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
11 | return areItemsTheSame(oldList[oldItemPosition], newList[newItemPosition])
12 | }
13 |
14 | override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
15 | return oldList[oldItemPosition] == newList[newItemPosition]
16 | }
17 |
18 | abstract fun areItemsTheSame(old: T, new: T): Boolean
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/magnum/melonds/utils/UriTypeHierarchyAdapter.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.utils
2 |
3 | import android.net.Uri
4 | import androidx.core.net.toUri
5 | import com.google.gson.*
6 | import java.lang.reflect.Type
7 | import kotlin.jvm.Throws
8 |
9 |
10 | class UriTypeHierarchyAdapter : JsonDeserializer, JsonSerializer {
11 | @Throws(JsonParseException::class)
12 | override fun deserialize(json: JsonElement, typeOfT: Type?, context: JsonDeserializationContext?): Uri {
13 | return json.asString.toUri()
14 | }
15 |
16 | override fun serialize(src: Uri?, typeOfSrc: Type?, context: JsonSerializationContext?): JsonElement {
17 | return JsonPrimitive(src.toString())
18 | }
19 | }
--------------------------------------------------------------------------------
/app/src/main/res/anim/fragment_translate_enter_pop.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/fragment_translate_enter_push.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/fragment_translate_exit_pop.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/fragment_translate_exit_push.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/rotate.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-night/logo_dsiware.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable-night/logo_dsiware.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/logo_splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable-xhdpi/logo_splash.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/background_bottom_screen.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/background_rewind_save_state_focused.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/background_top_screen.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/background_uiview.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_fast_forward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/button_fast_forward.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_fast_forward_disabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/button_fast_forward_disabled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_l.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/button_l.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_microphone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/button_microphone.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_microphone_disabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/button_microphone_disabled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/button_pause.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_quick_load.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/button_quick_load.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_quick_save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/button_quick_save.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_r.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/button_r.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_reset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/button_reset.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_rewind.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/button_rewind.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_select.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/button_select.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_start.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/button_start.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_swap_screens.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/button_swap_screens.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_toggle_lid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/button_toggle_lid.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/buttons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/buttons.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/drawable_ripple.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/drawable_ripple_small.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_add.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_audio.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_block.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_cheat.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_clear.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_clock.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_completed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/ic_completed.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_file.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_firmware.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_folder.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_gba_cart.xml:
--------------------------------------------------------------------------------
1 |
3 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_input.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_link.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_melon_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/ic_melon_small.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_points.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/ic_points.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_refresh.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_search.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_sort.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_sort_alpha.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_status_error.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_status_ok.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_status_warn.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_touch_disabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/ic_touch_disabled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_touch_enabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/ic_touch_enabled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_trophy.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_video.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/keypad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/keypad.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/logo_dsiware.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/drawable/logo_dsiware.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/selector_rewind_save_state.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/selector_rewind_save_state_time_text.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_layouts.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_rom_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_shortcut_setup.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_loading.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_text_input.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_layout_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_rom_simple.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/preference_directory_picker_status.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/preference_firmware_colour_picker_colour.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/layout_item_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/layouts_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_platform_ds.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_platform_dsi.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_platform_ds_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/mipmap-hdpi/ic_platform_ds_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_platform_dsi_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/mipmap-hdpi/ic_platform_dsi_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_platform_ds_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/mipmap-mdpi/ic_platform_ds_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_platform_dsi_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/mipmap-mdpi/ic_platform_dsi_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_platform_ds_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/mipmap-xhdpi/ic_platform_ds_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_platform_dsi_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/mipmap-xhdpi/ic_platform_dsi_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_platform_ds_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/mipmap-xxhdpi/ic_platform_ds_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_platform_dsi_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/mipmap-xxhdpi/ic_platform_dsi_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_platform_ds_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/mipmap-xxxhdpi/ic_platform_ds_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_platform_dsi_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/main/res/mipmap-xxxhdpi/ic_platform_dsi_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/values-night/values.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v29/strings_theme.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - @string/theme_option_light
5 | - @string/theme_option_dark
6 | - @string/theme_option_system_default
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens_input.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 140dp
4 | 150dp
5 | 60dp
6 | 40dp
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings_theme.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - @string/theme_option_light
5 | - @string/theme_option_dark
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/values.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 | 250
5 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/game_mode_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | nus.cdn.t.shop.nintendowifi.net
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/pref_cheats.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/pref_general_updates.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/nightly/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/nightly/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/nightly/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/nightly/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/nightly/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/nightly/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/nightly/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/nightly/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/nightly/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/nightly/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/nightly/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/nightly/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/nightly/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/nightly/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/nightly/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/nightly/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/nightly/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/nightly/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/nightly/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/app/src/nightly/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/nightly/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | melonDS Nightly
4 |
--------------------------------------------------------------------------------
/app/src/playStore/java/me/magnum/melonds/di/PlayStoreModule.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.di
2 |
3 | import dagger.Module
4 | import dagger.Provides
5 | import dagger.hilt.InstallIn
6 | import dagger.hilt.components.SingletonComponent
7 | import me.magnum.melonds.domain.repositories.UpdatesRepository
8 | import me.magnum.melonds.domain.services.UpdateInstallManager
9 | import me.magnum.melonds.playstore.PlayStoreUpdatesRepository
10 | import me.magnum.melonds.services.PlayStoreUpdateInstallManager
11 | import javax.inject.Singleton
12 |
13 | @Module
14 | @InstallIn(SingletonComponent::class)
15 | object PlayStoreModule {
16 | @Provides
17 | @Singleton
18 | fun provideUpdatesRepository(): UpdatesRepository {
19 | return PlayStoreUpdatesRepository()
20 | }
21 |
22 | @Provides
23 | @Singleton
24 | fun provideUpdateInstallManager(): UpdateInstallManager {
25 | return PlayStoreUpdateInstallManager()
26 | }
27 | }
--------------------------------------------------------------------------------
/app/src/playStore/java/me/magnum/melonds/playstore/PlayStoreUpdatesRepository.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.playstore
2 |
3 | import me.magnum.melonds.domain.model.appupdate.AppUpdate
4 | import me.magnum.melonds.domain.repositories.UpdatesRepository
5 |
6 | class PlayStoreUpdatesRepository : UpdatesRepository {
7 | override suspend fun checkNewUpdate(): Result {
8 | return Result.success(null)
9 | }
10 |
11 | override fun skipUpdate(update: AppUpdate) {
12 | // Do nothing. Update checking not supported in the Play Store version
13 | }
14 |
15 | override fun notifyUpdateDownloaded(update: AppUpdate) {
16 | // Do nothing
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/playStore/java/me/magnum/melonds/services/PlayStoreUpdateInstallManager.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.services
2 |
3 | import kotlinx.coroutines.flow.Flow
4 | import kotlinx.coroutines.flow.emptyFlow
5 | import me.magnum.melonds.domain.model.DownloadProgress
6 | import me.magnum.melonds.domain.model.appupdate.AppUpdate
7 | import me.magnum.melonds.domain.services.UpdateInstallManager
8 |
9 | class PlayStoreUpdateInstallManager : UpdateInstallManager {
10 | override fun downloadAndInstallUpdate(update: AppUpdate): Flow {
11 | return emptyFlow()
12 | }
13 | }
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.application) apply false
3 | alias(libs.plugins.android.library) apply false
4 | alias(libs.plugins.compose.compiler) apply false
5 | alias(libs.plugins.hilt.android) apply false
6 | alias(libs.plugins.kotlin.android) apply false
7 | alias(libs.plugins.kotlin.jvm) apply false
8 | alias(libs.plugins.kotlin.parcelize) apply false
9 | alias(libs.plugins.kotlin.serialization) apply false
10 | alias(libs.plugins.ksp) apply false
11 | }
12 |
13 | tasks.register("clean", Delete::class) {
14 | delete(rootProject.layout.buildDirectory)
15 | }
16 |
--------------------------------------------------------------------------------
/buildSrc/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/buildSrc/build.gradle.kts:
--------------------------------------------------------------------------------
1 | repositories {
2 | mavenCentral()
3 | }
4 |
5 | plugins {
6 | `kotlin-dsl`
7 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/AppConfig.kt:
--------------------------------------------------------------------------------
1 | object AppConfig {
2 | const val compileSdkVersion = 35
3 | const val targetSdkVersion = compileSdkVersion
4 | const val minSdkVersion = 24
5 | const val ndkVersion = "28.0.13004108"
6 |
7 | const val versionCode = 34
8 | const val versionName = "Beta 1.10.0"
9 | }
--------------------------------------------------------------------------------
/common/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/common/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.kotlin.jvm)
3 | }
4 |
5 | kotlin {
6 | jvmToolchain(21)
7 | }
8 |
9 | dependencies {
10 | implementation(libs.kotlin.coroutines)
11 | }
--------------------------------------------------------------------------------
/common/src/main/java/me/magnum/melonds/common/SuspendRunCatching.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.melonds.common
2 |
3 | import kotlinx.coroutines.ensureActive
4 | import kotlin.coroutines.coroutineContext
5 |
6 | suspend inline fun suspendRunCatching(block: () -> R): Result {
7 | return try {
8 | Result.success(block())
9 | } catch (e: Throwable) {
10 | coroutineContext.ensureActive()
11 | Result.failure(e)
12 | }
13 | }
14 |
15 | suspend inline fun Result.suspendRecoverCatching(transform: (exception: Throwable) -> R): Result {
16 | return when (val exception = exceptionOrNull()) {
17 | null -> this
18 | else -> suspendRunCatching { transform(exception) }
19 | }
20 | }
21 |
22 | suspend inline fun Result.suspendMapCatching(transform: (value: T) -> R): Result {
23 | return when {
24 | isSuccess -> suspendRunCatching { transform(getOrThrow()) }
25 | else -> Result.failure(exceptionOrNull()!!)
26 | }
27 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 |
15 | android.useAndroidX=true
16 | android.enableR8.fullMode=false
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelvcaetano/melonDS-android/7e31d08a3e0e3801c0aca393832238395ac63983/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Nov 02 16:55:59 GMT 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
7 |
--------------------------------------------------------------------------------
/masterswitch/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | }
5 |
6 | android {
7 | namespace = "com.smp.masterswitchpreference"
8 | compileSdk = AppConfig.compileSdkVersion
9 |
10 | defaultConfig {
11 | minSdk = AppConfig.minSdkVersion
12 | }
13 |
14 | buildTypes {
15 | getByName("release") {
16 | isMinifyEnabled = true
17 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
18 | }
19 | }
20 | compileOptions {
21 | sourceCompatibility = JavaVersion.VERSION_21
22 | targetCompatibility = JavaVersion.VERSION_21
23 |
24 | kotlin {
25 | jvmToolchain(21)
26 | }
27 | }
28 | }
29 |
30 | dependencies {
31 | implementation(libs.androidx.appcompat)
32 | implementation(libs.androidx.core)
33 | implementation(libs.androidx.preference)
34 | }
35 |
--------------------------------------------------------------------------------
/masterswitch/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/masterswitch/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/masterswitch/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #fafafa
5 | #4dfafafa
6 | #00796b
7 | #757575
8 |
--------------------------------------------------------------------------------
/masterswitch/src/main/res/xml/blank_preference_screen.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/masterswitch/src/main/res/xml/explanation_preference_screen.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/rcheevos-api/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/rcheevos-api/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.kotlin.jvm)
3 | alias(libs.plugins.kotlin.serialization)
4 | }
5 |
6 | kotlin {
7 | jvmToolchain(21)
8 | }
9 |
10 | dependencies {
11 | implementation(projects.common)
12 |
13 | implementation(libs.kotlin.serialization)
14 | implementation(libs.okhttp)
15 | }
--------------------------------------------------------------------------------
/rcheevos-api/src/main/java/me/magnum/rcheevosapi/RAAchievementSignatureProvider.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.rcheevosapi
2 |
3 | import me.magnum.rcheevosapi.model.RAUserAuth
4 |
5 | interface RAAchievementSignatureProvider {
6 | fun provideAchievementSignature(achievementId: Long, userAuth: RAUserAuth, forHardcoreMode: Boolean): String
7 | }
--------------------------------------------------------------------------------
/rcheevos-api/src/main/java/me/magnum/rcheevosapi/RAUserAuthStore.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.rcheevosapi
2 |
3 | import me.magnum.rcheevosapi.model.RAUserAuth
4 |
5 | interface RAUserAuthStore {
6 | suspend fun storeUserAuth(userAuth: RAUserAuth)
7 | suspend fun getUserAuth(): RAUserAuth?
8 | suspend fun clearUserAuth()
9 | }
--------------------------------------------------------------------------------
/rcheevos-api/src/main/java/me/magnum/rcheevosapi/dto/AchievementDto.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.rcheevosapi.dto
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | internal data class AchievementDto(
8 | @SerialName("ID")
9 | val id: Long,
10 | @SerialName("NumAwarded")
11 | val numAwarded: Int?,
12 | @SerialName("NumAwardedHardcore")
13 | val numAwardedHardcore: Int?,
14 | @SerialName("Title")
15 | val title: String,
16 | @SerialName("Description")
17 | val description: String,
18 | @SerialName("Points")
19 | val points: Int,
20 | @SerialName("Flags")
21 | val flags: Int,
22 | @SerialName("BadgeURL")
23 | val badgeUrl: String,
24 | @SerialName("BadgeLockedURL")
25 | val badgeUrlLocked: String,
26 | @SerialName("DisplayOrder")
27 | val displayOrder: String?,
28 | @SerialName("MemAddr")
29 | val memoryAddress: String,
30 | )
31 |
--------------------------------------------------------------------------------
/rcheevos-api/src/main/java/me/magnum/rcheevosapi/dto/GameDto.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.rcheevosapi.dto
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | internal data class GameDto(
8 | @SerialName("ID")
9 | val id: Long,
10 | @SerialName("Title")
11 | val title: String,
12 | @SerialName("ImageIconURL")
13 | val iconUrl: String,
14 | @SerialName("RichPresencePatch")
15 | val richPresencePatch: String?,
16 | @SerialName("Achievements")
17 | val achievements: List,
18 | )
19 |
--------------------------------------------------------------------------------
/rcheevos-api/src/main/java/me/magnum/rcheevosapi/dto/GamePatchDto.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.rcheevosapi.dto
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | internal data class GamePatchDto(
8 | @SerialName("PatchData")
9 | val game: GameDto,
10 | )
11 |
--------------------------------------------------------------------------------
/rcheevos-api/src/main/java/me/magnum/rcheevosapi/dto/HashLibraryDto.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.rcheevosapi.dto
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | internal data class HashLibraryDto(
8 | @SerialName("MD5List")
9 | val md5List: Map,
10 | )
11 |
--------------------------------------------------------------------------------
/rcheevos-api/src/main/java/me/magnum/rcheevosapi/dto/UserLoginDto.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.rcheevosapi.dto
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | internal data class UserLoginDto(
8 | @SerialName("Token")
9 | val token: String,
10 | @SerialName("Score")
11 | val score: Long,
12 | @SerialName("SoftcoreScore")
13 | val softcoreScore: Long,
14 | )
15 |
--------------------------------------------------------------------------------
/rcheevos-api/src/main/java/me/magnum/rcheevosapi/dto/UserUnlocksDto.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.rcheevosapi.dto
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | internal data class UserUnlocksDto(
8 | @SerialName("UserUnlocks")
9 | val userUnlocks: List,
10 | )
--------------------------------------------------------------------------------
/rcheevos-api/src/main/java/me/magnum/rcheevosapi/dto/mapper/GameDtoMapper.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.rcheevosapi.dto.mapper
2 |
3 | import me.magnum.rcheevosapi.dto.GameDto
4 | import me.magnum.rcheevosapi.model.RAGame
5 | import me.magnum.rcheevosapi.model.RAGameId
6 | import java.net.URI
7 |
8 | internal fun GameDto.mapToModel(): RAGame {
9 | val gameId = RAGameId(id)
10 | return RAGame(
11 | id = gameId,
12 | title = title,
13 | icon = URI(iconUrl).toURL(),
14 | richPresencePatch = richPresencePatch,
15 | achievements = achievements.map {
16 | it.mapToModel(gameId)
17 | },
18 | )
19 | }
--------------------------------------------------------------------------------
/rcheevos-api/src/main/java/me/magnum/rcheevosapi/exception/UnsuccessfulRequestException.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.rcheevosapi.exception
2 |
3 | class UnsuccessfulRequestException(reason: String) : Exception(reason)
--------------------------------------------------------------------------------
/rcheevos-api/src/main/java/me/magnum/rcheevosapi/exception/UserNotAuthenticatedException.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.rcheevosapi.exception
2 |
3 | class UserNotAuthenticatedException : Exception("The user is not authenticated to RetroAchievements")
--------------------------------------------------------------------------------
/rcheevos-api/src/main/java/me/magnum/rcheevosapi/model/RAAchievement.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.rcheevosapi.model
2 |
3 | import java.net.URL
4 |
5 | data class RAAchievement(
6 | val id: Long,
7 | val gameId: RAGameId,
8 | val totalAwardsCasual: Int?,
9 | val totalAwardsHardcore: Int?,
10 | val title: String,
11 | val description: String,
12 | val points: Int,
13 | val displayOrder: Int,
14 | val badgeUrlUnlocked: URL,
15 | val badgeUrlLocked: URL,
16 | val memoryAddress: String,
17 | val type: Type,
18 | ) {
19 |
20 | enum class Type {
21 | CORE,
22 | UNOFFICIAL
23 | }
24 |
25 | fun getCleanTitle(): String {
26 | return title.removeSuffix("[m]").trim()
27 | }
28 |
29 | fun isMissable(): Boolean {
30 | return title.endsWith("[m]")
31 | }
32 | }
--------------------------------------------------------------------------------
/rcheevos-api/src/main/java/me/magnum/rcheevosapi/model/RAGame.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.rcheevosapi.model
2 |
3 | import java.net.URL
4 |
5 | data class RAGame(
6 | val id: RAGameId,
7 | val title: String,
8 | val icon: URL,
9 | val richPresencePatch: String?,
10 | val achievements: List,
11 | )
--------------------------------------------------------------------------------
/rcheevos-api/src/main/java/me/magnum/rcheevosapi/model/RAGameId.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.rcheevosapi.model
2 |
3 | @JvmInline
4 | value class RAGameId(val id: Long)
--------------------------------------------------------------------------------
/rcheevos-api/src/main/java/me/magnum/rcheevosapi/model/RAUserAuth.kt:
--------------------------------------------------------------------------------
1 | package me.magnum.rcheevosapi.model
2 |
3 | data class RAUserAuth(
4 | val username: String,
5 | val token: String,
6 | )
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | mavenCentral()
6 | }
7 | }
8 |
9 | dependencyResolutionManagement {
10 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
11 | repositories {
12 | google()
13 | mavenCentral()
14 | }
15 | }
16 |
17 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
18 |
19 | include(":app")
20 | include(":common")
21 | include(":masterswitch")
22 | include(":rcheevos-api")
--------------------------------------------------------------------------------