From 5d6f67c612fd5f4d593209ccdffd023d42b43988 Mon Sep 17 00:00:00 2001 From: Gericom Date: Sat, 22 Nov 2025 17:21:45 +0100 Subject: [PATCH] Initial commit --- .gitattributes | 1 + .github/workflows/nightly.yml | 37 + .gitignore | 49 + .gitmodules | 3 + LICENSE.txt | 17 + Makefile | 110 + Makefile.arm7 | 189 + Makefile.arm9 | 243 + README.md | 81 + _pico/themes/material/theme.json | 12 + _pico/themes/raspberry/bannerListCell.bin | Bin 0 -> 12544 bytes _pico/themes/raspberry/bannerListCellPltt.bin | Bin 0 -> 64 bytes .../raspberry/bannerListCellSelected.bin | Bin 0 -> 12544 bytes .../raspberry/bannerListCellSelectedPltt.bin | Bin 0 -> 64 bytes _pico/themes/raspberry/bottombg.bin | 641 + _pico/themes/raspberry/gridcell.bin | Bin 0 -> 4096 bytes _pico/themes/raspberry/gridcellPltt.bin | Bin 0 -> 64 bytes _pico/themes/raspberry/gridcellSelected.bin | Bin 0 -> 4096 bytes .../themes/raspberry/gridcellSelectedPltt.bin | Bin 0 -> 64 bytes _pico/themes/raspberry/scrim.bin | Bin 0 -> 336 bytes _pico/themes/raspberry/scrimPltt.bin | Bin 0 -> 16 bytes _pico/themes/raspberry/theme.json | 12 + _pico/themes/raspberry/topbg.bin | 20 + arm7/dldi_ds_arm7.ld | 340 + arm7/dldi_ds_arm7.specs | 23 + arm7/source/Arm7State.h | 8 + arm7/source/ExitMode.h | 12 + arm7/source/common.h | 2 + arm7/source/dldi.s | 16 + arm7/source/ipcServices/DldiIpcService.cpp | 46 + arm7/source/ipcServices/DldiIpcService.h | 19 + arm7/source/ipcServices/DsiSdIpcService.h | 19 + .../ipcServices/DsiSdIpcService.twl.cpp | 36 + arm7/source/ipcServices/RtcIpcService.cpp | 9 + arm7/source/ipcServices/RtcIpcService.h | 14 + arm7/source/ipcServices/SoundIpcService.cpp | 49 + arm7/source/ipcServices/SoundIpcService.h | 12 + arm7/source/main.cpp | 241 + arm7/source/mmc/mmc_spec.h | 235 + arm7/source/mmc/sd_spec.h | 192 + arm7/source/mmc/sdmmc.h | 240 + arm7/source/mmc/sdmmc.twl.c | 751 + arm7/source/mmc/tmio.h | 414 + arm7/source/mmc/tmio.twl.c | 279 + arm7/source/picoLoaderBootstrap.cpp | 39 + arm7/source/picoLoaderBootstrap.h | 5 + arm9/data/NotoSansJP-Medium-10.nft2 | Bin 0 -> 17852 bytes arm9/data/NotoSansJP-Medium-11.nft2 | Bin 0 -> 20220 bytes arm9/data/NotoSansJP-Medium-7_5.nft2 | Bin 0 -> 12432 bytes arm9/data/NotoSansJP-Regular-10.nft2 | Bin 0 -> 17508 bytes arm9/gfx/backIcon.grit | 7 + arm9/gfx/backIcon.png | Bin 0 -> 172 bytes arm9/gfx/bannerListIcon.grit | 7 + arm9/gfx/bannerListIcon.png | Bin 0 -> 184 bytes arm9/gfx/bannerListItemBg0.grit | 5 + arm9/gfx/bannerListItemBg0.png | Bin 0 -> 256 bytes arm9/gfx/bannerListItemBg1.grit | 7 + arm9/gfx/bannerListItemBg1.png | Bin 0 -> 187 bytes arm9/gfx/bannerListItemBg2.grit | 7 + arm9/gfx/bannerListItemBg2.png | Bin 0 -> 248 bytes arm9/gfx/bottomSheetBg.grit | 18 + arm9/gfx/bottomSheetBg.png | Bin 0 -> 438 bytes arm9/gfx/carouselMask.grit | 3 + arm9/gfx/carouselMask.png | Bin 0 -> 1221 bytes arm9/gfx/chipFilled.grit | 5 + arm9/gfx/chipFilled.png | Bin 0 -> 197 bytes arm9/gfx/coverflowIcon.grit | 7 + arm9/gfx/coverflowIcon.png | Bin 0 -> 186 bytes arm9/gfx/folderCover.grit | 3 + arm9/gfx/folderCover.png | Bin 0 -> 1040 bytes arm9/gfx/gamesIcon.grit | 7 + arm9/gfx/gamesIcon.png | Bin 0 -> 227 bytes arm9/gfx/hGridIcon.grit | 7 + arm9/gfx/hGridIcon.png | Bin 0 -> 173 bytes arm9/gfx/heartIcon.grit | 7 + arm9/gfx/heartIcon.png | Bin 0 -> 226 bytes arm9/gfx/iconButtonSelector.grit | 7 + arm9/gfx/iconButtonSelector.png | Bin 0 -> 266 bytes arm9/gfx/iconButtonSelectorTexture.grit | 3 + arm9/gfx/iconButtonSelectorTexture.png | Bin 0 -> 922 bytes arm9/gfx/iconCell.grit | 7 + arm9/gfx/iconCell.png | Bin 0 -> 242 bytes arm9/gfx/iconCell2.grit | 5 + arm9/gfx/iconCell2.png | Bin 0 -> 368 bytes arm9/gfx/iconCell3.grit | 5 + arm9/gfx/iconCell3.png | Bin 0 -> 325 bytes arm9/gfx/largeDSCardIcon.grit | 7 + arm9/gfx/largeDSCardIcon.png | Bin 0 -> 203 bytes arm9/gfx/largeFileIcon.grit | 7 + arm9/gfx/largeFileIcon.png | Bin 0 -> 4189 bytes arm9/gfx/largeFolderIcon.grit | 7 + arm9/gfx/largeFolderIcon.png | Bin 0 -> 204 bytes arm9/gfx/listIcon.grit | 7 + arm9/gfx/listIcon.png | Bin 0 -> 160 bytes arm9/gfx/mainBg.grit | 13 + arm9/gfx/mainBg.png | Bin 0 -> 631 bytes arm9/gfx/moviesIcon.grit | 7 + arm9/gfx/moviesIcon.png | Bin 0 -> 203 bytes arm9/gfx/musicIcon.grit | 7 + arm9/gfx/musicIcon.png | Bin 0 -> 206 bytes arm9/gfx/picturesIcon.grit | 7 + arm9/gfx/picturesIcon.png | Bin 0 -> 215 bytes arm9/gfx/recentIcon.grit | 7 + arm9/gfx/recentIcon.png | Bin 0 -> 222 bytes arm9/gfx/scrim.grit | 18 + arm9/gfx/scrim.png | Bin 0 -> 285 bytes arm9/gfx/settingsIcon.grit | 7 + arm9/gfx/settingsIcon.png | Bin 0 -> 216 bytes arm9/gfx/smallHeartIcon.grit | 7 + arm9/gfx/smallHeartIcon.png | Bin 0 -> 207 bytes arm9/gfx/smallHeartIconFilled.grit | 7 + arm9/gfx/smallHeartIconFilled.png | Bin 0 -> 207 bytes arm9/gfx/sortNameAscendingIcon.grit | 7 + arm9/gfx/sortNameAscendingIcon.png | Bin 0 -> 204 bytes arm9/gfx/sortNameDescendingIcon.grit | 7 + arm9/gfx/sortNameDescendingIcon.png | Bin 0 -> 204 bytes arm9/gfx/splashTop.grit | 6 + arm9/gfx/splashTop.png | Bin 0 -> 4235 bytes arm9/gfx/unknownCover.grit | 3 + arm9/gfx/unknownCover.png | Bin 0 -> 1257 bytes arm9/gfx/unknownIcon.grit | 7 + arm9/gfx/unknownIcon.png | Bin 0 -> 227 bytes arm9/gfx/vGridIcon.grit | 7 + arm9/gfx/vGridIcon.png | Bin 0 -> 179 bytes arm9/source/App.cpp | 519 + arm9/source/App.h | 129 + arm9/source/DialogPresenter.cpp | 155 + arm9/source/DialogPresenter.h | 76 + arm9/source/PicoLoaderProcess.cpp | 13 + arm9/source/PicoLoaderProcess.h | 13 + arm9/source/VBlank.cpp | 4 + arm9/source/VBlank.h | 25 + arm9/source/animation/Animator.cpp | 35 + arm9/source/animation/Animator.h | 50 + arm9/source/animation/CubicBezierCurve.h | 48 + arm9/source/animation/Curve.h | 8 + arm9/source/animation/Interpolator.h | 20 + arm9/source/animation/LinearCurve.h | 11 + .../animation/ThreePointCubicBezierCurve.h | 48 + arm9/source/bgm/AudioStreamPlayer.cpp | 140 + arm9/source/bgm/AudioStreamPlayer.h | 106 + arm9/source/bgm/BcstmAudioStream.cpp | 327 + arm9/source/bgm/BcstmAudioStream.h | 66 + arm9/source/bgm/BgmService.cpp | 44 + arm9/source/bgm/BgmService.h | 28 + arm9/source/bgm/IAudioStream.h | 24 + arm9/source/bgm/IAudioStreamPlayer.h | 20 + arm9/source/bgm/IBgmService.h | 22 + arm9/source/bgm/Pcm16FileAudioStream.cpp | 26 + arm9/source/bgm/Pcm16FileAudioStream.h | 33 + arm9/source/bgm/bcstm.h | 102 + arm9/source/bgm/dspAdpcm.s | 55 + arm9/source/common.h | 31 + arm9/source/core/BitVector.h | 53 + arm9/source/core/Environment.cpp | 64 + arm9/source/core/Environment.h | 32 + arm9/source/core/LinkedList.h | 189 + arm9/source/core/LinkedListLink.h | 11 + arm9/source/core/SharedPtr.h | 163 + arm9/source/core/String.h | 60 + arm9/source/core/StringUtil.cpp | 104 + arm9/source/core/StringUtil.h | 32 + arm9/source/core/di.h | 3362 ++++ arm9/source/core/heap/tlsf.c | 1272 ++ arm9/source/core/heap/tlsf.h | 90 + arm9/source/core/math/ColorConverter.h | 93 + arm9/source/core/math/Point.h | 18 + arm9/source/core/math/Rectangle.h | 74 + arm9/source/core/math/Rgb.h | 77 + arm9/source/core/math/RgbMixer.h | 26 + arm9/source/core/math/SinTable.h | 29 + arm9/source/core/math/fixed.h | 479 + arm9/source/core/semihosting.h | 137 + arm9/source/core/semihosting.s | 7 + arm9/source/core/task/Task.cpp | 39 + arm9/source/core/task/Task.h | 89 + arm9/source/core/task/TaskFactory.h | 27 + arm9/source/core/task/TaskQueue.cpp | 43 + arm9/source/core/task/TaskQueue.h | 212 + arm9/source/core/task/TaskResult.h | 39 + arm9/source/core/task/TaskState.h | 10 + arm9/source/dldiIpc.cpp | 175 + arm9/source/dldiIpc.h | 14 + arm9/source/dsiSdIpc.cpp | 142 + arm9/source/dsiSdIpc.h | 14 + arm9/source/fat/Directory.h | 20 + arm9/source/fat/FastFileRef.h | 25 + arm9/source/fat/File.h | 70 + arm9/source/fat/diskio.cpp | 210 + arm9/source/fat/diskio.h | 77 + arm9/source/fat/dldi_stub.s | 100 + arm9/source/fat/ff.c | 6593 +++++++ arm9/source/fat/ff.h | 412 + arm9/source/fat/ffconf.h | 286 + arm9/source/fat/ffsystem.c | 109 + arm9/source/fat/ffunicode.c | 15597 ++++++++++++++++ arm9/source/globalHeap.cpp | 143 + arm9/source/globalHeap.h | 17 + arm9/source/gui/AdvancedPaletteManager.cpp | 258 + arm9/source/gui/AdvancedPaletteManager.h | 101 + arm9/source/gui/Alignment.h | 8 + arm9/source/gui/AscendingStackVramManager.h | 22 + arm9/source/gui/DescendingStackVramManager.h | 35 + arm9/source/gui/FocusManager.cpp | 58 + arm9/source/gui/FocusManager.h | 32 + arm9/source/gui/FocusMoveDirection.h | 9 + arm9/source/gui/GraphicsContext.h | 67 + arm9/source/gui/Gx.h | 198 + arm9/source/gui/IVramManager.h | 20 + arm9/source/gui/OamBuilder.h | 220 + arm9/source/gui/OamManager.cpp | 16 + arm9/source/gui/OamManager.h | 37 + arm9/source/gui/PaletteManager.h | 30 + arm9/source/gui/Rgb6Palette.cpp | 118 + arm9/source/gui/Rgb6Palette.h | 14 + arm9/source/gui/SimplePaletteManager.cpp | 15 + arm9/source/gui/SimplePaletteManager.h | 42 + arm9/source/gui/StackVramManager.h | 30 + arm9/source/gui/VBlankTextureLoadRequest.h | 37 + .../gui/VBlankTextureLoadRequestState.h | 10 + arm9/source/gui/VBlankTextureLoader.cpp | 177 + arm9/source/gui/VBlankTextureLoader.h | 35 + arm9/source/gui/VramContext.h | 23 + arm9/source/gui/font/nitroFont2.cpp | 324 + arm9/source/gui/font/nitroFont2.h | 84 + arm9/source/gui/input/IInputSource.h | 11 + arm9/source/gui/input/InputKey.h | 45 + arm9/source/gui/input/InputProvider.h | 58 + arm9/source/gui/input/InputRepeater.cpp | 58 + arm9/source/gui/input/InputRepeater.h | 31 + arm9/source/gui/input/PadInputSource.h | 17 + .../source/gui/input/SampledInputProvider.cpp | 22 + arm9/source/gui/input/SampledInputProvider.h | 34 + arm9/source/gui/materialDesign.h | 62 + arm9/source/gui/palette/DirectPalette.cpp | 13 + arm9/source/gui/palette/DirectPalette.h | 23 + arm9/source/gui/palette/GradientPalette.cpp | 14 + arm9/source/gui/palette/GradientPalette.h | 23 + arm9/source/gui/palette/IPalette.h | 18 + arm9/source/gui/views/DialogType.h | 8 + arm9/source/gui/views/DialogView.h | 21 + arm9/source/gui/views/Label2DView.cpp | 68 + arm9/source/gui/views/Label2DView.h | 20 + arm9/source/gui/views/Label3DView.cpp | 58 + arm9/source/gui/views/Label3DView.h | 20 + arm9/source/gui/views/LabelView.cpp | 121 + arm9/source/gui/views/LabelView.h | 74 + arm9/source/gui/views/RecyclerAdapter.h | 38 + arm9/source/gui/views/RecyclerView.cpp | 549 + arm9/source/gui/views/RecyclerView.h | 112 + arm9/source/gui/views/RecyclerViewBase.h | 17 + arm9/source/gui/views/View.h | 106 + arm9/source/gui/views/ViewContainer.h | 60 + arm9/source/json/ArduinoJson.h | 7225 +++++++ arm9/source/logger/SemihostingOutputStream.h | 14 + arm9/source/main.cpp | 220 + arm9/source/material/cam/cam.cpp | 209 + arm9/source/material/cam/cam.h | 47 + arm9/source/material/cam/hct.cpp | 57 + arm9/source/material/cam/hct.h | 132 + arm9/source/material/cam/hct_solver.cpp | 526 + arm9/source/material/cam/hct_solver.h | 28 + .../material/cam/viewing_conditions.cpp | 112 + arm9/source/material/cam/viewing_conditions.h | 66 + arm9/source/material/palettes/core.cpp | 88 + arm9/source/material/palettes/core.h | 71 + arm9/source/material/palettes/tones.cpp | 40 + arm9/source/material/palettes/tones.h | 43 + arm9/source/material/scheme/scheme.cpp | 113 + arm9/source/material/scheme/scheme.h | 95 + arm9/source/material/utils/utils.cpp | 173 + arm9/source/material/utils/utils.h | 204 + arm9/source/picoLoaderBootstrap.cpp | 87 + arm9/source/picoLoaderBootstrap.h | 6 + .../rng/LinearCongruentialGenerator.cpp | 8 + arm9/source/rng/LinearCongruentialGenerator.h | 20 + arm9/source/rng/RandomGenerator.h | 38 + arm9/source/rng/ThreadSafeRandomGenerator.h | 41 + arm9/source/romBrowser/CoverRepository.cpp | 104 + arm9/source/romBrowser/CoverRepository.h | 18 + .../BannerListFileRecyclerAdapter.cpp | 85 + .../BannerListFileRecyclerAdapter.h | 34 + .../CoverFlowFileRecyclerAdapter.cpp | 59 + .../CoverFlowFileRecyclerAdapter.h | 35 + .../IconGridFileRecyclerAdapter.cpp | 73 + .../DisplayMode/IconGridFileRecyclerAdapter.h | 30 + .../RomBrowserBannerListDisplayMode.cpp | 4 + .../RomBrowserBannerListDisplayMode.h | 39 + .../DisplayMode/RomBrowserDisplayMode.h | 23 + .../RomBrowserDisplayModeFactory.cpp | 34 + .../RomBrowserDisplayModeFactory.h | 10 + ...mBrowserHorizontalCoverFlowDisplayMode.cpp | 4 + ...RomBrowserHorizontalCoverFlowDisplayMode.h | 34 + ...omBrowserHorizontalIconGridDisplayMode.cpp | 4 + .../RomBrowserHorizontalIconGridDisplayMode.h | 39 + .../RomBrowserVerticalIconGridDisplayMode.cpp | 4 + .../RomBrowserVerticalIconGridDisplayMode.h | 39 + arm9/source/romBrowser/FileInfo.cpp | 20 + arm9/source/romBrowser/FileInfo.h | 42 + arm9/source/romBrowser/FileInfoManager.cpp | 32 + arm9/source/romBrowser/FileInfoManager.h | 70 + .../source/romBrowser/FileRecyclerAdapter.cpp | 26 + arm9/source/romBrowser/FileRecyclerAdapter.h | 37 + .../romBrowser/FileType/BmpFileCover.cpp | 48 + .../source/romBrowser/FileType/BmpFileCover.h | 27 + .../romBrowser/FileType/CustomFileType.h | 61 + .../FileType/ExtensionFileTypeProvider.cpp | 58 + .../FileType/ExtensionFileTypeProvider.h | 18 + arm9/source/romBrowser/FileType/FileCover.h | 46 + .../FileType/FileCoverBitmapToTiledCopy.s | 32 + arm9/source/romBrowser/FileType/FileIcon.h | 58 + arm9/source/romBrowser/FileType/FileType.h | 75 + .../FileType/FileTypeClassification.h | 12 + .../FileType/Folder/FolderFileCover.h | 11 + .../FileType/Folder/FolderFileType.cpp | 4 + .../FileType/Folder/FolderFileType.h | 27 + .../romBrowser/FileType/Gba/GbaFileType.cpp | 4 + .../romBrowser/FileType/Gba/GbaFileType.h | 29 + .../FileType/Gba/GbaInternalFileInfo.cpp | 20 + .../FileType/Gba/GbaInternalFileInfo.h | 15 + .../romBrowser/FileType/IFileTypeProvider.h | 13 + .../romBrowser/FileType/InternalFileInfo.h | 29 + .../romBrowser/FileType/Nds/NdsFileIcon.cpp | 142 + .../romBrowser/FileType/Nds/NdsFileIcon.h | 24 + .../romBrowser/FileType/Nds/NdsFileType.cpp | 4 + .../romBrowser/FileType/Nds/NdsFileType.h | 36 + .../FileType/Nds/NdsInternalFileInfo.cpp | 53 + .../FileType/Nds/NdsInternalFileInfo.h | 29 + .../romBrowser/FileType/Nds/ndsBanner.h | 66 + .../FileType/NullFileTypeProvider.h | 13 + .../romBrowser/FileType/StaticFileCover.h | 34 + .../source/romBrowser/FileType/StaticIcon.cpp | 33 + arm9/source/romBrowser/FileType/StaticIcon.h | 18 + .../romBrowser/FileType/UnknownFileCover.h | 11 + .../romBrowser/FileType/UnknownFileType.cpp | 4 + .../romBrowser/FileType/UnknownFileType.h | 12 + arm9/source/romBrowser/ICoverRepository.h | 17 + .../source/romBrowser/IRomBrowserController.h | 43 + .../romBrowser/RomBrowserController.cpp | 209 + arm9/source/romBrowser/RomBrowserController.h | 72 + arm9/source/romBrowser/RomBrowserState.h | 11 + .../romBrowser/RomBrowserStateMachine.cpp | 58 + .../romBrowser/RomBrowserStateMachine.h | 26 + .../romBrowser/RomBrowserStateTrigger.h | 14 + arm9/source/romBrowser/SdFolder.cpp | 111 + arm9/source/romBrowser/SdFolder.h | 41 + arm9/source/romBrowser/SdFolderFactory.cpp | 39 + arm9/source/romBrowser/SdFolderFactory.h | 16 + .../romBrowser/SdFolderFilterSortParams.h | 15 + .../source/romBrowser/SdFolderSortDirection.h | 7 + arm9/source/romBrowser/SdFolderSortType.h | 7 + .../romBrowser/StateMachineTriggerChecker.h | 70 + .../romBrowser/Theme/IRomBrowserViewFactory.h | 40 + .../romBrowser/Theme/IThemeFileIconFactory.h | 14 + .../Theme/Material/CarouselRecyclerView.cpp | 227 + .../Theme/Material/CarouselRecyclerView.h | 41 + .../Theme/Material/MaterialAppBarView.cpp | 32 + .../Theme/Material/MaterialAppBarView.h | 11 + .../Material/MaterialBannerListItemView.cpp | 147 + .../Material/MaterialBannerListItemView.h | 31 + .../MaterialCoverFlowFileRecyclerAdapter.cpp | 54 + .../MaterialCoverFlowFileRecyclerAdapter.h | 33 + .../Theme/Material/MaterialCoverView.cpp | 56 + .../Theme/Material/MaterialCoverView.h | 54 + .../Theme/Material/MaterialFileIcon.cpp | 66 + .../Theme/Material/MaterialFileIcon.h | 28 + .../Material/MaterialFileInfoCardView.cpp | 84 + .../Theme/Material/MaterialFileInfoCardView.h | 75 + .../Theme/Material/MaterialFolderIcon.h | 30 + .../Theme/Material/MaterialGenericFileIcon.h | 30 + .../Material/MaterialIconGridItemView.cpp | 67 + .../Theme/Material/MaterialIconGridItemView.h | 29 + .../Theme/Material/MaterialNdsFileIcon.h | 30 + .../Material/MaterialRomBrowserViewFactory.h | 71 + .../Material/MaterialThemeFileIconFactory.h | 35 + .../Theme/custom/CustomAppBarView.cpp | 60 + .../Theme/custom/CustomAppBarView.h | 16 + .../Theme/custom/CustomBannerListItemView.cpp | 124 + .../Theme/custom/CustomBannerListItemView.h | 28 + .../Theme/custom/CustomFileInfoView.cpp | 53 + .../Theme/custom/CustomFileInfoView.h | 72 + .../Theme/custom/CustomIconGridItemView.cpp | 54 + .../Theme/custom/CustomIconGridItemView.h | 24 + .../custom/CustomRomBrowserViewFactory.cpp | 112 + .../custom/CustomRomBrowserViewFactory.h | 82 + .../viewModels/DisplaySettingsViewModel.h | 49 + .../viewModels/RomBrowserAppBarViewModel.h | 28 + .../RomBrowserBottomScreenViewModel.h | 44 + .../viewModels/RomBrowserViewModel.cpp | 65 + .../viewModels/RomBrowserViewModel.h | 36 + arm9/source/romBrowser/views/AppBarView.cpp | 136 + arm9/source/romBrowser/views/AppBarView.h | 48 + .../romBrowser/views/BannerListItemView.cpp | 25 + .../romBrowser/views/BannerListItemView.h | 67 + arm9/source/romBrowser/views/BannerView.cpp | 63 + arm9/source/romBrowser/views/BannerView.h | 49 + .../source/romBrowser/views/BottomSheetView.h | 19 + arm9/source/romBrowser/views/ChipView.cpp | 138 + arm9/source/romBrowser/views/ChipView.h | 91 + .../views/CoverFlowRecyclerView.cpp | 156 + .../romBrowser/views/CoverFlowRecyclerView.h | 29 + .../views/CoverFlowRecyclerViewBase.cpp | 184 + .../views/CoverFlowRecyclerViewBase.h | 53 + arm9/source/romBrowser/views/CoverView.cpp | 95 + arm9/source/romBrowser/views/CoverView.h | 56 + .../views/DisplaySettingsBottomSheetView.cpp | 359 + .../views/DisplaySettingsBottomSheetView.h | 53 + .../romBrowser/views/IconButton2DView.cpp | 85 + .../romBrowser/views/IconButton2DView.h | 37 + .../romBrowser/views/IconButton3DView.cpp | 92 + .../romBrowser/views/IconButton3DView.h | 23 + .../romBrowser/views/IconButtonView.cpp | 98 + arm9/source/romBrowser/views/IconButtonView.h | 69 + .../romBrowser/views/IconGridItemView.cpp | 22 + .../romBrowser/views/IconGridItemView.h | 49 + .../views/NdsGameDetailsBottomSheetView.cpp | 80 + .../views/NdsGameDetailsBottomSheetView.h | 43 + .../romBrowser/views/RomBrowserAppBarView.cpp | 133 + .../romBrowser/views/RomBrowserAppBarView.h | 46 + .../views/RomBrowserBottomScreenView.cpp | 118 + .../views/RomBrowserBottomScreenView.h | 61 + .../views/RomBrowserTopScreenView.cpp | 151 + .../views/RomBrowserTopScreenView.h | 39 + .../romBrowser/views/RomBrowserView.cpp | 101 + arm9/source/romBrowser/views/RomBrowserView.h | 53 + arm9/source/rtcIpc.cpp | 42 + arm9/source/rtcIpc.h | 5 + arm9/source/services/process/IProcess.h | 10 + arm9/source/services/process/ProcessFactory.h | 11 + .../services/process/ProcessFactory.thumb.cpp | 37 + .../services/process/ProcessManager.cpp | 26 + arm9/source/services/process/ProcessManager.h | 28 + arm9/source/services/settings/AppSettings.h | 17 + .../services/settings/FileAssociation.h | 14 + .../services/settings/IAppSettingsService.h | 12 + .../settings/JsonAppSettingsSerializer.h | 9 + .../JsonAppSettingsSerializer.thumb.cpp | 211 + .../settings/JsonAppSettingsService.cpp | 9 + .../settings/JsonAppSettingsService.h | 20 + .../settings/RomBrowserDisplaySettings.h | 10 + .../services/settings/RomBrowserLayout.h | 10 + .../services/settings/RomBrowserSortMode.h | 8 + arm9/source/themes/DefaultFontRepository.cpp | 33 + arm9/source/themes/DefaultFontRepository.h | 8 + arm9/source/themes/FontType.h | 9 + arm9/source/themes/IFontRepository.h | 13 + arm9/source/themes/ITheme.h | 32 + arm9/source/themes/Theme.cpp | 17 + arm9/source/themes/Theme.h | 20 + arm9/source/themes/ThemeFactory.cpp | 29 + arm9/source/themes/ThemeFactory.h | 11 + arm9/source/themes/ThemeInfo.h | 38 + arm9/source/themes/ThemeInfoFactory.h | 14 + arm9/source/themes/ThemeInfoFactory.thumb.cpp | 104 + arm9/source/themes/ThemeType.h | 7 + .../themes/background/IThemeBackground.h | 29 + .../themes/custom/CustomMainBackground.cpp | 43 + .../themes/custom/CustomMainBackground.h | 9 + .../themes/custom/CustomSubBackground.cpp | 32 + .../themes/custom/CustomSubBackground.h | 9 + arm9/source/themes/custom/CustomTheme.cpp | 49 + arm9/source/themes/custom/CustomTheme.h | 50 + .../themes/custom/CustomTopBackgroundType.h | 6 + .../themes/material/MaterialColorScheme.h | 56 + .../material/MaterialColorSchemeFactory.cpp | 47 + .../material/MaterialColorSchemeFactory.h | 10 + .../material/MaterialMainBackground.cpp | 33 + .../themes/material/MaterialMainBackground.h | 17 + .../themes/material/MaterialSubBackground.cpp | 25 + .../themes/material/MaterialSubBackground.h | 16 + arm9/source/themes/material/MaterialTheme.cpp | 13 + arm9/source/themes/material/MaterialTheme.h | 49 + common/core/TickCounter.cpp | 40 + common/core/TickCounter.h | 26 + common/core/mini-printf.c | 312 + common/core/mini-printf.h | 73 + common/dldiIpcCommand.h | 17 + common/dsiSdIpcCommand.h | 16 + common/ipc/IpcService.cpp | 11 + common/ipc/IpcService.h | 19 + common/ipc/ThreadIpcService.cpp | 33 + common/ipc/ThreadIpcService.h | 26 + common/ipcChannels.h | 7 + common/logger/ILogger.h | 32 + common/logger/IOutputStream.h | 10 + common/logger/NitroEmulatorOutputStream.cpp | 47 + common/logger/NitroEmulatorOutputStream.h | 11 + common/logger/NocashOutputStream.h | 16 + common/logger/NullLogger.h | 8 + common/logger/NullOutputStream.h | 9 + common/logger/PicoAgbAdapterOutputStream.cpp | 28 + common/logger/PicoAgbAdapterOutputStream.h | 11 + common/logger/PlainLogger.h | 26 + common/logger/ThreadSafeLogger.h | 26 + common/picoAgbAdapter.h | 9 + common/picoLoader7.h | 55 + common/sharedMemory.h | 3 + common/soundIpcCommand.h | 19 + docs/Covers.md | 16 + docs/FileAssociations.md | 20 + docs/Themes.md | 57 + docs/Usage.md | 49 + docs/images/CoverExample.bmp | Bin 0 -> 13366 bytes docs/images/Coverflow.png | Bin 0 -> 27243 bytes docs/images/Horizontal.png | Bin 0 -> 11102 bytes docs/images/HorizontalCustom.png | Bin 0 -> 103632 bytes docs/images/List.png | Bin 0 -> 27582 bytes docs/images/SettingsPage.png | Bin 0 -> 8332 bytes docs/images/Vertical.png | Bin 0 -> 11903 bytes icon.bmp | Bin 0 -> 1144 bytes libs/libtwl | 1 + licenses/ArduinoJson.txt | 22 + licenses/Material.txt | 202 + licenses/dsi_sdmmc.txt | 21 + licenses/mini-printf.txt | 24 + licenses/newlib.txt | 1543 ++ licenses/tlsf.txt | 24 + 517 files changed, 63025 insertions(+) create mode 100644 .gitattributes create mode 100644 .github/workflows/nightly.yml create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 LICENSE.txt create mode 100644 Makefile create mode 100644 Makefile.arm7 create mode 100644 Makefile.arm9 create mode 100644 README.md create mode 100644 _pico/themes/material/theme.json create mode 100644 _pico/themes/raspberry/bannerListCell.bin create mode 100644 _pico/themes/raspberry/bannerListCellPltt.bin create mode 100644 _pico/themes/raspberry/bannerListCellSelected.bin create mode 100644 _pico/themes/raspberry/bannerListCellSelectedPltt.bin create mode 100644 _pico/themes/raspberry/bottombg.bin create mode 100644 _pico/themes/raspberry/gridcell.bin create mode 100644 _pico/themes/raspberry/gridcellPltt.bin create mode 100644 _pico/themes/raspberry/gridcellSelected.bin create mode 100644 _pico/themes/raspberry/gridcellSelectedPltt.bin create mode 100644 _pico/themes/raspberry/scrim.bin create mode 100644 _pico/themes/raspberry/scrimPltt.bin create mode 100644 _pico/themes/raspberry/theme.json create mode 100644 _pico/themes/raspberry/topbg.bin create mode 100644 arm7/dldi_ds_arm7.ld create mode 100644 arm7/dldi_ds_arm7.specs create mode 100644 arm7/source/Arm7State.h create mode 100644 arm7/source/ExitMode.h create mode 100644 arm7/source/common.h create mode 100644 arm7/source/dldi.s create mode 100644 arm7/source/ipcServices/DldiIpcService.cpp create mode 100644 arm7/source/ipcServices/DldiIpcService.h create mode 100644 arm7/source/ipcServices/DsiSdIpcService.h create mode 100644 arm7/source/ipcServices/DsiSdIpcService.twl.cpp create mode 100644 arm7/source/ipcServices/RtcIpcService.cpp create mode 100644 arm7/source/ipcServices/RtcIpcService.h create mode 100644 arm7/source/ipcServices/SoundIpcService.cpp create mode 100644 arm7/source/ipcServices/SoundIpcService.h create mode 100644 arm7/source/main.cpp create mode 100644 arm7/source/mmc/mmc_spec.h create mode 100644 arm7/source/mmc/sd_spec.h create mode 100644 arm7/source/mmc/sdmmc.h create mode 100644 arm7/source/mmc/sdmmc.twl.c create mode 100644 arm7/source/mmc/tmio.h create mode 100644 arm7/source/mmc/tmio.twl.c create mode 100644 arm7/source/picoLoaderBootstrap.cpp create mode 100644 arm7/source/picoLoaderBootstrap.h create mode 100644 arm9/data/NotoSansJP-Medium-10.nft2 create mode 100644 arm9/data/NotoSansJP-Medium-11.nft2 create mode 100644 arm9/data/NotoSansJP-Medium-7_5.nft2 create mode 100644 arm9/data/NotoSansJP-Regular-10.nft2 create mode 100644 arm9/gfx/backIcon.grit create mode 100644 arm9/gfx/backIcon.png create mode 100644 arm9/gfx/bannerListIcon.grit create mode 100644 arm9/gfx/bannerListIcon.png create mode 100644 arm9/gfx/bannerListItemBg0.grit create mode 100644 arm9/gfx/bannerListItemBg0.png create mode 100644 arm9/gfx/bannerListItemBg1.grit create mode 100644 arm9/gfx/bannerListItemBg1.png create mode 100644 arm9/gfx/bannerListItemBg2.grit create mode 100644 arm9/gfx/bannerListItemBg2.png create mode 100644 arm9/gfx/bottomSheetBg.grit create mode 100644 arm9/gfx/bottomSheetBg.png create mode 100644 arm9/gfx/carouselMask.grit create mode 100644 arm9/gfx/carouselMask.png create mode 100644 arm9/gfx/chipFilled.grit create mode 100644 arm9/gfx/chipFilled.png create mode 100644 arm9/gfx/coverflowIcon.grit create mode 100644 arm9/gfx/coverflowIcon.png create mode 100644 arm9/gfx/folderCover.grit create mode 100644 arm9/gfx/folderCover.png create mode 100644 arm9/gfx/gamesIcon.grit create mode 100644 arm9/gfx/gamesIcon.png create mode 100644 arm9/gfx/hGridIcon.grit create mode 100644 arm9/gfx/hGridIcon.png create mode 100644 arm9/gfx/heartIcon.grit create mode 100644 arm9/gfx/heartIcon.png create mode 100644 arm9/gfx/iconButtonSelector.grit create mode 100644 arm9/gfx/iconButtonSelector.png create mode 100644 arm9/gfx/iconButtonSelectorTexture.grit create mode 100644 arm9/gfx/iconButtonSelectorTexture.png create mode 100644 arm9/gfx/iconCell.grit create mode 100644 arm9/gfx/iconCell.png create mode 100644 arm9/gfx/iconCell2.grit create mode 100644 arm9/gfx/iconCell2.png create mode 100644 arm9/gfx/iconCell3.grit create mode 100644 arm9/gfx/iconCell3.png create mode 100644 arm9/gfx/largeDSCardIcon.grit create mode 100644 arm9/gfx/largeDSCardIcon.png create mode 100644 arm9/gfx/largeFileIcon.grit create mode 100644 arm9/gfx/largeFileIcon.png create mode 100644 arm9/gfx/largeFolderIcon.grit create mode 100644 arm9/gfx/largeFolderIcon.png create mode 100644 arm9/gfx/listIcon.grit create mode 100644 arm9/gfx/listIcon.png create mode 100644 arm9/gfx/mainBg.grit create mode 100644 arm9/gfx/mainBg.png create mode 100644 arm9/gfx/moviesIcon.grit create mode 100644 arm9/gfx/moviesIcon.png create mode 100644 arm9/gfx/musicIcon.grit create mode 100644 arm9/gfx/musicIcon.png create mode 100644 arm9/gfx/picturesIcon.grit create mode 100644 arm9/gfx/picturesIcon.png create mode 100644 arm9/gfx/recentIcon.grit create mode 100644 arm9/gfx/recentIcon.png create mode 100644 arm9/gfx/scrim.grit create mode 100644 arm9/gfx/scrim.png create mode 100644 arm9/gfx/settingsIcon.grit create mode 100644 arm9/gfx/settingsIcon.png create mode 100644 arm9/gfx/smallHeartIcon.grit create mode 100644 arm9/gfx/smallHeartIcon.png create mode 100644 arm9/gfx/smallHeartIconFilled.grit create mode 100644 arm9/gfx/smallHeartIconFilled.png create mode 100644 arm9/gfx/sortNameAscendingIcon.grit create mode 100644 arm9/gfx/sortNameAscendingIcon.png create mode 100644 arm9/gfx/sortNameDescendingIcon.grit create mode 100644 arm9/gfx/sortNameDescendingIcon.png create mode 100644 arm9/gfx/splashTop.grit create mode 100644 arm9/gfx/splashTop.png create mode 100644 arm9/gfx/unknownCover.grit create mode 100644 arm9/gfx/unknownCover.png create mode 100644 arm9/gfx/unknownIcon.grit create mode 100644 arm9/gfx/unknownIcon.png create mode 100644 arm9/gfx/vGridIcon.grit create mode 100644 arm9/gfx/vGridIcon.png create mode 100644 arm9/source/App.cpp create mode 100644 arm9/source/App.h create mode 100644 arm9/source/DialogPresenter.cpp create mode 100644 arm9/source/DialogPresenter.h create mode 100644 arm9/source/PicoLoaderProcess.cpp create mode 100644 arm9/source/PicoLoaderProcess.h create mode 100644 arm9/source/VBlank.cpp create mode 100644 arm9/source/VBlank.h create mode 100644 arm9/source/animation/Animator.cpp create mode 100644 arm9/source/animation/Animator.h create mode 100644 arm9/source/animation/CubicBezierCurve.h create mode 100644 arm9/source/animation/Curve.h create mode 100644 arm9/source/animation/Interpolator.h create mode 100644 arm9/source/animation/LinearCurve.h create mode 100644 arm9/source/animation/ThreePointCubicBezierCurve.h create mode 100644 arm9/source/bgm/AudioStreamPlayer.cpp create mode 100644 arm9/source/bgm/AudioStreamPlayer.h create mode 100644 arm9/source/bgm/BcstmAudioStream.cpp create mode 100644 arm9/source/bgm/BcstmAudioStream.h create mode 100644 arm9/source/bgm/BgmService.cpp create mode 100644 arm9/source/bgm/BgmService.h create mode 100644 arm9/source/bgm/IAudioStream.h create mode 100644 arm9/source/bgm/IAudioStreamPlayer.h create mode 100644 arm9/source/bgm/IBgmService.h create mode 100644 arm9/source/bgm/Pcm16FileAudioStream.cpp create mode 100644 arm9/source/bgm/Pcm16FileAudioStream.h create mode 100644 arm9/source/bgm/bcstm.h create mode 100644 arm9/source/bgm/dspAdpcm.s create mode 100644 arm9/source/common.h create mode 100644 arm9/source/core/BitVector.h create mode 100644 arm9/source/core/Environment.cpp create mode 100644 arm9/source/core/Environment.h create mode 100644 arm9/source/core/LinkedList.h create mode 100644 arm9/source/core/LinkedListLink.h create mode 100644 arm9/source/core/SharedPtr.h create mode 100644 arm9/source/core/String.h create mode 100644 arm9/source/core/StringUtil.cpp create mode 100644 arm9/source/core/StringUtil.h create mode 100644 arm9/source/core/di.h create mode 100644 arm9/source/core/heap/tlsf.c create mode 100644 arm9/source/core/heap/tlsf.h create mode 100644 arm9/source/core/math/ColorConverter.h create mode 100644 arm9/source/core/math/Point.h create mode 100644 arm9/source/core/math/Rectangle.h create mode 100644 arm9/source/core/math/Rgb.h create mode 100644 arm9/source/core/math/RgbMixer.h create mode 100644 arm9/source/core/math/SinTable.h create mode 100644 arm9/source/core/math/fixed.h create mode 100644 arm9/source/core/semihosting.h create mode 100644 arm9/source/core/semihosting.s create mode 100644 arm9/source/core/task/Task.cpp create mode 100644 arm9/source/core/task/Task.h create mode 100644 arm9/source/core/task/TaskFactory.h create mode 100644 arm9/source/core/task/TaskQueue.cpp create mode 100644 arm9/source/core/task/TaskQueue.h create mode 100644 arm9/source/core/task/TaskResult.h create mode 100644 arm9/source/core/task/TaskState.h create mode 100644 arm9/source/dldiIpc.cpp create mode 100644 arm9/source/dldiIpc.h create mode 100644 arm9/source/dsiSdIpc.cpp create mode 100644 arm9/source/dsiSdIpc.h create mode 100644 arm9/source/fat/Directory.h create mode 100644 arm9/source/fat/FastFileRef.h create mode 100644 arm9/source/fat/File.h create mode 100644 arm9/source/fat/diskio.cpp create mode 100644 arm9/source/fat/diskio.h create mode 100644 arm9/source/fat/dldi_stub.s create mode 100644 arm9/source/fat/ff.c create mode 100644 arm9/source/fat/ff.h create mode 100644 arm9/source/fat/ffconf.h create mode 100644 arm9/source/fat/ffsystem.c create mode 100644 arm9/source/fat/ffunicode.c create mode 100644 arm9/source/globalHeap.cpp create mode 100644 arm9/source/globalHeap.h create mode 100644 arm9/source/gui/AdvancedPaletteManager.cpp create mode 100644 arm9/source/gui/AdvancedPaletteManager.h create mode 100644 arm9/source/gui/Alignment.h create mode 100644 arm9/source/gui/AscendingStackVramManager.h create mode 100644 arm9/source/gui/DescendingStackVramManager.h create mode 100644 arm9/source/gui/FocusManager.cpp create mode 100644 arm9/source/gui/FocusManager.h create mode 100644 arm9/source/gui/FocusMoveDirection.h create mode 100644 arm9/source/gui/GraphicsContext.h create mode 100644 arm9/source/gui/Gx.h create mode 100644 arm9/source/gui/IVramManager.h create mode 100644 arm9/source/gui/OamBuilder.h create mode 100644 arm9/source/gui/OamManager.cpp create mode 100644 arm9/source/gui/OamManager.h create mode 100644 arm9/source/gui/PaletteManager.h create mode 100644 arm9/source/gui/Rgb6Palette.cpp create mode 100644 arm9/source/gui/Rgb6Palette.h create mode 100644 arm9/source/gui/SimplePaletteManager.cpp create mode 100644 arm9/source/gui/SimplePaletteManager.h create mode 100644 arm9/source/gui/StackVramManager.h create mode 100644 arm9/source/gui/VBlankTextureLoadRequest.h create mode 100644 arm9/source/gui/VBlankTextureLoadRequestState.h create mode 100644 arm9/source/gui/VBlankTextureLoader.cpp create mode 100644 arm9/source/gui/VBlankTextureLoader.h create mode 100644 arm9/source/gui/VramContext.h create mode 100644 arm9/source/gui/font/nitroFont2.cpp create mode 100644 arm9/source/gui/font/nitroFont2.h create mode 100644 arm9/source/gui/input/IInputSource.h create mode 100644 arm9/source/gui/input/InputKey.h create mode 100644 arm9/source/gui/input/InputProvider.h create mode 100644 arm9/source/gui/input/InputRepeater.cpp create mode 100644 arm9/source/gui/input/InputRepeater.h create mode 100644 arm9/source/gui/input/PadInputSource.h create mode 100644 arm9/source/gui/input/SampledInputProvider.cpp create mode 100644 arm9/source/gui/input/SampledInputProvider.h create mode 100644 arm9/source/gui/materialDesign.h create mode 100644 arm9/source/gui/palette/DirectPalette.cpp create mode 100644 arm9/source/gui/palette/DirectPalette.h create mode 100644 arm9/source/gui/palette/GradientPalette.cpp create mode 100644 arm9/source/gui/palette/GradientPalette.h create mode 100644 arm9/source/gui/palette/IPalette.h create mode 100644 arm9/source/gui/views/DialogType.h create mode 100644 arm9/source/gui/views/DialogView.h create mode 100644 arm9/source/gui/views/Label2DView.cpp create mode 100644 arm9/source/gui/views/Label2DView.h create mode 100644 arm9/source/gui/views/Label3DView.cpp create mode 100644 arm9/source/gui/views/Label3DView.h create mode 100644 arm9/source/gui/views/LabelView.cpp create mode 100644 arm9/source/gui/views/LabelView.h create mode 100644 arm9/source/gui/views/RecyclerAdapter.h create mode 100644 arm9/source/gui/views/RecyclerView.cpp create mode 100644 arm9/source/gui/views/RecyclerView.h create mode 100644 arm9/source/gui/views/RecyclerViewBase.h create mode 100644 arm9/source/gui/views/View.h create mode 100644 arm9/source/gui/views/ViewContainer.h create mode 100644 arm9/source/json/ArduinoJson.h create mode 100644 arm9/source/logger/SemihostingOutputStream.h create mode 100644 arm9/source/main.cpp create mode 100644 arm9/source/material/cam/cam.cpp create mode 100644 arm9/source/material/cam/cam.h create mode 100644 arm9/source/material/cam/hct.cpp create mode 100644 arm9/source/material/cam/hct.h create mode 100644 arm9/source/material/cam/hct_solver.cpp create mode 100644 arm9/source/material/cam/hct_solver.h create mode 100644 arm9/source/material/cam/viewing_conditions.cpp create mode 100644 arm9/source/material/cam/viewing_conditions.h create mode 100644 arm9/source/material/palettes/core.cpp create mode 100644 arm9/source/material/palettes/core.h create mode 100644 arm9/source/material/palettes/tones.cpp create mode 100644 arm9/source/material/palettes/tones.h create mode 100644 arm9/source/material/scheme/scheme.cpp create mode 100644 arm9/source/material/scheme/scheme.h create mode 100644 arm9/source/material/utils/utils.cpp create mode 100644 arm9/source/material/utils/utils.h create mode 100644 arm9/source/picoLoaderBootstrap.cpp create mode 100644 arm9/source/picoLoaderBootstrap.h create mode 100644 arm9/source/rng/LinearCongruentialGenerator.cpp create mode 100644 arm9/source/rng/LinearCongruentialGenerator.h create mode 100644 arm9/source/rng/RandomGenerator.h create mode 100644 arm9/source/rng/ThreadSafeRandomGenerator.h create mode 100644 arm9/source/romBrowser/CoverRepository.cpp create mode 100644 arm9/source/romBrowser/CoverRepository.h create mode 100644 arm9/source/romBrowser/DisplayMode/BannerListFileRecyclerAdapter.cpp create mode 100644 arm9/source/romBrowser/DisplayMode/BannerListFileRecyclerAdapter.h create mode 100644 arm9/source/romBrowser/DisplayMode/CoverFlowFileRecyclerAdapter.cpp create mode 100644 arm9/source/romBrowser/DisplayMode/CoverFlowFileRecyclerAdapter.h create mode 100644 arm9/source/romBrowser/DisplayMode/IconGridFileRecyclerAdapter.cpp create mode 100644 arm9/source/romBrowser/DisplayMode/IconGridFileRecyclerAdapter.h create mode 100644 arm9/source/romBrowser/DisplayMode/RomBrowserBannerListDisplayMode.cpp create mode 100644 arm9/source/romBrowser/DisplayMode/RomBrowserBannerListDisplayMode.h create mode 100644 arm9/source/romBrowser/DisplayMode/RomBrowserDisplayMode.h create mode 100644 arm9/source/romBrowser/DisplayMode/RomBrowserDisplayModeFactory.cpp create mode 100644 arm9/source/romBrowser/DisplayMode/RomBrowserDisplayModeFactory.h create mode 100644 arm9/source/romBrowser/DisplayMode/RomBrowserHorizontalCoverFlowDisplayMode.cpp create mode 100644 arm9/source/romBrowser/DisplayMode/RomBrowserHorizontalCoverFlowDisplayMode.h create mode 100644 arm9/source/romBrowser/DisplayMode/RomBrowserHorizontalIconGridDisplayMode.cpp create mode 100644 arm9/source/romBrowser/DisplayMode/RomBrowserHorizontalIconGridDisplayMode.h create mode 100644 arm9/source/romBrowser/DisplayMode/RomBrowserVerticalIconGridDisplayMode.cpp create mode 100644 arm9/source/romBrowser/DisplayMode/RomBrowserVerticalIconGridDisplayMode.h create mode 100644 arm9/source/romBrowser/FileInfo.cpp create mode 100644 arm9/source/romBrowser/FileInfo.h create mode 100644 arm9/source/romBrowser/FileInfoManager.cpp create mode 100644 arm9/source/romBrowser/FileInfoManager.h create mode 100644 arm9/source/romBrowser/FileRecyclerAdapter.cpp create mode 100644 arm9/source/romBrowser/FileRecyclerAdapter.h create mode 100644 arm9/source/romBrowser/FileType/BmpFileCover.cpp create mode 100644 arm9/source/romBrowser/FileType/BmpFileCover.h create mode 100644 arm9/source/romBrowser/FileType/CustomFileType.h create mode 100644 arm9/source/romBrowser/FileType/ExtensionFileTypeProvider.cpp create mode 100644 arm9/source/romBrowser/FileType/ExtensionFileTypeProvider.h create mode 100644 arm9/source/romBrowser/FileType/FileCover.h create mode 100644 arm9/source/romBrowser/FileType/FileCoverBitmapToTiledCopy.s create mode 100644 arm9/source/romBrowser/FileType/FileIcon.h create mode 100644 arm9/source/romBrowser/FileType/FileType.h create mode 100644 arm9/source/romBrowser/FileType/FileTypeClassification.h create mode 100644 arm9/source/romBrowser/FileType/Folder/FolderFileCover.h create mode 100644 arm9/source/romBrowser/FileType/Folder/FolderFileType.cpp create mode 100644 arm9/source/romBrowser/FileType/Folder/FolderFileType.h create mode 100644 arm9/source/romBrowser/FileType/Gba/GbaFileType.cpp create mode 100644 arm9/source/romBrowser/FileType/Gba/GbaFileType.h create mode 100644 arm9/source/romBrowser/FileType/Gba/GbaInternalFileInfo.cpp create mode 100644 arm9/source/romBrowser/FileType/Gba/GbaInternalFileInfo.h create mode 100644 arm9/source/romBrowser/FileType/IFileTypeProvider.h create mode 100644 arm9/source/romBrowser/FileType/InternalFileInfo.h create mode 100644 arm9/source/romBrowser/FileType/Nds/NdsFileIcon.cpp create mode 100644 arm9/source/romBrowser/FileType/Nds/NdsFileIcon.h create mode 100644 arm9/source/romBrowser/FileType/Nds/NdsFileType.cpp create mode 100644 arm9/source/romBrowser/FileType/Nds/NdsFileType.h create mode 100644 arm9/source/romBrowser/FileType/Nds/NdsInternalFileInfo.cpp create mode 100644 arm9/source/romBrowser/FileType/Nds/NdsInternalFileInfo.h create mode 100644 arm9/source/romBrowser/FileType/Nds/ndsBanner.h create mode 100644 arm9/source/romBrowser/FileType/NullFileTypeProvider.h create mode 100644 arm9/source/romBrowser/FileType/StaticFileCover.h create mode 100644 arm9/source/romBrowser/FileType/StaticIcon.cpp create mode 100644 arm9/source/romBrowser/FileType/StaticIcon.h create mode 100644 arm9/source/romBrowser/FileType/UnknownFileCover.h create mode 100644 arm9/source/romBrowser/FileType/UnknownFileType.cpp create mode 100644 arm9/source/romBrowser/FileType/UnknownFileType.h create mode 100644 arm9/source/romBrowser/ICoverRepository.h create mode 100644 arm9/source/romBrowser/IRomBrowserController.h create mode 100644 arm9/source/romBrowser/RomBrowserController.cpp create mode 100644 arm9/source/romBrowser/RomBrowserController.h create mode 100644 arm9/source/romBrowser/RomBrowserState.h create mode 100644 arm9/source/romBrowser/RomBrowserStateMachine.cpp create mode 100644 arm9/source/romBrowser/RomBrowserStateMachine.h create mode 100644 arm9/source/romBrowser/RomBrowserStateTrigger.h create mode 100644 arm9/source/romBrowser/SdFolder.cpp create mode 100644 arm9/source/romBrowser/SdFolder.h create mode 100644 arm9/source/romBrowser/SdFolderFactory.cpp create mode 100644 arm9/source/romBrowser/SdFolderFactory.h create mode 100644 arm9/source/romBrowser/SdFolderFilterSortParams.h create mode 100644 arm9/source/romBrowser/SdFolderSortDirection.h create mode 100644 arm9/source/romBrowser/SdFolderSortType.h create mode 100644 arm9/source/romBrowser/StateMachineTriggerChecker.h create mode 100644 arm9/source/romBrowser/Theme/IRomBrowserViewFactory.h create mode 100644 arm9/source/romBrowser/Theme/IThemeFileIconFactory.h create mode 100644 arm9/source/romBrowser/Theme/Material/CarouselRecyclerView.cpp create mode 100644 arm9/source/romBrowser/Theme/Material/CarouselRecyclerView.h create mode 100644 arm9/source/romBrowser/Theme/Material/MaterialAppBarView.cpp create mode 100644 arm9/source/romBrowser/Theme/Material/MaterialAppBarView.h create mode 100644 arm9/source/romBrowser/Theme/Material/MaterialBannerListItemView.cpp create mode 100644 arm9/source/romBrowser/Theme/Material/MaterialBannerListItemView.h create mode 100644 arm9/source/romBrowser/Theme/Material/MaterialCoverFlowFileRecyclerAdapter.cpp create mode 100644 arm9/source/romBrowser/Theme/Material/MaterialCoverFlowFileRecyclerAdapter.h create mode 100644 arm9/source/romBrowser/Theme/Material/MaterialCoverView.cpp create mode 100644 arm9/source/romBrowser/Theme/Material/MaterialCoverView.h create mode 100644 arm9/source/romBrowser/Theme/Material/MaterialFileIcon.cpp create mode 100644 arm9/source/romBrowser/Theme/Material/MaterialFileIcon.h create mode 100644 arm9/source/romBrowser/Theme/Material/MaterialFileInfoCardView.cpp create mode 100644 arm9/source/romBrowser/Theme/Material/MaterialFileInfoCardView.h create mode 100644 arm9/source/romBrowser/Theme/Material/MaterialFolderIcon.h create mode 100644 arm9/source/romBrowser/Theme/Material/MaterialGenericFileIcon.h create mode 100644 arm9/source/romBrowser/Theme/Material/MaterialIconGridItemView.cpp create mode 100644 arm9/source/romBrowser/Theme/Material/MaterialIconGridItemView.h create mode 100644 arm9/source/romBrowser/Theme/Material/MaterialNdsFileIcon.h create mode 100644 arm9/source/romBrowser/Theme/Material/MaterialRomBrowserViewFactory.h create mode 100644 arm9/source/romBrowser/Theme/Material/MaterialThemeFileIconFactory.h create mode 100644 arm9/source/romBrowser/Theme/custom/CustomAppBarView.cpp create mode 100644 arm9/source/romBrowser/Theme/custom/CustomAppBarView.h create mode 100644 arm9/source/romBrowser/Theme/custom/CustomBannerListItemView.cpp create mode 100644 arm9/source/romBrowser/Theme/custom/CustomBannerListItemView.h create mode 100644 arm9/source/romBrowser/Theme/custom/CustomFileInfoView.cpp create mode 100644 arm9/source/romBrowser/Theme/custom/CustomFileInfoView.h create mode 100644 arm9/source/romBrowser/Theme/custom/CustomIconGridItemView.cpp create mode 100644 arm9/source/romBrowser/Theme/custom/CustomIconGridItemView.h create mode 100644 arm9/source/romBrowser/Theme/custom/CustomRomBrowserViewFactory.cpp create mode 100644 arm9/source/romBrowser/Theme/custom/CustomRomBrowserViewFactory.h create mode 100644 arm9/source/romBrowser/viewModels/DisplaySettingsViewModel.h create mode 100644 arm9/source/romBrowser/viewModels/RomBrowserAppBarViewModel.h create mode 100644 arm9/source/romBrowser/viewModels/RomBrowserBottomScreenViewModel.h create mode 100644 arm9/source/romBrowser/viewModels/RomBrowserViewModel.cpp create mode 100644 arm9/source/romBrowser/viewModels/RomBrowserViewModel.h create mode 100644 arm9/source/romBrowser/views/AppBarView.cpp create mode 100644 arm9/source/romBrowser/views/AppBarView.h create mode 100644 arm9/source/romBrowser/views/BannerListItemView.cpp create mode 100644 arm9/source/romBrowser/views/BannerListItemView.h create mode 100644 arm9/source/romBrowser/views/BannerView.cpp create mode 100644 arm9/source/romBrowser/views/BannerView.h create mode 100644 arm9/source/romBrowser/views/BottomSheetView.h create mode 100644 arm9/source/romBrowser/views/ChipView.cpp create mode 100644 arm9/source/romBrowser/views/ChipView.h create mode 100644 arm9/source/romBrowser/views/CoverFlowRecyclerView.cpp create mode 100644 arm9/source/romBrowser/views/CoverFlowRecyclerView.h create mode 100644 arm9/source/romBrowser/views/CoverFlowRecyclerViewBase.cpp create mode 100644 arm9/source/romBrowser/views/CoverFlowRecyclerViewBase.h create mode 100644 arm9/source/romBrowser/views/CoverView.cpp create mode 100644 arm9/source/romBrowser/views/CoverView.h create mode 100644 arm9/source/romBrowser/views/DisplaySettingsBottomSheetView.cpp create mode 100644 arm9/source/romBrowser/views/DisplaySettingsBottomSheetView.h create mode 100644 arm9/source/romBrowser/views/IconButton2DView.cpp create mode 100644 arm9/source/romBrowser/views/IconButton2DView.h create mode 100644 arm9/source/romBrowser/views/IconButton3DView.cpp create mode 100644 arm9/source/romBrowser/views/IconButton3DView.h create mode 100644 arm9/source/romBrowser/views/IconButtonView.cpp create mode 100644 arm9/source/romBrowser/views/IconButtonView.h create mode 100644 arm9/source/romBrowser/views/IconGridItemView.cpp create mode 100644 arm9/source/romBrowser/views/IconGridItemView.h create mode 100644 arm9/source/romBrowser/views/NdsGameDetailsBottomSheetView.cpp create mode 100644 arm9/source/romBrowser/views/NdsGameDetailsBottomSheetView.h create mode 100644 arm9/source/romBrowser/views/RomBrowserAppBarView.cpp create mode 100644 arm9/source/romBrowser/views/RomBrowserAppBarView.h create mode 100644 arm9/source/romBrowser/views/RomBrowserBottomScreenView.cpp create mode 100644 arm9/source/romBrowser/views/RomBrowserBottomScreenView.h create mode 100644 arm9/source/romBrowser/views/RomBrowserTopScreenView.cpp create mode 100644 arm9/source/romBrowser/views/RomBrowserTopScreenView.h create mode 100644 arm9/source/romBrowser/views/RomBrowserView.cpp create mode 100644 arm9/source/romBrowser/views/RomBrowserView.h create mode 100644 arm9/source/rtcIpc.cpp create mode 100644 arm9/source/rtcIpc.h create mode 100644 arm9/source/services/process/IProcess.h create mode 100644 arm9/source/services/process/ProcessFactory.h create mode 100644 arm9/source/services/process/ProcessFactory.thumb.cpp create mode 100644 arm9/source/services/process/ProcessManager.cpp create mode 100644 arm9/source/services/process/ProcessManager.h create mode 100644 arm9/source/services/settings/AppSettings.h create mode 100644 arm9/source/services/settings/FileAssociation.h create mode 100644 arm9/source/services/settings/IAppSettingsService.h create mode 100644 arm9/source/services/settings/JsonAppSettingsSerializer.h create mode 100644 arm9/source/services/settings/JsonAppSettingsSerializer.thumb.cpp create mode 100644 arm9/source/services/settings/JsonAppSettingsService.cpp create mode 100644 arm9/source/services/settings/JsonAppSettingsService.h create mode 100644 arm9/source/services/settings/RomBrowserDisplaySettings.h create mode 100644 arm9/source/services/settings/RomBrowserLayout.h create mode 100644 arm9/source/services/settings/RomBrowserSortMode.h create mode 100644 arm9/source/themes/DefaultFontRepository.cpp create mode 100644 arm9/source/themes/DefaultFontRepository.h create mode 100644 arm9/source/themes/FontType.h create mode 100644 arm9/source/themes/IFontRepository.h create mode 100644 arm9/source/themes/ITheme.h create mode 100644 arm9/source/themes/Theme.cpp create mode 100644 arm9/source/themes/Theme.h create mode 100644 arm9/source/themes/ThemeFactory.cpp create mode 100644 arm9/source/themes/ThemeFactory.h create mode 100644 arm9/source/themes/ThemeInfo.h create mode 100644 arm9/source/themes/ThemeInfoFactory.h create mode 100644 arm9/source/themes/ThemeInfoFactory.thumb.cpp create mode 100644 arm9/source/themes/ThemeType.h create mode 100644 arm9/source/themes/background/IThemeBackground.h create mode 100644 arm9/source/themes/custom/CustomMainBackground.cpp create mode 100644 arm9/source/themes/custom/CustomMainBackground.h create mode 100644 arm9/source/themes/custom/CustomSubBackground.cpp create mode 100644 arm9/source/themes/custom/CustomSubBackground.h create mode 100644 arm9/source/themes/custom/CustomTheme.cpp create mode 100644 arm9/source/themes/custom/CustomTheme.h create mode 100644 arm9/source/themes/custom/CustomTopBackgroundType.h create mode 100644 arm9/source/themes/material/MaterialColorScheme.h create mode 100644 arm9/source/themes/material/MaterialColorSchemeFactory.cpp create mode 100644 arm9/source/themes/material/MaterialColorSchemeFactory.h create mode 100644 arm9/source/themes/material/MaterialMainBackground.cpp create mode 100644 arm9/source/themes/material/MaterialMainBackground.h create mode 100644 arm9/source/themes/material/MaterialSubBackground.cpp create mode 100644 arm9/source/themes/material/MaterialSubBackground.h create mode 100644 arm9/source/themes/material/MaterialTheme.cpp create mode 100644 arm9/source/themes/material/MaterialTheme.h create mode 100644 common/core/TickCounter.cpp create mode 100644 common/core/TickCounter.h create mode 100644 common/core/mini-printf.c create mode 100644 common/core/mini-printf.h create mode 100644 common/dldiIpcCommand.h create mode 100644 common/dsiSdIpcCommand.h create mode 100644 common/ipc/IpcService.cpp create mode 100644 common/ipc/IpcService.h create mode 100644 common/ipc/ThreadIpcService.cpp create mode 100644 common/ipc/ThreadIpcService.h create mode 100644 common/ipcChannels.h create mode 100644 common/logger/ILogger.h create mode 100644 common/logger/IOutputStream.h create mode 100644 common/logger/NitroEmulatorOutputStream.cpp create mode 100644 common/logger/NitroEmulatorOutputStream.h create mode 100644 common/logger/NocashOutputStream.h create mode 100644 common/logger/NullLogger.h create mode 100644 common/logger/NullOutputStream.h create mode 100644 common/logger/PicoAgbAdapterOutputStream.cpp create mode 100644 common/logger/PicoAgbAdapterOutputStream.h create mode 100644 common/logger/PlainLogger.h create mode 100644 common/logger/ThreadSafeLogger.h create mode 100644 common/picoAgbAdapter.h create mode 100644 common/picoLoader7.h create mode 100644 common/sharedMemory.h create mode 100644 common/soundIpcCommand.h create mode 100644 docs/Covers.md create mode 100644 docs/FileAssociations.md create mode 100644 docs/Themes.md create mode 100644 docs/Usage.md create mode 100644 docs/images/CoverExample.bmp create mode 100644 docs/images/Coverflow.png create mode 100644 docs/images/Horizontal.png create mode 100644 docs/images/HorizontalCustom.png create mode 100644 docs/images/List.png create mode 100644 docs/images/SettingsPage.png create mode 100644 docs/images/Vertical.png create mode 100644 icon.bmp create mode 160000 libs/libtwl create mode 100644 licenses/ArduinoJson.txt create mode 100644 licenses/Material.txt create mode 100644 licenses/dsi_sdmmc.txt create mode 100644 licenses/mini-printf.txt create mode 100644 licenses/newlib.txt create mode 100644 licenses/tlsf.txt diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2227abb --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.bin binary \ No newline at end of file diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml new file mode 100644 index 0000000..38022c5 --- /dev/null +++ b/.github/workflows/nightly.yml @@ -0,0 +1,37 @@ +name: Build Pico Launcher + +on: + push: + branches: ["develop"] + paths-ignore: + - 'README.md' + pull_request: + branches: ["develop"] + paths-ignore: + - 'README.md' + workflow_dispatch: + +jobs: + pico_launcher: + runs-on: ubuntu-latest + container: skylyrac/blocksds:slim-v1.13.1 + name: Build Pico Launcher + env: + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 + DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: 1 + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + submodules: true + - name: Run build script + run: | + make + - name: Publish build to GH Actions + uses: actions/upload-artifact@v4 + with: + path: | + _pico/ + LAUNCHER.nds + name: Pico Launcher diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..77bce19 --- /dev/null +++ b/.gitignore @@ -0,0 +1,49 @@ +# Ignore build directories # +############################ +Debug/ +Release/ +build/ +out/ + +# Compiled source # +################### +*.com +*.class +*.dll +*.d +*.map +*.o +*.so + +# Packages # +############ +# it's better to unpack these files and commit the raw source +# git has its own built in compression methods +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip + +# Logs and databases # +###################### +*.log +*.sql +*.sqlite + +# OS generated files # +###################### +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db +*.nds +*.elf +*.a +.vscode/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ea93230 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libs/libtwl"] + path = libs/libtwl + url = https://github.com/Gericom/libtwl.git diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..59dc7d2 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,17 @@ +Copyright (c) 2025 LNH team + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9b4a847 --- /dev/null +++ b/Makefile @@ -0,0 +1,110 @@ +# SPDX-License-Identifier: CC0-1.0 +# +# SPDX-FileContributor: Antonio Niño Díaz, 2023 + +BLOCKSDS ?= /opt/blocksds/core +BLOCKSDSEXT ?= /opt/blocksds/external + +export LIBTWL ?= $(shell pwd)/libs/libtwl + +# User config +# =========== + +NAME := LAUNCHER + +GAME_TITLE := Pico Launcher +GAME_AUTHOR := LNH team +GAME_ICON := icon.bmp + +# DLDI and internal SD slot of DSi +# -------------------------------- + +# Root folder of the SD image +SDROOT := sdroot +# Name of the generated image it "DSi-1.sd" for no$gba in DSi mode +SDIMAGE := image.bin + +# Source code paths +# ----------------- + +# A single directory that is the root of NitroFS: +NITROFSDIR := + +# Tools +# ----- + +MAKE := make +RM := rm -rf + +# Verbose flag +# ------------ + +ifeq ($(VERBOSE),1) +V := +else +V := @ +endif + +# Directories +# ----------- + +ARM9DIR := arm9 +ARM7DIR := arm7 + +# Build artfacts +# -------------- + +ROM := $(NAME).nds + +# Targets +# ------- + +.PHONY: all clean arm9 arm7 dldipatch sdimage checklibtwl + +all: $(ROM) + +clean: + @echo " CLEAN" + $(V)$(MAKE) -f Makefile.arm9 clean --no-print-directory + $(V)$(MAKE) -f Makefile.arm7 clean --no-print-directory + $(V)$(RM) $(ROM) build $(SDIMAGE) + +arm9: checklibtwl + $(V)+$(MAKE) -f Makefile.arm9 --no-print-directory + +arm7: checklibtwl + $(V)+$(MAKE) -f Makefile.arm7 --no-print-directory + +checklibtwl: + $(MAKE) -C $(LIBTWL) + +ifneq ($(strip $(NITROFSDIR)),) +# Additional arguments for ndstool +NDSTOOL_ARGS := -d $(NITROFSDIR) + +# Make the NDS ROM depend on the filesystem only if it is needed +$(ROM): $(NITROFSDIR) +endif + +# Combine the title strings +ifeq ($(strip $(GAME_SUBTITLE)),) + GAME_FULL_TITLE := $(GAME_TITLE);$(GAME_AUTHOR) +else + GAME_FULL_TITLE := $(GAME_TITLE);$(GAME_SUBTITLE);$(GAME_AUTHOR) +endif + +$(ROM): arm9 arm7 + @echo " NDSTOOL $@" + $(V)$(BLOCKSDS)/tools/ndstool/ndstool -c $@ \ + -7 build/arm7.elf -9 build/arm9.elf \ + -b $(GAME_ICON) "$(GAME_FULL_TITLE)" \ + $(NDSTOOL_ARGS) + +sdimage: + @echo " MKFATIMG $(SDIMAGE) $(SDROOT)" + $(V)$(BLOCKSDS)/tools/mkfatimg/mkfatimg -t $(SDROOT) $(SDIMAGE) + +dldipatch: $(ROM) + @echo " DLDIPATCH $(ROM)" + $(V)$(BLOCKSDS)/tools/dldipatch/dldipatch patch \ + $(BLOCKSDS)/sys/dldi_r4/r4tf.dldi $(ROM) diff --git a/Makefile.arm7 b/Makefile.arm7 new file mode 100644 index 0000000..335ce01 --- /dev/null +++ b/Makefile.arm7 @@ -0,0 +1,189 @@ +# SPDX-License-Identifier: CC0-1.0 +# +# SPDX-FileContributor: Antonio Niño Díaz, 2023 + +export BLOCKSDS ?= /opt/blocksds/core +export BLOCKSDSEXT ?= /opt/blocksds/external + +export LIBTWL ?= $(shell pwd)/libs/libtwl + +export WONDERFUL_TOOLCHAIN ?= /opt/wonderful +ARM_NONE_EABI_PATH ?= $(WONDERFUL_TOOLCHAIN)/toolchain/gcc-arm-none-eabi/bin/ + +# Source code paths +# ----------------- + +SOURCEDIRS := arm7/source common +INCLUDEDIRS := arm7/source common +BINDIRS := + +# Defines passed to all files +# --------------------------- + +DEFINES := -DLIBTWL_ARM7 + +# Libraries +# --------- + +LIBS := -ltwl7 -lnds7 +LIBDIRS := $(BLOCKSDS)/libs/libnds \ + $(LIBTWL)/libtwl7 $(LIBTWL)/common $(LIBTWL) + +# Build artifacts +# ----------------- + +NAME := arm7 +BUILDDIR := build/$(NAME) +ELF := build/$(NAME).elf +DUMP := build/$(NAME).dump +MAP := build/$(NAME).map + +# Tools +# ----- + +PREFIX := $(ARM_NONE_EABI_PATH)arm-none-eabi- +CC := $(PREFIX)gcc +CXX := $(PREFIX)g++ +LD := $(PREFIX)gcc +OBJDUMP := $(PREFIX)objdump +MKDIR := mkdir +RM := rm -rf + +# Verbose flag +# ------------ + +ifeq ($(VERBOSE),1) +V := +else +V := @ +endif + +# Source files +# ------------ + +ifneq ($(BINDIRS),) + SOURCES_BIN := $(shell find -L $(BINDIRS) -name "*.bin") + INCLUDEDIRS += $(addprefix $(BUILDDIR)/,$(BINDIRS)) +endif + +SOURCES_S := $(shell find -L $(SOURCEDIRS) -name "*.s") +SOURCES_C := $(shell find -L $(SOURCEDIRS) -name "*.c") +SOURCES_CPP := $(shell find -L $(SOURCEDIRS) -name "*.cpp") + +# Compiler and linker flags +# ------------------------- + +ARCH := -marm -mthumb-interwork -mcpu=arm7tdmi + +SPECS := arm7/dldi_ds_arm7.specs + +WARNFLAGS := -Wall + +ifeq ($(SOURCES_CPP),) + LIBS += -lc +else + LIBS += -lstdc++ -lc +endif + +INCLUDEFLAGS := $(foreach path,$(INCLUDEDIRS),-I$(path)) \ + $(foreach path,$(LIBDIRS),-I$(path)/include) + +LIBDIRSFLAGS := $(foreach path,$(LIBDIRS),-L$(path)/lib) + +ASFLAGS += -x assembler-with-cpp $(DEFINES) $(INCLUDEFLAGS) \ + $(ARCH) -ffunction-sections -fdata-sections \ + -specs=$(SPECS) + +CFLAGS += -std=gnu17 $(WARNFLAGS) $(DEFINES) $(INCLUDEFLAGS) \ + $(ARCH) -O2 -ffunction-sections -fdata-sections \ + -specs=$(SPECS) + +CXXFLAGS += -std=gnu++23 $(WARNFLAGS) $(DEFINES) $(INCLUDEFLAGS) \ + $(ARCH) -O2 -ffunction-sections -fdata-sections \ + -fno-exceptions -fno-rtti \ + -fno-threadsafe-statics \ + -Wsuggest-override -Werror=suggest-override \ + -specs=$(SPECS) + +LDFLAGS := $(ARCH) $(LIBDIRSFLAGS) -Wl,-Map,$(MAP),--gc-sections $(DEFINES) \ + -Wl,--start-group $(LIBS) -Wl,--end-group -specs=$(SPECS) + +# Intermediate build files +# ------------------------ + +OBJS_ASSETS := $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_BIN))) + +HEADERS_ASSETS := $(patsubst %.bin,%_bin.h,$(addprefix $(BUILDDIR)/,$(SOURCES_BIN))) + +OBJS_SOURCES := $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_S))) \ + $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_C))) \ + $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_CPP))) + +OBJS := $(OBJS_ASSETS) $(OBJS_SOURCES) + +DEPS := $(OBJS:.o=.d) + +# Targets +# ------- + +.PHONY: all clean dump + +all: $(ELF) + +$(ELF): $(OBJS) + @echo " LD.7 $@" + $(V)$(LD) -o $@ $(OBJS) $(LDFLAGS) + +$(DUMP): $(ELF) + @echo " OBJDUMP.7 $@" + $(V)$(OBJDUMP) -h -C -S $< > $@ + +dump: $(DUMP) + +clean: + @echo " CLEAN.7" + $(V)$(RM) $(ELF) $(DUMP) $(MAP) $(BUILDDIR) + +# Rules +# ----- + +$(BUILDDIR)/%.s.o : %.s + @echo " AS.7 $<" + @$(MKDIR) -p $(@D) + $(V)$(CC) $(ASFLAGS) -MMD -MP -c -o $@ $< + +$(BUILDDIR)/%.c.o : %.c + @echo " CC.7 $<" + @$(MKDIR) -p $(@D) + $(V)$(CC) $(CFLAGS) -MMD -MP -c -o $@ $< + +$(BUILDDIR)/%.arm.c.o : %.arm.c + @echo " CC.7 $<" + @$(MKDIR) -p $(@D) + $(V)$(CC) $(CFLAGS) -MMD -MP -marm -mlong-calls -c -o $@ $< + +$(BUILDDIR)/%.cpp.o : %.cpp + @echo " CXX.7 $<" + @$(MKDIR) -p $(@D) + $(V)$(CXX) $(CXXFLAGS) -MMD -MP -c -o $@ $< + +$(BUILDDIR)/%.arm.cpp.o : %.arm.cpp + @echo " CXX.7 $<" + @$(MKDIR) -p $(@D) + $(V)$(CXX) $(CXXFLAGS) -MMD -MP -marm -mlong-calls -c -o $@ $< + +$(BUILDDIR)/%.bin.o $(BUILDDIR)/%_bin.h : %.bin + @echo " BIN2C.7 $<" + @$(MKDIR) -p $(@D) + $(V)$(BLOCKSDS)/tools/bin2c/bin2c $< $(@D) + $(V)$(CC) $(CFLAGS) -MMD -MP -c -o $(BUILDDIR)/$*.bin.o $(BUILDDIR)/$*_bin.c + +# All assets must be built before the source code +# ----------------------------------------------- + +$(SOURCES_S) $(SOURCES_C) $(SOURCES_CPP): $(HEADERS_ASSETS) + +# Include dependency files if they exist +# -------------------------------------- + +-include $(DEPS) diff --git a/Makefile.arm9 b/Makefile.arm9 new file mode 100644 index 0000000..f743e63 --- /dev/null +++ b/Makefile.arm9 @@ -0,0 +1,243 @@ +# SPDX-License-Identifier: CC0-1.0 +# +# SPDX-FileContributor: Antonio Niño Díaz, 2023 + +export BLOCKSDS ?= /opt/blocksds/core +export BLOCKSDSEXT ?= /opt/blocksds/external + +export LIBTWL ?= $(shell pwd)/libs/libtwl + +export WONDERFUL_TOOLCHAIN ?= /opt/wonderful +ARM_NONE_EABI_PATH ?= $(WONDERFUL_TOOLCHAIN)/toolchain/gcc-arm-none-eabi/bin/ + +# Source code paths +# ----------------- + +SOURCEDIRS := arm9/source common +INCLUDEDIRS := arm9/source common +GFXDIRS := arm9/gfx +BINDIRS := arm9/data +AUDIODIRS := + +# Defines passed to all files +# --------------------------- + +DEFINES := -DLIBTWL_ARM9 + +# Libraries +# --------- + +LIBS := -ltwl9 -lnds9 +LIBDIRS := $(BLOCKSDS)/libs/libnds \ + $(LIBTWL)/libtwl9 $(LIBTWL)/common $(LIBTWL) + +# Build artifacts +# --------------- + +NAME := arm9 +BUILDDIR := build/$(NAME) +ELF := build/$(NAME).elf +DUMP := build/$(NAME).dump +MAP := build/$(NAME).map +SOUNDBANKDIR := $(BUILDDIR)/maxmod + +# Tools +# ----- + +PREFIX := $(ARM_NONE_EABI_PATH)arm-none-eabi- +CC := $(PREFIX)gcc +CXX := $(PREFIX)g++ +LD := $(PREFIX)gcc +OBJDUMP := $(PREFIX)objdump +MKDIR := mkdir +RM := rm -rf + +# Verbose flag +# ------------ + +ifeq ($(VERBOSE),1) +V := +else +V := @ +endif + +# Source files +# ------------ + +ifneq ($(BINDIRS),) + SOURCES_BIN := $(shell find -L $(BINDIRS) -name "*.bin") + SOURCES_NFT2 := $(shell find -L $(BINDIRS) -name "*.nft2") + INCLUDEDIRS += $(addprefix $(BUILDDIR)/,$(BINDIRS)) +endif +ifneq ($(GFXDIRS),) + SOURCES_PNG := $(shell find -L $(GFXDIRS) -name "*.png") + INCLUDEDIRS += $(addprefix $(BUILDDIR)/,$(GFXDIRS)) +endif +ifneq ($(AUDIODIRS),) + SOURCES_AUDIO := $(shell find -L $(AUDIODIRS) -regex '.*\.\(it\|mod\|s3m\|wav\|xm\)') + ifneq ($(SOURCES_AUDIO),) + INCLUDEDIRS += $(SOUNDBANKDIR) + endif +endif + +SOURCES_S := $(shell find -L $(SOURCEDIRS) -name "*.s") +SOURCES_C := $(shell find -L $(SOURCEDIRS) -name "*.c") +SOURCES_CPP := $(shell find -L $(SOURCEDIRS) -name "*.cpp") + +# Compiler and linker flags +# ------------------------- + +ARCH := -marm -mthumb-interwork -mcpu=arm946e-s+nofp + +SPECS := $(BLOCKSDS)/sys/crts/ds_arm9.specs + +WARNFLAGS := -Wall + +ifeq ($(SOURCES_CPP),) + LIBS += -lc +else + LIBS += -lstdc++ -lc +endif + +INCLUDEFLAGS := $(foreach path,$(INCLUDEDIRS),-I$(path)) \ + $(foreach path,$(LIBDIRS),-I$(path)/include) + +LIBDIRSFLAGS := $(foreach path,$(LIBDIRS),-L$(path)/lib) + +ASFLAGS += -x assembler-with-cpp $(DEFINES) $(INCLUDEFLAGS) \ + $(ARCH) -ffunction-sections -fdata-sections \ + -specs=$(SPECS) + +CFLAGS += -std=gnu17 $(WARNFLAGS) $(DEFINES) $(INCLUDEFLAGS) \ + $(ARCH) -O2 -ffunction-sections -fdata-sections \ + -fno-devirtualize-speculatively \ + -Werror=return-type \ + -specs=$(SPECS) + +CXXFLAGS += -std=gnu++23 $(WARNFLAGS) $(DEFINES) $(INCLUDEFLAGS) \ + $(ARCH) -O2 -ffunction-sections -fdata-sections \ + -fno-exceptions -fno-rtti \ + -fno-devirtualize-speculatively \ + -Werror=return-type \ + -fno-threadsafe-statics \ + -Wno-volatile -Wsuggest-override -Werror=suggest-override \ + -specs=$(SPECS) + +LDFLAGS := $(ARCH) $(LIBDIRSFLAGS) -Wl,-Map,$(MAP),--gc-sections,--use-blx $(DEFINES) \ + -Wl,--start-group $(LIBS) -Wl,--end-group -specs=$(SPECS) + +# Intermediate build files +# ------------------------ + +OBJS_ASSETS := $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_BIN))) \ + $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_NFT2))) \ + $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_PNG))) + +HEADERS_ASSETS := $(patsubst %.bin,%_bin.h,$(addprefix $(BUILDDIR)/,$(SOURCES_BIN))) \ + $(patsubst %.nft2,%_nft2.h,$(addprefix $(BUILDDIR)/,$(SOURCES_NFT2))) \ + $(patsubst %.png,%.h,$(addprefix $(BUILDDIR)/,$(SOURCES_PNG))) + +ifneq ($(SOURCES_AUDIO),) + OBJS_ASSETS += $(SOUNDBANKDIR)/soundbank.c.o + HEADERS_ASSETS += $(SOUNDBANKDIR)/soundbank.h +endif + +OBJS_SOURCES := $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_S))) \ + $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_C))) \ + $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_CPP))) + +OBJS := $(OBJS_ASSETS) $(OBJS_SOURCES) + +DEPS := $(OBJS:.o=.d) + +# Targets +# ------- + +.PHONY: all clean dump + +all: $(ELF) + +$(ELF): $(OBJS) + @echo " LD.9 $@" + $(V)$(LD) -o $@ $(OBJS) $(LDFLAGS) + +$(DUMP): $(ELF) + @echo " OBJDUMP.9 $@" + $(V)$(OBJDUMP) -h -C -S $< > $@ + +dump: $(DUMP) + +clean: + @echo " CLEAN.9" + $(V)$(RM) $(ELF) $(DUMP) $(MAP) $(BUILDDIR) + +# Rules +# ----- + +$(BUILDDIR)/%.s.o : %.s + @echo " AS.9 $<" + @$(MKDIR) -p $(@D) + $(V)$(CC) $(ASFLAGS) -MMD -MP -c -o $@ $< + +$(BUILDDIR)/%.c.o : %.c + @echo " CC.9 $<" + @$(MKDIR) -p $(@D) + $(V)$(CC) $(CFLAGS) -MMD -MP -c -o $@ $< + +$(BUILDDIR)/%.arm.c.o : %.arm.c + @echo " CC.9 $<" + @$(MKDIR) -p $(@D) + $(V)$(CC) $(CFLAGS) -MMD -MP -marm -mlong-calls -c -o $@ $< + +$(BUILDDIR)/%.cpp.o : %.cpp + @echo " CXX.9 $<" + @$(MKDIR) -p $(@D) + $(V)$(CXX) $(CXXFLAGS) -MMD -MP -c -o $@ $< + +$(BUILDDIR)/%.arm.cpp.o : %.arm.cpp + @echo " CXX.9 $<" + @$(MKDIR) -p $(@D) + $(V)$(CXX) $(CXXFLAGS) -MMD -MP -marm -mlong-calls -c -o $@ $< + +$(BUILDDIR)/%.bin.o $(BUILDDIR)/%_bin.h : %.bin + @echo " BIN2C.9 $<" + @$(MKDIR) -p $(@D) + $(V)$(BLOCKSDS)/tools/bin2c/bin2c $< $(@D) + $(V)$(CC) $(CFLAGS) -MMD -MP -c -o $(BUILDDIR)/$*.bin.o $(BUILDDIR)/$*_bin.c + +$(BUILDDIR)/%.nft2.o $(BUILDDIR)/%_nft2.h : %.nft2 + @echo " BIN2C.9 $<" + @$(MKDIR) -p $(@D) + $(V)$(BLOCKSDS)/tools/bin2c/bin2c $< $(@D) + $(V)$(CC) $(CFLAGS) -MMD -MP -c -o $(BUILDDIR)/$*.nft2.o $(BUILDDIR)/$*_nft2.c + +$(BUILDDIR)/%.png.o $(BUILDDIR)/%.h : %.png %.grit + @echo " GRIT.9 $<" + @$(MKDIR) -p $(@D) + $(V)$(BLOCKSDS)/tools/grit/grit $< -ftc -W1 -o$(BUILDDIR)/$* + $(V)$(CC) $(CFLAGS) -MMD -MP -c -o $(BUILDDIR)/$*.png.o $(BUILDDIR)/$*.c + $(V)touch $(BUILDDIR)/$*.png.o $(BUILDDIR)/$*.h + +$(SOUNDBANKDIR)/soundbank.h: $(SOURCES_AUDIO) + @echo " MMUTIL $^" + @$(MKDIR) -p $(@D) + @$(BLOCKSDS)/tools/mmutil/mmutil $^ -d \ + -o$(SOUNDBANKDIR)/soundbank.bin -h$(SOUNDBANKDIR)/soundbank.h + +$(SOUNDBANKDIR)/soundbank.c.o: $(SOUNDBANKDIR)/soundbank.h + @echo " BIN2C soundbank.bin" + $(V)$(BLOCKSDS)/tools/bin2c/bin2c $(SOUNDBANKDIR)/soundbank.bin \ + $(SOUNDBANKDIR) + @echo " CC.9 soundbank_bin.c" + $(V)$(CC) $(CFLAGS) -MMD -MP -c -o $(SOUNDBANKDIR)/soundbank.c.o \ + $(SOUNDBANKDIR)/soundbank_bin.c + +# All assets must be built before the source code +# ----------------------------------------------- + +$(SOURCES_S) $(SOURCES_C) $(SOURCES_CPP): $(HEADERS_ASSETS) + +# Include dependency files if they exist +# -------------------------------------- + +-include $(DEPS) diff --git a/README.md b/README.md new file mode 100644 index 0000000..6a7c1a5 --- /dev/null +++ b/README.md @@ -0,0 +1,81 @@ +# Pico Launcher +This repository contains Pico Launcher, which is a front-end for [Pico Loader](https://github.com/LNH-team/pico-loader). + +![Horizontal display mode with custom theme](docs/images/HorizontalCustom.png) +![Banner list display mode](docs/images/List.png) +![Coverflow display mode](docs/images/Coverflow.png) + +## Features +- Can load homebrew and retail games using [Pico Loader](https://github.com/LNH-team/pico-loader). +- Various display modes + - Horizontal and vertical icon grid + - Banner list + - Coverflow +- [File associations](docs/FileAssociations.md) +- [Covers](docs/Covers.md) +- [Material Design 3 and custom themes](docs/Themes.md) +- Support for background music (see [Themes](docs/Themes.md)) + +General usage documentation can be found here: [Usage](docs/Usage.md). + +## Setup & Configuration +We recommend using WSL (Windows Subsystem for Linux), or MSYS2 to compile this repository. +The steps provided will assume you already have one of those environments set up. + +1. Install [BlocksDS](https://blocksds.skylyrac.net/docs/setup/options/) + +## Compiling + +1. Run `make` + +The launcher can be found in the root directory under the name `LAUNCHER.nds`. + +2. Copy `LAUNCHER.nds` to your SD card. + - If you are using DSpico, rename to `_picoboot.nds` and place it in the root of your SD card. +3. Copy the `_pico` pico folder to the root of your SD card. + +> [!NOTE] +> To use Pico Launcher, the Pico Loader files (`aplist.bin`, `savelist.bin`, `picoLoader7.bin` and `picoLoader9.bin`) must also be present in the `/_pico` folder on your SD card. + +For DSpico the final directory structure will look like this: +``` +. +├── _pico +│ ├── themes +│ │ ├── material +│ │ │ └── theme.json +│ │ └── raspberry +│ │ ├── bannerListCell.bin +│ │ ├── bannerListCellPltt.bin +│ │ ├── bannerListCellSelected.bin +│ │ ├── bannerListCellSelectedPltt.bin +│ │ ├── bottombg.bin +│ │ ├── gridcell.bin +│ │ ├── gridcellPltt.bin +│ │ ├── gridcellSelected.bin +│ │ ├── gridcellSelectedPltt.bin +│ │ ├── scrim.bin +│ │ ├── scrimPltt.bin +│ │ ├── theme.json +│ │ └── topbg.bin +│ ├── aplist.bin +│ ├── savelist.bin +│ ├── picoLoader7.bin +│ └── picoLoader9.bin +└── _picoboot.nds +``` +Note: If you want to play DSiWare on the DSpico, additional files are required. See the [Pico Loader](https://github.com/LNH-team/pico-loader) readme for more information. + +## License + +Icons by [icons8](https://icons8.com/) + +This project is licensed under the Zlib license. For details, see `LICENSE.txt`. + +Additional licenses may apply to the project. For details, see the `license` directory. + +## Contributors +- [@Gericom](https://github.com/Gericom) +- [@XLuma](https://github.com/XLuma) +- [@Dartz150](https://github.com/Dartz150) +- [@lifehackerhansol](https://github.com/lifehackerhansol) diff --git a/_pico/themes/material/theme.json b/_pico/themes/material/theme.json new file mode 100644 index 0000000..b037198 --- /dev/null +++ b/_pico/themes/material/theme.json @@ -0,0 +1,12 @@ +{ + "type": "material", + "name": "Material Design 3", + "description": "Theme based on Google's Material Design 3.", + "author": "Gericom", + "primaryColor": { + "r": 138, + "g": 217, + "b": 255 + }, + "darkTheme": false +} \ No newline at end of file diff --git a/_pico/themes/raspberry/bannerListCell.bin b/_pico/themes/raspberry/bannerListCell.bin new file mode 100644 index 0000000000000000000000000000000000000000..d6b7cd5e22ab6c3d79428e13239511f7e5badc1e GIT binary patch literal 12544 zcmb0c1*0J_qC zx&BwUGc5dnM}f@rKkEO{_#Xx#Fq-~H^FKKa0EW@>Zy4nMIdewy|7iXn&Hto_z-ak5 z+WsF76~JiuKU)5iUi!(%jFx|+<=<%eH}pebwEQ0}|3}Mz(nDah{u{0ThJNWkI{q_S z|Bcpvq?i7q{h!hLZ?yg!t^bC82#{a@4U_dRWUv32GA!0VO(EC+3`SFK4fW-3cWzBF vB6s~0gMxy?&<6?%Wc#0iL1E|v&?;7Pntw7Njl%?!<^PebX_&-3+1>yEPBtbc literal 0 HcmV?d00001 diff --git a/_pico/themes/raspberry/bannerListCellPltt.bin b/_pico/themes/raspberry/bannerListCellPltt.bin new file mode 100644 index 0000000000000000000000000000000000000000..1a2c743e99407d84d1552bcace41417f39322fdd GIT binary patch literal 64 fcmZP|0yuGSC`u%QV0HeS;=Sefs_#|*uZKVYlvNG9 literal 0 HcmV?d00001 diff --git a/_pico/themes/raspberry/bannerListCellSelected.bin b/_pico/themes/raspberry/bannerListCellSelected.bin new file mode 100644 index 0000000000000000000000000000000000000000..dd80db446f6d562082930374f453a8713ced1012 GIT binary patch literal 12544 zcmb0d1*0J_qC*FL*Mtme*KzGw*QBP_kaB&*Z&H` z#QzFprvFj@503;e8vo>#|D)-DH2n{U5E#w>qxqkV5MUTB|As^E|MhD$|C5pWrKCpF z|L{ouqv?ON|3^*%Fq;2I^Z&32fzkS(oa{eZ|Btr+heh@uZU2n6f5<5SN83NcBm0l$ z|Iz$En*T`;fzk4BwEP7T)9I0V2JBXZY2F(@cF j41J)WK(_xG7!-y+0Igyrulxh)93Ge~|Bq@K$sqs$;hGxD literal 0 HcmV?d00001 diff --git a/_pico/themes/raspberry/bannerListCellSelectedPltt.bin b/_pico/themes/raspberry/bannerListCellSelectedPltt.bin new file mode 100644 index 0000000000000000000000000000000000000000..77f11308df37270166543d78d4a1d6fb5d0db5b2 GIT binary patch literal 64 ecmZP|0wnWjs#>O7rblJZDV|fjxBP!S0s;W8H4g~@ literal 0 HcmV?d00001 diff --git a/_pico/themes/raspberry/bottombg.bin b/_pico/themes/raspberry/bottombg.bin new file mode 100644 index 0000000..02f9deb --- /dev/null +++ b/_pico/themes/raspberry/bottombg.bin @@ -0,0 +1,641 @@ +S4Sų5UU5͵ɔT4334ҨӨҨҨҨ10QqQPQQ͒űűűőq0/pP/o//01356t3wɵ6wΕӬ԰SSs⓽ӬԬӬssRRQ1115sQ3r22Q111Q0100000/p3tpϨθ񬱨pO/NO...ONNN..---M----, -,,,,KlK,,,,,,+,,,++,+ +-ljˬpjPˬ̨4Sɳ345U45ʹɕt43ӬҨ01QQ00Qr0ͱűŒq00/0//O///Oq36Sŗ޴ŶɴŖ86w8V3Ӭ԰t33ӬӬӬrsRRq11QrRRsQQr1Q110100pPPq5pP/N/N.....NN-...-MM-.,-- -,,,,,LL,,,Kll,,,,+,, ,+Mk O֬jR444T55ѵɵɔS331ҨӬҨ000QQQP0QqR1ɑőɑQ0qo00O/.op/P5USҵɶɶҗޖvu԰ӬӰ4ӬӬԨrRQRQQ00Q22RRQRQ10P11000/01RqЬpPɳop...N....n..NnMNϨMMLM- -,L ,,LKKL,,,,+,L+,l, lk ɌʤˤS334StT4͵ɴɔT433RѨҬҬѬ000QQ10Q11ұőqőq0P0P//Q3USɶ5uڔŴԬrqrӬRRQQ1011QRR12rRQ10QOQ001pҸҬQ0P1ON...--...-...N -Pm-L-M- , ,,,,,m+,,+,L+,,,,,l+,Mlk 쬬ʨlmk̰-jk3S3SSSTST4ʹɵɴɵɵɵɔtT323Ѩ00qQPPQQɑűőpp/pO/QPvrxڕŶɕŵɵŕŕ6ҕuT5ttӬ󬓤󰓤rQR2QQRr1010s1rQQP1q01Pq0Po//O/NON..--..-.-.-..-nMLL,Ψ,Lm,,, ,, ,,,LL+,,,LL,LLMk̨MLkK+鬌Ln,ˬ3R2sޓtSSS3ɔŕŵɴŔŔŔS3򰲨Q00PQQ00q01͐őp/0P//00001044ΔUuuutuŵUTwtɔtӨQ2QQ10110QRQQ1100Op/0/////.ONNN-..-.-..---.- M,M-,L,ML,,,,,,+,,,,,,,,KlKL,*쬌̨2ҋMSڭɌkŬ/ JLSִsS3ѵɴɔŔŴŔŔsS3ҬѬѨ񬰤qqP000qPQ0Q1ҰűŐp0/00/////000000QRuTuɕŔŵ5Tҕ6֔ʹStӬӬ󬲤QQq2Q0100111R000o0/O/..///NN.....------N,--,, ,-,lKk,K, ,,,,+,,,LKLlL,+묌.nkJ˨ɭO.Oq 3sSڳsɵɴŴŴŔɔŔs3ҬѨѨqq2p00Q0Qq1QPɱŰő0//OO/////P/0ppPPQ1uSttUtֵ޶޵ŴŔtuӬӨҬrrrqqQ2Q2q00100Ҥ00/UPPNo/./O.N..N-..-N-.N------N,--,-,L,l,lL,, ,, ,,,L,,,l,,,,kꬋ kK,r//.pɌ + )IS3sss2ʹ͵ʹɴɔŴɔɴt3ѬѨѬѨѨqqqp0P1100pqQ2ɰűŰp//OPOOP///O///0OPPPQ3S5ғtŕ5ɖtuɔtӬ԰ҨӬ򬒠rrrrrR111Q1Q0000oPOOOON...oo....-...NM------- , , ,,-,,L,,,L+,,,,,,KLL,KL+L+K̬kkJkPPKˬLqޑ)s333ͲɴɴɴɴɵɴɴtrѬѨѨѬѬѨФѨqSůq0P00p1qڑڑQɑŰq//PopP/O0///P///POPPpppPP2Rt65U4S4VUw3ӬӬҤqrrQrqQ1110111P0000ɳųp/P/NNNN.Opn.--N.M.--MM- - ,,L ,,,,,,,KL,LlkL+,L++,+,+,+,kʬ./1Ү+Kŋ//s͓sɴʹɴɴɴ͵ɔtSҬѨѨѨѨѬѨѨѬѨѬѨ1PP0Qp0pڑqq1ҰŰűp//OOO/O0/P/pOOpppppPpppp0s42ŵu8VU4vWڵUӬ򨒤qQr21111100qqO14ғ00Ѩp0////NN./N.-N.O-nN--M-- ---. ,LlL, -,,,,,LKlLK̤ KL+KK,L,,*kN1ˬ0JpPmҋ*ŬɌksœͳɓRsʹɴs2ҬѬѨѬѬѨѬѨѬѨѨѬѨШ1Q/֐oQqQPڑޑpQ2ͰŰŐP00///ppOPPOppŐŐőppɑQ2βSuŕ6ַ57⓽55UsӬSTӨqRrr1111100poP0R91q0Фp0/P/.O..N..N.n-- .-.---- -N. ,l,-,,,,,,,,,lK,LKLK˨MjKKK+KK,+kkNlLO-+/ //O֊stœųɔɓrSRsɴ֔3ҬҬѨѬѬѬѬѨѨШѨѨШQQְpŐҐڲq1ɱűɰŰp0//0PPpPͰűŏppɐŏŰɑŏpŰS֑tu64tUsTӨ44ҨqQqQq01Ҥ/ЬX񬐠ϨѨ///OONNO-....MN..--n-M--- NMNMNLM,,,,,,LK,,LLKK,LKNjKKKKkK++ʨ jj +͍r ͊sstsssS23SŴɴʹYZs2ҬѨѨѬѨѨѬѨѨШѬpёoͰɰŰP֑QɐűŐp0//POPPooɰŏpŐŐŰɰɐɱS4Ttŕŵ3ҔŔSޓtӬrӨӤq110Q0P4ҕ޵O/oNONO/OONO.......N.MM -N--, -- .pɮnLLlL,,,,+L+L,,,+, ,+K.K+KK++ 0,kkPґ-. k KSssSrR322sŴɴɴSXYYRҬѬҬѨҬѬѬѨШШp֑޲OҳrҰqڲqɱőŐP0OPoPpppŰɰɐŏɱɰҰŐ2RT3SSTUu6ҴɔT7SҨu󬳨ԬӨqqqq1s2/1QШѬ/P/..NN.oNNO....oN.-NMm-.- -------N.PnML,,,,+,LL,K,kL+,+, ,++NjKKKK+,*MkS̨֫lo oͫkɨSSsSSS22SŴɔɓs8YҨҬҨѨѨѬѨѬѬЬqOqޑO/4/֑޲QҰɱŐpp0/PoppŐ1ұSޓ2ʷuTɶ͵͕utSSs3ӬӨrqrQ0PsP11Q1Ru摹QpP//..../.ON....NN.MnNn------- --. nҮŬL,,L,,,KK,K+,l+,KΤk,,,+kNKkk+++ ȨL +* ˨*.N-oҊ͍ 3SssR322sŴɓų͔޵3QҬҰѨҬѬѬѨѬѬЬQ1OPpp/.PPP֑PѱɑqppP0/oŐŰŰŐŰQQQ110128ʱS2TuɵɔŕɕtTSsT4Ӭ33tӨqqqQ010041rӬSűoO/0//...../NNNN..--.NM-M--. ----,- nmQ0ү̬mLLLL+,, +,,,+,lj+,++,+ KKK+,+J騍L΍ʨʤK ΐҫ OҌ +33SS23SŔɓŒŒQѬѬШЬЬ1ɑޑpOpO.o5wޱ4ww5PɑŐpqpPP/pŰůŰɱr00qPP0QQQRQ00QR2stVtutŔtuŔTsͳ33ҤҬӨӬrRqqR0Q00P1QҨpⰤpp/o///....../NoNoMM-.N-NN---------- - ΰMlLLLL,,KL+,,+LͤlkKlKK++쨋KK+++*ɤ )LLO ͬ΋0K222212SssssrRQ1222R2ѬШp1ppNO654WޱPɰőŐŐŰppop0QQPPqP0qڲpQQ0QֳqSδ1STTTTSsStTӬus󬲤󰲤rҨQ1000QrTwtQqP0OO/0.O....nN...NM...---N-M------- -- nMLmLlLL,,,+kL,,,,L+KJK+lKk+++JjKKK *) j Opklm222Ssr111QsrrrŒrrQQѬ0ɑޱoڐwwW45wޑpҰɰɲɰΑpͰQqQrڑޑqޑp޲p/QΓRғړ343ɴRɔ򬲨Ҭ11001pqP0POO////....-N--N..-. .-.- ----- --- L/MLLL,L,L+,,,+++,+,++K+,+++KkK,++KK+++ ȠQꬪP.ҰOj+ʨʤ + 222RsŲRRRrŲɒɲͲɒqR0pڱޑW5wx5wޑޑޒұ0QҰŰůͱɰɰɯ0PPQ163prڳQޒsQ1p4W4tSTTqq1000qrɖ0vvҰPqPO/P//.Q....-mnM.NM---- -M--M-------- LLL-,,,LL,,,,,,,,+++KNk++,+++++++* jKK+++i,kLLkP o֐-* +묪RR22rr2rRrŲ͒qQϬ0PֱޱV4wVVVpQQ1ͱ0p0ҐůQڐŰɰ0pqޒq0q56ސڲ4O.0ғmҰU2ttTtStSҬqqtqq0/0񬲽r/OpON/..--..-N-------------,- -,- OmmlL,LLLLLK,+,K,+,+,+KkK ++K+K++++JJKK+ jmklLk- ґj+ Kk΍ +QRRrŲ3R2ͲrŲ͒q1ЬϨґޱ5VVVV5qͱɱɰɰPڑɰɓ͏ůPqppqڑޒPR5ޑޱޑpִ6PT4sSҰҰҰ3tSuҬSqpҬ2ҬѬQ0/QőTTѨOP0//p.NN./-.-..-...------------ --  ,-,nΨlLl,,,,LLL,,+,+,++++KK+++++KK+++*+*J++++ɨ kK n 1Β* + llKRQQq23S2ѲɒŒɲѲͲɒQQЬϨpPڲ5Nְ5wVqɐŐŐŐŐŐŰppqR1ҏq޳PpP1pސޑp/ҳ.PpP֓ޑ4RSҲӬҬӰҬҬӬ3SqӬQpҬqP/OPPPpP1uڶҬP/0/O.N.N.......-. N--M- -- - - ,- ,,쨭lLlLL,,,L+K,,+,++,++,lKL+,KK++K++*++J JK+ +m̨kllLKl/Ҭ+ɭQQQ2222ͲrŒŒɲͲɒŒrQ1ЬϨШϬO/ґn,p51͐qpŐɯrpPʹqr޲Pqqڑڳ632ҬҬӬҬҬҬҬҬҬҬҬrpppҬT򰱤pQqѨr޴Ѥp//O.......N. .--N--- - L,LKklLL,,,,,,,+, ,,,+ +++++K++++K+++++*KKK++Ŏ˨lk̨ +qެŭklQQqqű͒ɒrqqrqQ1ЬϨϨϨϨΨq.Nowx0pppOpŐŰŰR01Pֱޒ޲P655qW޳rr3ҬҬҬҬҬҨҬҬҬҨqQrpPs򰱨ҨsTSW1Ѥq4ҐpOO//...-. ...M --.-. . - - -- -,-̨,,LLL,L,,,,L+L,+,,+,++++++++++K*K++*K++KKKK*+) 2nk/ҬŌkk0QQqqŲɲɑőrqrqQ111ϬϨϨϨϨϨϨo0q/.pUVp͐PPppɯɱ6ps511//pڑqPqWO/P5/RRQ0rVִ򰲨ѬҬҬҬҬѨqpp2Ŕ򰲤򬱨ѨШѬҬ/op//.........M- - -- M- .- ,LnpשּׁL,LL,,,,,,,,K,,L+++,,Kl *+*++++JKlJK+JKK*JljJ+J1֌ kkpڐ/ŭŭ00PQqpqqQqqrq1ϨЬϨϨϨϨϨ/Op4T/oppŐ͐UOPpWQ11Q./0ҳ///OPp5r֖ӰRpѨҬҬѨѨҬqqpRҬҨQpPӬO0/PO/pO/.N-..... .N. N----MMMMmϨ0P/ͨOLL+LLL+,,,, ,+,+,++,,JjL,,,JKK++++*++KJ*K+*Jkjj**IŌ+kjjMkk010QqrQ1ЬϨЬϨϨϬЬϨϨ/pұwWOҰŰŐŰŐpŰŏŐPޱ5w2r1֒/PQ֐ůP֑ޓpop55oڵTUֵRSҨҨҬѨҬҬѬҬqqճs132Tpq0q0//oO//..N-........---.ΨϬlllKL,,,+L,,,,,,,,,,K,, ,,kk KkkK+JK+JKkJJ*K+++KKKKkJK+* +I+jJJik͌K/1QQrQ0ЬϨϬϨЬЬϬ//4xPͰŐɰooŐo͐4pɭpV5627-Psڒ0T֒ڒp޳RRS֒1RҬѬ򬱨ѨѨѤqqppѬ5Uo00///rON...nM...-NNM..ϬpOmMmm-L-L,,+,LKl+,, , ,, +, ,,Nklk+KKjKKKKKKK*KJ+++K,KKKKJK+ ***K Jj)pګŬϬϬϬQqrrQϨϨϨϨШϨϬϬϬϨϬpOwwp0ɰŰpOoŏͰ0pW5V1Ғ, Ѵ͓0p6pڲpڑOp޳6RUЬѨ񬱤3PpPOѨRsSsRѤP/0oN/oШ.N-N..-n./.mnNnNQqnmMMM-,L,,mLLLLLL K𰌠,,+K,+,+KlKKm+K+kkkKKJKJ+JJkJLKKKK+KJ**+Ȥl鬫JJIN֐OϨϨϬϨ0QQQPϨϨϨϨϨϬЬЬϨϨϬϬ/oޱ55PͰɰɐoOOooѐސPqp6Vp.Nֱͳ͓ɰ/O֐pֲ/6rqPrR򰱨ҬpPoOOpѨѨҬҬoOOO.OONPN.....-M.NϰΨ/NMnPpάmmMML,LL,,,L,+,+,+,,MOkK+,+MkkKKKJK+KkJ+* K*jKkkK+KJ*++J訋ɨjj Ό ϨϬ0//PQQP0ϬϬϬϨϨЬϨϬϬ/oN֏ްޱΏŐŐpONNɎpOq/ґ5VOp5wRɑSOO5.o֑ P/p/3ɓ2ҬRѨpoppOoOSҬѬP//O//...NON....-.3.Ϩ/..O.p-MMlL,L,,,LL,+,,,+ LŮK,K+++ͤlkkKKKkKK+K+kJ+KKjkKKk kkKJj+K**kɤ騫ʼnK,ʨjjj00P0/00PP0ϬЬϨϨϨϨϬϨϬϬ/OҏްڰoɯooonҏO/֒qpұ5.p5wwo5WpqOqP0ɒqqrڏS3QpoppPoOOpѬ򰱤pooOOON.....0M....--..N.OmɶN/...-nͨmM-MLM,,LKL,KL,,+, ,kKl+KK+,+KKJK+KKKKJ+K+KJK+kljKkkk̤kjJ+KJ+++* ɍkjjjj00ppp0000/0ϬϬϨϬϬϬ/PppoopoɏnoqҏqPpPڲP4o ҐWxww56ސPڑP֬ڲɓ0ɭrQpШ1q31񬐠ppPOO.PO/N./.O.M...M...-.-άɵΨnN-.M.M-mMLLL,L,L,KL++,K,,, ,LK+K+K+++KkkkJJKJJK+*kJ+K+KKKkkάlkkk+J*+J*J* ) ,jkkjji0PoŐp////0/0/ϬϨϬϨϬ0PpoON.O/ͮɯ0֑ŐҲP޳/MұN֐55vx55V//qqPqQ0/ɮQүpϨ4Ҳ23ҬpœpOONNѨoOo񬯠.N/N....Om-.....NN S֑nM..N---mmmlLM,,+L,,,++++,,,kLK+L++++K++++K*K++KJKJJK+KJKk+KkͨͨkKkJK*JJ*KK*)J .jjjjjjOpŰɐO/00000ϬϬϬϨϨ0OpŐůO4UoN.Oo֑ޑpڑڲqO/֑UOqs1Ppo-oڐސްް55UVwwV5U5/P/000үnoŎ,k2R0ɱTs֔oStsoOOoѨoooOMNO...NN...O-.... SSP0NM-MM,--ml.͠L,,,L,,,,+L,+,,LlK+,+,K*K++K+*K+K*JJK*++J+JOjk/KKJkJkJ*+**+**jhɯjjjipŐŐŐp/0//0/ϬϨϨ/0PpŰɐ͐55opo.p޲/֍/5/rQ/Pqo-Noֱoڰ544UUV5P֓1OnmoP0p/rɯ1ѬONOonoNoNoNNN.N..o..N ..ϨpϬnMM---- ,l-LL,,-,,,,,,+,, LLk+L+,*j+*+K+K++K++K+J*JJJJJJJ쬫̨jJkKJJJJ*JJ***IIq +jjjpŐoO/////ΨΨά/PpŰűɰqopޱo֐5VV65ѭ͑uPQ0֑ޑڑސON-ұ-ҏ4vV34VޑQگŏ/ΒޑQrRS/0/SsRR1ʰSRШoonNnOOno../.NNN./.l -./QqϨnШM------ m ,,-,,,,,,,L+,,,+KllK+K+jK*K*++KJ+JK*JKJJ*+JKKK+.-KKkJKJJJ)JJJJJ+*)i訋ʤjJooOO.///////άά/OpppŐŐɱɰPO/.OڰސڐV665ҍŭ.4OP//P.OOo N ΰvW5.5޳nɔ02Ғrts3ґppU/򰓽nooNn/Ьn...--. .-...0ϨomM- ,-,- -M,-,,,,L,,,LLK,+,+LKkL,LKkKKK+++KK+JJ+KKJKJJK**J*kj jjkJ+JJJJ*JJJJkjJ***jIi*jooOOOO////////OppŐŐŐŐɰůOֱV65ްޱ.͌ToPPON.NO pɰxvޱ//Ү7Q0Q01stڵStO0O/ponnnN..N-.  ..Ͻ -/.O.nNϬq0M.- - -- - mιL-, ,,,,, ,KL,++,lKkK,KK*jK+K+K*K+K+K+*JKJJJJ+K+*J+K jjJKJ*JJKJJJJjiJ*JJJjJ+jjoooooOOO.N/NOO//.//PpőoppŏɯŎŎNֱV6.ͭɬɰvqp//Pp-pO αސŪɱUwwOOO oqQ70Ɍq֓srPrűPQon񬏠nnoNnN..- .-..On/M-- - -,,LNl,, ,,,,,,,+L+,,+ +L+KL*KJ++JK+KJ+J+KK+KJKJJKJ*+K+JkKKJkjJJJJJJJJiJJJI***JiJɤɨɠjooonnnonoooooooŏnnOOoopppo0ɯɎ/ֱ55p. .NU4ޑOOppڑސ޲pɱ޲p֑֫OְvvUpPp/P֒W6rrΓͱp0Qpɓ񬰤poШnШonnoNNn..  - .QM../oqnnnΤLM,- ,Lll,-LM, -,,,,,,,+,,+,+,,+,+JjK*++KKKKJJJKJ*JJK+KJKJK+++*J+J*KKJjJJJJJJJjJJJJJJJKiJɤɤŏŏŏŏnnŮɮŮɯɏopŏon0PrɮŮŎ/ұ .Nֱ . vppO.Oֱޱpɑސ⊽oO4qp pɲڳrr֓t2͒11QSs0P/PPooOoononnoM--..NqnMMOO33ήΨNMM---,MML,,,,kL , ,,,,,,,+++,, ,+jKKK+KJJ+K+kK+KJ+KKJKJK*JJKJ+JjJ*JJJJJJJjjJJIJJ)K*JIiůɏŏŎ0PPPPqڑp/͎ŏ21qQڑQͮŮn.ְ5-ΐڲ͐VUޑn/-OҐ޲/ҫ.oqֲhn͐ڐN/ҒPҳWޒڳrֱrQ͑rsttѬr3o쨮ЬoѬnnnnnnN.nΤ,pq֭Mno4tگYQnmM-M-mmL,,, ,L,, , ,,,,, +,K,,k jK+++K+kJKK+JJJ+KJ*JK+KJKJJJJJJPMKJJJJJJJJjkJJJJIJJJJ)jɯɮ/0PQQڐqޑޑ޲0͓Q֑PqڲOɯmnmұq. ΐޱ.wޱ-/ֱrr ʑ0iޱ..p/qrɒųɓnqqqŐPtڐ4ͤMnШnonmnon/.-mmmŕ03ΔϬΨ1mmMmmmL,,, ,+L, , , , ++,LK++KKK++*+K+JJJ+KKJK*+KJJK*KK*+JKJ*JJii*KJJJ*J)JJJIJJJIJ*JJjJj/Opޑޑޱo-Ґ/ͮmMmpOoֱNpw5 NpڪOғO΋/rHWoPPO/QPpqڐP0TTsSr3֯0pr֐noononN֫pmnnnSpΨmϬΨQnlLMmlLLL,-,,ͤ, LmlLkk,,kK+,++++*K+KJ*JJJ+J++JJKJ*KJ+***KJ*J+*KJJJJJ)JJJJJJJ*JJ*JJjiȤ.OOޑ޲޲55O p6oo/ҭn.MM.ҲOOҰ/ -p։ڴPrO/qWw/qqp֓OҳڑqP/ϰ3Ұ0qϬm3 ŭoonooNϨop0.nmmnP/ϬmmMnmmMmΨmMMMllLLLL,LLlͬoͬLLLKkkͬkKK+++++KJ*JJ+J+J+++KJ+Kk**JJ+**+**J*JJ**J*JIIJJI*JJJJiiipސޱ66p..ґW45O/͍N--p. .ҐNp޲O,PqQҫqqPqҋwVr//ғޑֳpڒڑ-qάά QPoΏΏ4֭nononNop0.mmΨlMMmmLMΤLLLmmMllLKLLKLLSڏmllllŋk̤kkkKK+J*+KK*JJK***J*JJKJJjJJ****J**JJ*IJ***JI*****JJii56nN֐޲6xWVO.ή.N-, NҐڱޑN֬k0oO Ūp.pғɒrqiWX/Q֑ڳ.rOֲo֑ڑʓͬάͨ.OϬ24ҍnoonmɐޑޏmΨmMMmmmLMΤlLlMmmLlLLLL,LLO-̬lLlMLlKlkkkKKJKKJJ*JKK+J**KKJjJJJiǬIJ*JJJJJJJJIJJJ*J*J+JJJiij665ސޱ޲VO.ͮNN--M-Ґڑڑޑp-+Ů P. qڳʪ7 //QPy7q0Pq ʲOβp֑ڑޱ.oP֎̨ͬͨ άάű3qnnnnnШnnΨŎϬΨmmlmאַlLmmLmLϨLlmlmlllllLL,kko.ﰌά0kkLKKkklJkkJK*JJJJ*JjJjij*JJIIjIJJJ)IJJJIJJJJ*KJJijjIiijiiiU6ޱWvPɎnN.N.M pN-ŮŌɭ/O OPҪX ҒQq֫XXqQW7޳.ɐސڱoN֐ +ͨ -N.ͰPpЬnnnnnmnmnΨΨΨϬmmmlmLLmϨlmlmlLlLLL,lL̨̨oo/OqllKKKKk+KKkjJJ*J+JʤijjjK*JJJJ +/ +JJJJJ*JJKJKJJKIiȤiiiiiiiޱwW4/ɮŎnNnNNoŬ ΐސޏ. P֌m/N5px,O.PqxP07X/Nְސo--o-ɫKͨ -NnpRrΨnnnmommmmMΨΨmnommmmlLmllllmlllLlLLLL̨ŕ//llKLKKKK+Kjkj*JJJJJijjjiJ**JIiJJJJ*JIJJJJjjȤȤȤijiijiiʤ6O ɯoN.o.O- . qɎLlk/Ґ XxM./Β/8qŒWP.ΰMN--n ͬͬͨMnmN31TֳNmmmnnmmmnNmmmΨlůM̨mllmlmlmmllLLllMLlpQml/KKK+KK*+KJjJJJKJjjjjɤiJJJJ*JJJ*JJJJijȤ砨iiiijʨޏoޏpސްްސސoڏڐސ.NOO/ͯON.NN...-.֑Lmk p45 O Pq֑q/ʓ77OɏnN- . - ΫK쬭 mͮMrްP1ҭmmnnMmmmmmmmnMmΨ-N.ͬlllmlmllllLlllllmO/OoOmllkLlklKKLKKK*+J+KkjjJJ)jiJjj)ȠɤI*KJ+*JJJJIjiȤȤȠȤǤȤȤjiiijoNN.....OppoNMM--ұpoP/o.MnN.OooOonkLɫ/O֐4UPN֒pִ.-W6OpڰNo . .jJ +-P,qQl0mnmmMMMMmNmnnmmMmm̬ͤͨͬO. mllmlllllllɲﰮqLmKlKKkKkKKKJKKJKKKJ*J*jiji(ȤȤɨIJJJJJJJiHȤȤȤiiiʤji-- --֏pO/ͭm/pp/NNoڐo αO֋l./-U5P Β8q .p֒pNWonֱ,NN-- ūjjJJ,qɍqڲޑڬ ͬnnMML˨̨mmNM ̬ͨάͬllmllllmmlά0kLlLLKKKKKlKKKKJKJ++JKkJIJJJji鬧ȤǠǤȤiJJJJJJjiiȤǤǤǤȤȤihiiiJhhiɮɯɯ -.-.MN֐O /p֒p OoON ΐɬ  pvvV.qXOO-Ҳp-Ґ.ūoڏ-ҏo- N ɪIJ*J0ō/5qɎެL++쨭̨˨ + + +* + JMlmͤͨ쬭̨ͬlllLlllPϬlllLl+LK+KLKKK+KK*KKKKKkkkJJIJjjJjjȤȤ褧Ǥ計JJJJIjiiiȤǤȤȤȤȤiiIiiiijꨯɯɯɯ .ONONoNooONN ..Opp- -M N-NNp͉VV5OҪO66..p.ҐNpOOְNoo. O., ŊjIIkJҌqڬ ,ΫKʨ +IHII)))))*)+̨̨̨ͨͨͨͬlllͨPͤΨllllKK+,KKKKKKJK*KJ+JKKKJkjJJJJiJjjJjjɤhiǤ計IJJIii ƠȤȤɤȠiiiIIiʨ --MnNNNNNNNNNopOoqޑoO. - Non֐p/ҫŏ5p OֱXp/-ڲO αO.ґ-Oū Oo -ɉjJJJɋ JŊjJJjJII)II))IjŊjiJJ+ + ̨ͨlklllllklKK+KKKJKKKK+KKK+KJJJJkkkjJJIJJJJJIiJIiiȤȨȤiiIiiiiǤȤȤhIIjij-,Mnoްްްޑޏooopoo֑޲oNnoon-.--ұPɊp4 p7O oO Ώ ŰNPkJɐڐNo ʫjJJjŬŊ ūjjkjiII)))Iij)**KK ̤ͬkkllkllkkkllͨmlllkKK+KKKKJKKKJK+KKK*KJkjkkkjjJJIJJIJJJjɤhȠhjijiiɤǠȤȠhIIIȤooopooڑޱpڐڐޱo - ΐnڰQj΋.ұ޲pP/ PON ΑWWŬppڍKko֐.ҫjJJJkŬj ҪK +*jjjjJI)ijI )I + I*ͨͨͨkklͨkllͨάllLlkkKKK+KKkKjKKKKKKJJKKKjkkjjKjJJJJJJJJIjɠiȠȤIJjiiihȠǠȤI ސonNNMMNnڏޱ4465V5ū ooްL,ґQ1.ɪ poֳ-WV50ΌL+NڱpjjIKū Ϋj+ ̨̨ +*JJJIjjI(iI養jjjɤIJͨͨͨlά̨ͬͬͬlKkΨllllKkKKKJ+KJkJKK*JKJKKjkKKkjkjkJJjiJIiJiJjjjHȤȤȤȤȠIIiiiiȠǤǤǤg許nN--Nn֏ޏޱ6WVwW4N ΏnnސnM-,pͪŊʼn /ґ.αW4ڒŭl ސoūjjjJJ+*.ҫK , + ) IjjIʤkkkkj +̨̨̤ͬ ,̬̤ͬͨlllllllllLKlKlKJK*KKKJKJKKKJKKJKKjjJkjjJjJjJJIJJJIIIIJjIJiIijhiIiIIIiiiȤǤȤǠƠްްސޏސްޱ55UVޏڏگްސN- onMnMMNNOpiGGhGū/pڲN OVqڲ, /ڐŪ** + +jɋok jJ)))(Ijijjꬋkjkjjj +쬬-͌ ̨̨kkkkllllllllkKLKJKkKKKKKKkKKJJJKJkjkkkKkjkkkjjJJiJJIJJ*IIjjiIIIJIJ*))iIiIiihȤǠސް޲45ޏگڰްޏ-- ,NޏސސM, Nɪ N֐OOx޲5nN +-NֱŪk +Kkok* lk - ΫJI)))))) +ꨫkkkȨ̨ -, ̨̬˨˨kkkkkllllklKLkkkKKKKkJkKkJjJJkjkkkjkkkjjjjkIjIJJJJIJIJiIiIII))))))IIIIIiihȠȠ礏ސޱްonn֎nگްޏnNM-MoްސMnn֐oNOONҲڳ7N.Α7q5ڱP0, N֐-Ҫj* */kj j ΐnְ L + + ) + +ˤʤ ( 쬬-Mk+ ++ˤkKKjKklllkLkKKkKKkkkkkkjkjkkkkjkjkjjkjjIJJjJJJJJjiiiII)))II)IIHIIHIihiiȤǠǤ礱ޑސڏnmMMMmmnMMNML,,MNoڏoڎonmN-Mnְڑޱޱޱ6r.ґX.NޱpN-ΐ4 LNNo֪j* **kkJJ Jjmo֏OKˤ) +) +   (  ̨LL,Ґo֊JJ + +I* +̤kKJKJkkkllkKKKkkkkkjkjjjjjjKJkjjjjjJIIJJJIIJJIiIII)J)IIIIihiIIHIiiihȤ..No֑ޱސnNL,MM֎ڎnnmmnn֏ڏڰްޱ5V֑ޏ ʑڐڱޱޱްڲސN֐ڰ40έ.n֐ ƪjJ)**kIO֋JJ** + +j,--l˨ˤ˨ )))()(()((((() ++Kŏn֪I*) III +˨ˤkjJKJJKkkkKKkkkkjjjjJjjkjkkjjjjjKjjjjjJJiIJIIIJJJIIJ)I)II)JIiiihIiIiHhiiǠ礲qOoN --NҐڱ6VސVpp֑pڏM ,Mn֯NjjIJ*kI/J*J** +JImְ j +*)()))()( (((((((() *+-ҏڲ ΊIJIIiJˤˤkjJjJJJJJJkkkkJKKJjkKjkJjKKjJJkjJJkJjJjjJJjJIJ*IJIIJJIIj)IIIIihhiiiiihhhǠOֲ6oMN, ,M֏ڑڲ5444555ސoo., oֱސOOoo֐ڱސ-nnڎnMMnooڏڐڐްޏڰސon֏nN֏ڰްޏސްޏ ŪijJj)//***J))*J)In**jjiII))))()((((((())H IJIIjj *ˤˤˤˤkkjjjjJjjjjjJJJjjjjjJjKJjJJKKJJJJJJJJ*JJJJJjJJjIIIiiiȤhhhȠ椪 .Α7WWސnN-,,-MMNnnnoڏڎon, + + + MNn֏ڰސnoN-Mnnn֐onooopڐomN,,MLM-moֱްގnnnnnNNŪjjPjIJII*)JIIInJiIII)((((((((((((((()((( IJIij)j*ʤjjjjjjjiijjjiijjjijjjjiijjijiJiJJJJ)*JIJJJIjjiiȠǤǤȨȤȤǤȤȤȤǤȤǤȠȤȤǤǤǠǤȤȤǤhǠ6o, -oڱVWxW5ްޑްޏڐޏޏޏoonONMML--+ -L,MNN-,-NnonooڏoڏonmNN֏ڐڏooonMNnN֐o..ҫJIjjIiiIjiJmn֊iiIII))IH))I(((((()(((((())( ūiJIIjIII +ˠkjjjjjjjjjijijiiiiiiiiiiiiiiiijiiIIjIIJ)IjiIiiȤȤǤȤȨȤȤȤǤȤȤȠȤǠǠǤǤǤȤǠgWXxyVo- N֐ޱްޱސްoonooڐސޏonOnM--,, ,MmNNNnnoگޱoM,--MMҎN- ɋjjjjjjiijjIiIIIII)HI()))()((()())()HiiJII)iIJj+ +ʤˤkjjjjkjj˨jjijiiȤȤȤȤȤȨǠȤǠǠǤǤǠǤȤǤȤǠǤǤ56WW5onoooڏڏnmnNNL-,,,, ,-,-MNMn֏oڏon֐oooڏOM.---,-MMNnoڏڏސސڏoڎonNM-, Mҏm ŪŪjjjjjjjjiII)IIII)I))I)))))))) ))IijiIIHJI))IJj* +ꨪkkjkjjjj+K +ɤʨʬɬʰ + ++++LLKkllkKK+ +ɨȤȤȤȤȤȤȤȤȠȤǠȤȤȤȤȤȤǠȤǤǠǤǤȤǤ,,,MMmn֏ڏڰްޏ֏nMMMM,,, ,NoooonNMM, ,----MnNnڏگސސސoN- - -MN-, ,ŪjjjjjjjjjIJII))**** + + + + +jJII)JjJI))IIJjIJ* ꨊjjjjjʨ+K +ɨʬ ꬩʬʬʰ ++LLlō͍ͬͭͮѮѭѮɋK* ȤȤȤȤȤȤǠȤȤȤȤȤȤȨǤȨǤȤǤȤǤǤǤ -, , +,MNn֏ޱ޲ޱޱސڐސpڐސސސڏڐސސސސޏooڏoڎڏononNNnooڏڏڐޱo. -M-- --M--M,, ,-N- ŪkjjkjJKJ*JK+* +ʨʨʤʤʤʤɤʤɤʨʨɨʨ ****JjIjJ))))*)J*) +ʨjj++ +ɬ + + + + ++LllŌɌɌɭͭͭ.--.-, ɪj))ɨȤȤȤȤǤȨȤȤȨȤǤȨȨȤȤȤǠmMNMLMNnNnMNM-,,-,,----,-MMM--M-,,--M-,,---N,MMNNNMNn֏o.ɫūūūŬŬɬŬɬ-MMNnn- .MMMNMMMNnڎ- -NM-ūjK + KkK +KJ, + ʨʤʤʤʤʨ + + +*)JJ** + + *****ʨˬˬʨ˨ˬʬ ++**JKKkkkkkɬɬ ..N..N-NNMNnNMnNMM- Ɋi() Ȩ ȤȤnNMmMMnNMMMnnMMMMNNN-MNNm֏ڏޏonN,,, + ,,, ,- ɫū Oo.-,MMڏڏڐޏڏN -NpޏޏoڎnN- ɫūK ˨˨Lk+ ++ʤʤʤʤʨɤʨ + +* +* +***)*********+++Kkkkjkŋūū -.-..NNNNNNnnnnnnnڏnڎڎnڏnnڎmmn ΫiI)()()( )  ( )(() mnڎnnnNMMmnnoڏoڰޱonnnnnnnnM,,- , ,,NMMNNN--,- - - -N֏ްޏڏoڎnN -oڰޏnnN- ɬjKjūj+˨ ͬ+ +kK ʤ˨ʤʨʨʨ + + + + + + + + +JkJJjjjjkjkjkkŋŬū -,-MMNmMmnnnnnnnnގnڏގڎގڏnڏޏޏގގގnnm-ŊIHH(((())(((((((()()(I)())I)()( )謏ގonMMN-MnnnڰްoN-,-.MN.NONNNNNM.--- --.--M-MnooڏޏooonM-MNM- kk --noM ,--- ͫŋJKJk +*+Kkk ʨ +++**** ****jkjŋūūūū ,,L-MMmnmnڎnnڎڎnڎnގޏnގޏޏޏގޏޏގޏޏnڎގnmM- ɉiHHH((((((((((((I()(I))II(II())HI)))(())nn-NM-NM,NoڐޱޱN, ---.MNNNNNNM-- - - -- , - ,-- ,-- άŋl+K+k NoNM--.. ɫkJjJK+K++LKlLl jlkk++ + +kKkkkkkjjjkjū ,,,,MMMNmnnڎގގڏގގڏގގގڏޏޏޏޏގޏޏގڏޏnڎnnnnM, ɉIHHH(HHG(HH(H(HH(HHIHHIiIIIIIII)IHIII)HII)onM---N.Nnoڰްo- .MNN------- - ɋK+++*Jj-nN----, ūŬjkkkkllŬ ͬŋKK++K,,LLŬūūŪūūūū + +, ,,,,,,LMMmnmڎڎޏގގޏގޏޏޏޏގޏޏoޯnڍomnMmML+, ũhHHG(HG(HHGHGHHHHhHIiHiIiiiiIIIIIIII)IIIIiIN- - -.NNnooڏޱnM -------, ɫkkKKKkkū LN-- - ɫūŋ ŬŬŬŌllll- , , , ,,, ,+,, +,L,,MLmMMnڎnڏގڏnnnڍmLMLLL , ŪihiHGH'G(G'G'H(GHH'HHHhHhiiiiiiiiijIIJiIIIIiiijio--  ...NooڐڏްްޏoN -, , , ɫkŋ -,-- ɬŬŬŬ - ɬɬ -NNNNnMM, + , ,,,,L,,,,M,,LM, ŪũihhGHHHH'HHG'('''''G'''G'H'HHHHiiiiiiiiiIiiIIiIiiU5. ..NOooN. , , , , --.- - NonNoڎڎގmMn+, + + + +ũihhhGgHGGHH'G('(G(''&&'G''''''(G(G(H(HHGhihŪŪɩiiiIiiiii5O - , , , - ,- ū ---.NNNN....-M-Nnoڏސްޯްޯގmm-, + hghHGGGHHGG&'G(''''&'G''''&''G''''''''''''''''GHHHhhiũŪɪŊjijiiij/ - -,, --, ɫūūūū ......N.NNnNnNnMNnoڏްްގM,,ɨũhghhhGhghHGhGGGGHGHGGG'''''GG'''&(&'&'G''&&'&'''''&'''''''''G'HGGHHh ,,,, ũŊi- - - - ,,, - ---, --,--, ɬūūūŋŬ ./OoOO.NNONnonooonoڏްm, ŪũghhhhHgHgGGGGGG'HGGG'hGh'GHHG''GG''G&'&&&'&'&&''''''''&'&''''''''''GGHGHHHHhh ,LMmmڎMmML, ɪŪ5oN- - , .-M--M , -,,-M-MN-- , ,--- ɫŬūūŋ .oڏޏpoޏooonoގonޏڏޏޯސްn,, ũŪhiHhgHHGGGHGGGgGGG'hGGGGGHGGGgHGGGGFG''''&''&''&&'''G&'&'''''''''''G(G'HGHHHgh ,MڎMڎޯޯގގnm- ɪŪVv4o. -,-MnnMMN-M--- , MMMMMNN--- , , ɬūūj.Ooڐސްސޏސޏޏސޏޏޏޏސ44nL,+ũGhhhGGHgHHHGGHGGG'HGGGHhHGGHhGGGHhGG'GG'G''G&&&''G'''&&'&&'''''G'HGGGHHhGhh ,MmގޏގnM ŪʼnwUU4N .--M֏nnڎmNMM- ,LMMMnMnMMM,- ɫkkjkkNoސޱްްޏްް33444ޏnL, ũňhhhghHGhGHhGGgGHHgHghhhGhhHhhhgghGhG'GGHG&'&&'&'&''&''&''''&'''F'G'(GHHHHhhh ,Lmڎޯޏm, ɪŪʼnʼnřwU4NN.- -MnڏގnڎnnnN-- -MMmMnMMM-,, ɫŋkkkjk-N֏ްޯޏގޯ444ގM, + ũŨhhhhhhhgghhhhhhhghghhgghggGGHHGGF&FF'&'&G&G&&(&&'''''''G'G'HhhhHũ Mmڎnl ŪŪŊxvUo.NN-... -NmڎގnڎnnnmM- --MmMmMMMM, ūjkkjkŬ Nڏް3ޏގڏ343MlM,+ ++ + ɩũhhhhhhhghghgGGGGGG'G'G&'&''&'&'''''&GGHGGhhhɩ ,LMnޯ2m, ɪwT4nonooOOoN..  -MnڎޏގގޏmnnM- ,,LMMMMM,,, ɫkjkjjkū--nְ444ޏnڏޏް3ގގmMm+L,,, +ũňʼnhghhgHGgGh&GGHH&FGGGh&HGGGGGGHHghh ,Mmmڏޯ3334ގm,ɪŪijwvU4ްްްoon. -MmڎޏnڎnڎnnM- --MMML- , ɫūkkJJjjkū -mڏڰ444ޏގnnڏްގmmmLML+,++ ɩŨũũʼnũŪhhhghGhghggGGGhGhhhGhhHhhŪ LLmmڎޯޯ33333333ގM,ɪʼnjiiWuU44N-   - .MNnoڏnޏޏnnMM- -,- -, ūjkJkJJKjj ,Mnڏ43nnnnڏޯޏގڎގގmmm,l,+K ++ + + + + ũhgghŪ ,Lmnڎޏޯ3T34TS4S33m- ɊijIIwvVvUUT44N  ... --MNNonڏޏnnnmNL - ɫŋkjKJJJ*KJjk ,MnoڰޯގnnMnoޯޯގގnڎmMlMl++++,,+ ++++ +++, + + + + + + + M +ũũŨũũ ,Lmnڎޯޯ334TT4T333ލM ɪjjIII5443434444333U454oN-- -.-N.NM.M.MNNNnnnnnnnMMM ͫɬ ūūjkJJ*****Jj ,-nڎڏڰޯްޯnnmMnmmڏޯޯޯޯޏގڮގmmm֎m+L,LL+L,LLLLmLnm,LLM,+,+,++,LL,LLmnM-LL-, ũũ + ,+mmڍگޯް334T4UTT33ގM ͫŊiJ)I)333444TTTUU43oNOnnNNNnoooononooڏoononMM-- ɫūɫɫūūŊkjJjJJ****)JK -MNoڏޏޯޏޏnnMM-MMMmڎoޯޯްޯްސޯڎގnގmmMlmKLKLLLLmnmmmڏޏގގގnmmLLMLmMMMnnmڎnގގގގmmL, +ũ + + + + ++ + + +Lmnގڏޯ233T44T443mL,ɪjiJ)))(354UT54UUU43ސސޏNnM,- ɬɫūūūŊkjkjJj***))**JJ -MMnڎoڎڐޏޏޏޏoގonnM--- ,,Mmm֎ڏޏޏޯޏޏގڏގގnmmmmLMLMLLlmmڎޏnڮޏޏގnڏގnގmmmmmڎnnnގގގޯގޯޯoޏmmm++ + + + +** +*+,+,,+,,++ +,MLmڎڏ44T433ޏM,ɫjJI)() 3T54UUU5U4on-- ɫūɋjkkkjjkjjjJJJI***)*JJ ,,--MNMNMNMMNM--- ,,Mmmnnnڎnnڎmnmm֎LmLmKmmKm֎mnnڎޏގގޏޏޏnڎmnnڍmnmmڍލގڎގޮޯnmM,+ + + + + ++ **KKKLKlLLM+,L,L,,Lmmnڮް33333L,ͪŊKII)  3444544U444544T5U5UUUU54nM- ŬūūŋjjjjjjjjJJJ)))*IJj , - - ,,MMMMNMMmmmMLmM+LLlLMMmnmnl֎ގڎڎnnڏގmmmmnmڎnmڍnڎގގގޏmLL + + + +,++*LKKmmLmLmLLmL,L,Lmmmڏޯ2m, ɪJ) 33444444334UUUUVvVvwwvvvvUU53nM- ɬɫŋŋjjjjjjkjjjJIJI)IIjjjūɫ --,LLMM,,L-,,,LLLLLllmLmnmmnmmڍnmmmmmmnmmMmnmnmڎޮޯޮގmL+ + +, + ++,+LLmlLmڍލmmmMmMLLlLmmnގޯޯޮm, ɋjJ) Ȩ34434TUVvWvwxxwwwvvV53ލM- ɫŋŋjjjjjijjJJJJJIjjjūūŪūŪŪū ,,,K,LMLmLLmLlLLlMmMLmMmMMMMMMMMmmڍnگޯޯގލmL,+ +*,+KLLmlLmmڍmmmnڍmlmLmLmlmڍڎگްޯގM-,ŋj** Ȩ4T45UUvvvwywvuU3ގMM- ūŋjjjjjiiiiiIJIijijjijiiiiiijjjiŪŪ ,,,,,-,,,+L,,LLLLMLMM,LLMLL,L,MmmڍڏގޏޮޏޮގލmL, +,L,LLMmmmnnmގmmmmlmLMlMmnގޏޯްގގގnML,ɫŋjJ ȨȨ3T4TuVuvvvwwwvvU4nL- ɫŪjjjiijjjjiijiijji +,+,+,,+ + +,,,,-,,,, , ,,,,LMnڎڏޮޮޯޯޯގގMLL, +++++LLLlMmmmmmmmmmmMMMMlMmMnڎގޯްގގލnMM,ɫj*) ȨȨɨȨȨȤȤ3455UUVvwwvwwwvuT3nnnM ŪŪŪŪi ,+ + + ,,Mnmڎڏގޯޯޏގގml,+, + LL+LLMmLMmmmmlmmmmMLLMLmmnڎގޯޯޯޯޯޏnnM-, ɋJ** ȨȨȤȨȤȨȨȨ444UVVvwvwwvvU53ޏnNM, ŪŪŪũ , , ,+ , ,, , ,,MmڎޮޏޯޯޏގmlL++ + ++,+,LLLmMmmmmmLMMLLLL,L,MLmmnڎޏގޮޏގގnmM ɫkJ* ȤȤȤȤȨȨȨȤ33444U5UUvvvwwwxwwxwwvvVU542ޏnmM, ɪŪŪũũũũũ ,, ,,,,+ + + , MMnڎޯޯގmmL+ + + + L+,,LLLLL,,,,,, + ,,,,M-MMNMM- ͬŋkjJ* + ȨȤȨȤǤȤȤǤȨȤȤȤȨUUUUUVUvvvVvvwwwvvUUU433ގޏnmML ŪŪŪŪũŪũũŪ ,,, MMLL,,, , ,+ ,, ɫŪŪŪŪŪ ,MM֎ڏޯގލmL + +,++ ɋjJ) ȨȤȤȤȤȤȤȨȤȨȨȤȨȨȨVvvwwwwWwxwxwwvwVwUUU4U534ގnmML,+ ũŪũɩũ + ,M,,,M,,+ ,, ++ Ū ,-nnڎޏޯގML+ + ɊjI) ɨɨȤȤȨȤȤȤȨȨȨȨȨȨȨǤȨwwwwwwwwwwvwvwwvvvUvUU4T43ޯގގޏmmM,, Ūũ , ,,MMMLM,,L,, ,+, ūūū,-nڎڏޮnm, ūūūūŪūūūūūūūūūūūŪŪŪɪjJ) ɨȤȤȤȨȤȨȨǤȨȤȨȨȬȨǨȬ謘wwwvwvvvvvVvUUuU4UTޯޯގގnڎmmLL,+ + + +ũ ,-L-MMM,M , , ŪjijjIjijjjū -nڎگގMK+ŪũŪŪjɫɊI)ȤȨȨȨȨǨȨȨȬȰ )wvwUvUUvUUUUUUU4T33ޯޮڎގnڎmmmMLL+ ŪŪ ++LMLM,,,++ ŪŪiiJiIJIiJJjJJjjjk -M֎ڎޯޏގmL ũũũhhjjjjjjjjjjjkjkjjjjjjjjjjɫj)ȨȤǤȨǨǨȨȰ  )))IJIwwuvUVVUU6uUU5UTT34ްޯޏގڎގގލڎڍmmMmMmLLL, +Ū ++ +,,MMM, , ŪjjiIJIJJIJ)JJ*)JJKkjj -MnڏޯގmM ũũňhhhiiiiiijjIjjjjjJjjjjJJJJJJIJJI i) ȨȨȨǨ) *IIiIiwvvuvUUVvUUvUuU4434ޏnmmmmlmmMLMLLML,, + +ũŪũũũ ,,,-,,, ŪjjJJJ)J*))* +* +) + +***JJj MnڏޯޯގmM+ ũŪʼnhihiIiiIiJIJJIIJJjIJJIIJI))))*IIɊI ȬȨȨȨ ))JJiiiiŊʼnũŊŹvvvvuvUUVvVUuUUT43nmLLML,L,+,,,,+, ŪũũɪɩũũŪŪŪŪŪŪ -, -, ɫūŋjjJJJ)*)) + + + + + + +**K -Mnڎگޏnm, ɪŪʼniihhihIIIIJIIIJJJII)*)I)))) ) *I ͊I ))IjjŪŊŪɪɪəwwwvvVuUuVUuvUUu5TT4nLLL,,,,+, ɪŪŪŪũŪũŪ ɪūjJJ*) +) + + + + +)Jj MnnڎޏޯޯޯޯޯޏގގڎL, ɪũʼnhiiiHII)JIIII)I)I*)) ) )i ͫj )IIIŊŊŊŪɩ͹wwwwVvVvuVVUvvVvvVUUU33ޏnLmL,+,+,+ ũɩŪɪũŪŊ ūŪjjKJJ* +* + + +*Jk,-MnnnڏގگގގޏލnM, +ɩiihIiIIHI))I))J)*))))  )I j ) I(ihŊŪ ҙwwwvvvvVUuVvvvVVuvuUU43nnMLLL,,,, ɪũɪũŪŪŊjūūjKJ*** + + + ɨʨʬ + + +j,-MMnnڎގގޏގnڍnmL + ũhiiiHIH(JII))I))**)) + I Ɋ* )(HIiŊŪū ҘxxwvvvvvvvUVvUvVvvvvvvVUT43nmMM,L,+, ɪɪɪŪŪŪŪŪjjjjjjjjɬɬŬŋKJK*+ +* + + +ɨʬʬʬ)*Kj MMmmnnڎގnnnmmM+ ɨihiH)III)))I)))))))))) )j Ŋ))(Iji ,, ,M,֘xwwwwwwwvVvVuUUvvuVVvvvvUU44ގmmMLL,LL,, ɩŪɪŪɪŪūɪŪjjkJjjkJjjŋŬŬŋJK+ + + + + + + +ʬɨʨ +*j -MMnnnmnmnmmLL ŪʼniiIIH))))) )I))*)*))) ɨȨȨ )I ͪI))Iiiūɫ ,,,,,,,LLLژxxwwwwwvVvVvUUUUvVVUuvvUUUUUTUnnMmLLM,,, ɪɪŪŪŪɪŪūŪɪŪŪŊjjkJjjJjJjjJjkkkŌkkkK* + + + + + ɨɨ +*Jkū ,-MMMnnnmmmmL,, ũũiiI))I))()) )))) * ȨȨȨ )ŊI))IiiŪ ,,,L,LLLLmMޘwwwwwvvwwwUvvvUUUUVv5vUUUUT45ގmNMM,L,L,+ ŪɪŪŪŪūūŪŊŪūūūūŪūūŊjjkjjJJJJJJJKJKJKkkkkkkkKK** + + + + + + +ɨ + + +*k ---NMnMmnMMLM,, ũʼnjiII)(())) ) )))) ) ɨȨȨȨȨȨȨ(II ()Iii ,L-LMMLmlnlmmژwvwwvwUwVUv4UUUUTUUUU5TT4ޏnMmMnLML,, ͪŪɫɪŊɫŋūūūūūūŋūūŊŊŊkkjJJJJJJJ*KJ**K*kJ+KkKkJkKJ*+* + + + + + + + + + +*KJj ---LMMLMM-,, ɪŪiji(H))) ( )))) ) ȬȨȨɨȨȨȨɨ)JJjɪ ,,,LlLlmmmMmmmlڸxwwwvwVvUUU4U54T54UU433nڏmmMMmLMM,, ɪŪūɪūɊūŋūŊŋjjjjjjkkJJKJ**J+**KKJKJKKKKKKK++ ++ + + + + + + + + + + *Jɬ ----,--,, ɫŪŊiII)) ) ) + ) ) ȨɨȤȤȨȨȨ ))IIjū ,,LMMmLmmmmލmmnޙwwvvUVUU44434443ގnڎmmmMMM,M-, ūɫɫɫūŊŋūūŊŋŊŊŊŋŋŋjjkkkjJkJKJJ**J****+JKKKKJKKKKK*** * + + + + + + + + + + +*Kj -, ɪŊiI)())) ))) ȨȨȨɨȨɨȨȨ  IIj ,LLMLmmmmmڍmގލmڙwwwvvuU5U444333ޏގގmmnMmLnM,L, ɫɪɫūŪūɫūŪŪɪŊūŊŊkjjkkjjjJjkJJ*+JJ**J*JJJKJKKKKKKKKkK+* + *+ ++* * + + + + + + + + +* +K ŊŊŪŊIH))   )))  ȨȬȨȨȤȨȤɨɨȨȨ )IJjŊ +,LMMmmmmmmmmmڎmژwwwvvVuUTUT443ޏގގmnnmmmMm,mM,+ ɪŪɫūūɫɫūɫūūŋūūŊŊŋjkkkkjkjJJKJ+J*JK***JKJ+JKkkkkKKKKKK+*+ +++++++++ ++ + + + + + + + + + + +JkɫɫūŪiiI(( ) )  ɨȤȨȨȨȨɨɨȤ )IIj ,LLMmlmmڍmmmmmmlڹwvvvvUU44433ޏގގލnmnnmnMnMMMM, ɪɫɫūɫūɫɫūɫūɫŪūŋŋŋjjjkjkJKJKKK*JKJ*JJ**KKKkKkkkkklkKkKKk*KK+*KK**K+* +*** + + + + + + + + + + + + +*JjŬɫɫŪŋjjII) ) ) ) )) ɬȨȨȨȨȨȤȤȨ ))Jjū ,,,LLMmmMmmmmMML֙wwvvwvVuUU5T443ޯގnnڎnnmLmnmnMmLL ɫɫūɫɫɫɫūŬŋūŋūŊjŊkŊkjjjjkkjJKKJ*KJ+**+KKJKkKkkkkkkkkkkKK+KK+KKKKJKK**+**+* + +* +* + + + + + + +JJŋūŌūŬɫūŬūūūūūūŊjijII( *) )*(  ȨȨȨȨȨȨȤȨȨ )JJ ,LLLLMLMML,L,+,ҙxxwvvwUUuUT5443ޏސގގގގގގoގnmnmnnnMM,+ ɫɫɫɫɪūɫŋūŪŋŋkjŋkkkkjKjKKJJJK*KJ*KKJkkkkkŋlkkkkkJkkkkKkKKKKJJ*JJJJ+*J**** + + + * + *JJjūŋŋŊŋŊjjiI))( ))) ) ) ) ɨȨȨȨɨȨȤ )*i ,+,,,L,, +ɘwvvVUUU5T43ްްޏޏގޮގގڏnnڎnnnnnnmL,+ ɫɫɬɬɫɋūŋŋŌkŊkkjkkKkJKJJKKJkJKkkkkŬūŌūŌkŋkkkkkkjjkKkjjJkKKK****) + ++ JJkjkjjjŋjjiiJI)))* )))))I )( )) ɨȨȨȤȨȨȨȨ ))JjjŪ ɩũŘwwvvVUT4T3ްޯްޯޏޏޏގގގގޏnގnڎmNM,,,, ͫɬ͋ūūɫŋŬɌŋŋŋŌkkjkKKJJJKKKKjkkkūŬūɫūŌŋūŌŬŬūŬŬūŌŋkkkjkkkkjKJJJ***)*J*jjjjjjjjjijjjjjiJII)))) )) ))))JH))) ))) ȬȨɨȨɨȨȤɨɨȨȨȨ *IjŪɪʼniwwvwvvvUU443ްޯޯޯްڏގޏޏގޏnnmmNM-M,-, ͫͬɬͬɬɫɬɫŬɬɬɋŋŋŋkkkkkjjkkkŋŬŬŬɬŬūŬŬŬŬɬɬɬɬūŋūūūūūūūŋŋūūūŋjkkjJJJ)*IIJjjJjIjIjJIjIII)HII*)) )*)*I**))I))))))) ɨȨȤɨɨȤȨ ))JjjŪūūɪūɫŪŊʼniiiiivvvvvuUUT4ޯޯޯޏޏގnnnmnnNM,,, -,------- ͫɬɬɬɫŌŋŋkkkkkŋūūūūɬūɬŬūūɫɫɫūūūūūjjjjjJIJI*IJJjJJIJiIIJIIJII*))*****JJJIIII)IIH)()()  ȨȨȨȨȨȨȨɨȨȨɨȨ )IijjjjjiiIIIIHIIIUUvuUVUT443ްޯޯޏޏޏޏގގގnnmnMM-,--MMNNMNN.M-,-- ͫŬɫŋŋŋūūūūūūɫɫūūŋŊjjiJJJjIIJiIIIIII)))))))*)*)**JJJJjJIJIIIIII)I)) ȨȨȨɨȨȤɨȨȨɨȨ  )I*I)JI)II)))(I)H)HI4TTUUUTU43ްްޯޏޏޯގޏޏޏnnmmMMMnNnnooގnNnNNM-M - - - -----,--- ɫ ŪŊŊŊijiiijiJIII(II))))I**)*JJJjjkjjjJIiJiIIJI)*)) ɨɨȨȨȨȨɨȨȨɬ  ) ( ) ( (()(33TUUuU44ޯޏގޏގnގnmmnnڎޏޏޯޯޏڏޏnnMM--,- --,-- -- -N-NNNnNNNMM- ɫ , , , ,-,,,-, ūŪŪŊijiiiIiIHII)I))I)I))JJjkkŊjiiIiiiII)) ɬȨȨɨɨȤɨȨȤɨȨȨ    ) )H(3TU54444ޯޯonڎގޏްޏޏnnmM--M------.N.NNnnnnގޏގޏnގNMM-, ,, ,-,-L-MMMMMMMML--, ŪŪŪŪʼniiiIIIII))I)I)JIJjŋŋŊjijjiiiJJ)* ɨɨȨȨɨȤȨȨɨȤȨȨɨȨɨȨ   ( (((()3455UTT3ްޯގޏޏޯްޏގonnNNMN-M--MNMNnnڎގޏޯޯގގnnMM,- , , --,,-M,MMMmnڍnmnnnMmMLL-,-, ŪŊiiiIhIII))IIJJjjūūūɫɫūŊjIJJIjjII)) ɬɨɬɨɨȤɨɨȨɬȨɨȨɬ       (344T4T4ްޏޏޏnnMMNNNnnNnnڏޏްްޏnnnMM-, , ,,L-LLmmڍڎގގގގޏގގڎڎnnmmMMMM,,, ŪŪŪŪŪʼniiiihIIIIIijūūūūɊJI*IIijjI**) + ɬɨȨȨȨɬ    (3444343ޯސޏޏގڎގMnnnnnnڎޏޯްޯޏގnnnnM, , ,-M,MLmڎގގގގگޯޯގގnڍmnLM-,,, ŪũũiihiIijūɬɫɬ ͫjJ*)*JJijjJJ) ) ɬɨɬȨɨɬȨȨɬ    34344ްޯޯޏޯޏonnnmnoޏގޯޯޏގnmmM,, LLLLlmmގގޮޏޮގގnڎmnmmMM,, ũũʼniIiIiūɬ ͫj** **JjjjiJ*) + ɬȨȨɬȨɬ )   3433ްްްސޯގގގޏގޏްގގގnmm,-+ ,L-LmLmڍގގޮޮގޏnގڍnnmmMLL + +ŪũhiiiŊJ)** *JJkjJ**) + +ɬɬȨȨȨ )    ްޯްޏޏގގޯޏޯޯޯގގގގnmML,, +,+,KLLlmڎލڎޏޯގޯޮގޏޏގގގmڍMmMLL+ ũũũũũũii ͫūjJ* + + *JjŋjJ***) + + ȨȨɬȨɬȨ ))  ޯޯޯޏްްޏޯޯގގmnMM+,++,,,,LMlLllڍލގnڮގގޯގްޮގގڍڎގmmLL,+ +ũũi,M ūūŋjjJ)** + **JkŋjJJ)*) + + + ɨɨȨȨ) )) ) ޯޯޯޯޯޯޮގnmmLLL,L,LLmmlllڍގnڏގڎގޯڎޯޯޯގގmڍmmmLL++ + +ũũũʼn MnLɫŪŪjJKK*J* + +*Kjūūjj*J*** + + + + ɬɬɬɬ )) )  ްޯސޯޮލmmMLlMMmmmmmލލmڎލگގގޯގڏޯޯގގގގڍmmML,, +ͪŪɩũũ,֎ޏM ɫŋjjJjjjKJK** +*JkɫūkKJ***)* + ɨȨȨɨ )) ) ) ( ްްްޯޯޯޯްޯޯޯގnmmmmލmmڍڎލlڎmڏڎގގڎޏގޮޯޯޯގގލގmڍmLL,++ ɪɪ nޯMŊJJjjkkkkJkK*+**KkūūūkkJ** )* + + + + ȨȨȨȬɨ ) ))  ގޏޯޯޯޯޏޯޯް23ޯޮލގލޮޮގގގލގڏޮޏޮޮގގޮޮލڮޯޯގގmnmL,++ + mڰn ΫɋkjJkkjkKJ+JKkŬɬūjKJJ*)* +*) + + ȨɬȨɨ )) +) ) 3mގޯޯޯޯޯޯ333333ްގޮލޮޮޯޮޯޮގގގގގގڎގޮޯގڮގޮލލmLL++ + +ɪɪ nn ΫŋkjjkŋūŋkkKKKKkɫŋJJJ* +* + + ɨɬɨȨ)) )  Mڍޯޮޯޯ3334333ްޯޯޮޏޮޮޭގލލڮޮޮގލގޮޮލލmmmLL++ + + m-ͫŋŋkūŋūŌŋŋkkkKKKkɫŋjjJJ) +*) + + + + ȨɬȨɨȬ *) )  mmڎޯގގޏޯ2333333333ޯޯޮޯޮޏޮޯގޯޮލگލލގލގmLLL, +ɩŪ,ҎMͫŋkŋŌūŬɬŋŬŋklLkKkɫɫŋkJJ**** + + + + ɨɬɬɨ )) mnگގޯޯްޮގޯޯޯ333TT3T432322ޯގގގޯޭގލގޮގޮޮޯޮޯޮލڎގmmM++++ɩũũ,֎ޯ ɫŋŋūŬɬɬɌŋkkkKkjlɫɋjjJJ* + + + + + +ɬɬȨɬ )  mگގޯޯ2433ST4T33332ޯڎޮޮޭޮڮޮޭޯޮޯްޯޮގގގmmmL+++ũũŪŪL֎- ͬɫŬŋŬɫŬɬɬɋŋkkKkkͫŋJJJ** + + + + + ɬɨɨȨ ) ) ގގޯގގޯ233343334342222ޮޯޮގޭގޮގގޮޮޮޭޮޮޯޮޮލގmڎގlLLL,+ +ũɪũ-֎ޯnͬɫŋŋŬŬɬɬɬɋkkkkKkkɋŋkjK*** + ) + + ɬɨȨȨȨ ) )  Ȭލގޯޮޯޯ233334STS4343232ޮގޮގޮޮގޯގޯޮޯޯޯގޏގmmlll+ + +ɩɩũũɩ ,mگ- ɌŬŌūɋūɬɬɋŌkkklKk ͫɋŊJJ** +* +* + + ɨɨɨɬ) )  ȬȬގލގޯޯޮޯޯ223333344S3332222222ްޮޯޯޯڮޮޮޮޮޮޮގގލލlllL*+ + +ɩũũũ nގM ɬɫūŋŬɬɬɬɋŋlklKKkkɌŋjj)J** + + + ɬȨɨȨɨ   Ȭ33nލގޯޯޮގޯ2333334SS3322222ޯޮޮގޮޮޮޮޮޮޮޮޮޭގގގލlmLll + + ũũũɩ L֎ޯn ɬɬɫŌŬɬɫɬɬɋklkkKK ͫɋŋKJJ* +* + + + ȨɨɨɨȨ ) ) Ȭ32Uގmގޯޯގގޮޮގޮ2333334333332223ޮޮޮޮލޮޯޮގޮލڍލmllL++* + +ɨŨŨɈ ,,ڎގn- ͬɌŬūɫŬɬɬŋŋkklKKKlɫɋŋJJ**** + + + ȨɨȨɨȨ   ȬȨ323343ލގޮގޏގގޯ3333333S333222ޮޮޯޮޮގގޮޮޮގލmڌmlKL+ +ɨɩũ ++ ,,mڏ- ɫɬūŬŬɫɬɬŋlklkkKkɫkjJ* + +* + + + ɬɨɨȨ 23343ލnގޯގގގޮ22333333S32ްޮޯޮޯޯޮޮޯގޮޮޮގގލލlmlLKK + ɨŨŨŨɩ ,LڎގM. ͬɌɋŋŬɬɫɬŋlkkkKKKkɬɫɋkJK* + * + + ɬɨɨȨɬ  Ȭ3T3nގޯގޮގޯގޮގޯ233333333ގޯޮޯޭގގޮތڎލmڍklKK* ++ͩũŨ + L-nn ͬɫŋŌŌŬŬɬūŋkkLKKKKkɫɋk+*** + + + + ɬɬȨɬ   343ގގޯގޮޏޯ2233333333333222ޯޮޯގޮޮގލލގލlmKLKK+ + +Ũũũɩ +, , ,Lmގޏ- ɬɋŋŌŬŌɌŋkkkKKKKkɫŋkj*** +* + + + ȨɨȨȨ  Ȩ333ލގޯގޮޮޯ333333332ޮޯޮޯޮގޮޮޮގގލލލmލlmLKK++ + Ũɩɩ ,,+-MnmN ɫŌŋkŋŋŌŋklkKKKKKkɬɪkkKJ* +* + + +ȨȨɨɨȨ  23ގގޯޯޯޮޮޮޮޮ22233333333ޯޮލގޭޮލލލڍmmllKK+ + + +ɨŨũ , , MLnNڎ ͬɌŋŋlŌlkLkKK+KKKɬɫŋkKJ** + + + + ɬȨȨȨȨ   Ȩ43ގގޯޮޮޯ3322233232223ޮގޮގގލލmmލlLkl+++ + +ɩ , L+,LNmmڰM ͬɌkkkkkklkLJKKLKKKͫūŋkKJ** + + + +ɨɨɨȨȨȨ 23ގގގޯ223223232ޯޮގޮޭލލڎލڍލmlLmK++) + ,+, MMnL֯n ͫɌkkkkkkkkKKJKKK+KkͬɫŋjKJ*** + + ȨȨɤɨ Ȭ3ގ23223332222ޮލގގޮޭލmڍllLlkLL+ + +ͨɩ L , ,MNMm, ɬljkkkkklkkKKKK*KKKlɬɋŋkK* +* + +* ȨȨȨ 謎2ޯ12332332ޯޮގޮޮލڍmmލmlllLKK*++ ɩɩɩ ,,,,,MMMMڰM ɬɋkkkkkkkKK*KK++*KKɬɋkkK** + + + + + + ȨɨȨȨ  謍ޮ3232222232ޮޯޮގގޭmڍmllLlLK+++  +ũŪ M+,+,nMLڎްn ͬɬkkjkkkllkKKKJ++K+KkŬɬɬɫɋkJ*J + + + ɬȨȨȨȬ mލگ34332233ޯޮގޭލލލmތlmLllKL+*+ + + +ɩʼnŊL,,,+Lm,MگMɬŋkkKkKkkkKKK+K++K+KŋūɬɫŋkkJ*** +* + + +ȨɨȤȨȨmڍޮ3TUT4322ޮ3ޮޮޮގލލލڍlllllK+K+ + + + + ͨʼn L,L LMMMڏn ͬɌŋkKKlkklkKJ++++++*KɫŬɫŋkJK** * + + +ȨȨȨȨȨɬlmڍޮޯ3TUuTT4333222ޮޮ2ޮޮގގޮލlڌmllkKKK+J + + Ŋʼn -,M,,Mm,nگ,ɫkkkkkkkkkKKKK*+K+KKkūŬɫŋŋkJ*** + + + + ɨɨȨȬmnڎގޯTTuuuTTS4322222323ޮޮޮޮޭޯޮލޮލގލڍlڍllL*l+K + ++ *+ + +ɉʼn,L,M+,n,L֏ޏmͭɋkKKkKkklkKK+*++K+KKlŬɫɋŋkJJ*** + + + + + + ȨɨɨȨȨɬȬmmmڍޮ2TUTUutUUU32333233232333ޮތޮޭތޮޮޮޮޮޮޮޮޮޮޯޮޮޮޮލލޮތڎތlmlڌKKK**+* + + + + + +ɩʼniL,-,,MM,nڏn ͬɌŋkKKkKkkKKK++*++++JkŋūŋjkK + + + + + + +ȬȨȨȨɬɬmލڮގޮ3TTuUUUvT3232333333322233333332ޮޮޮގޮޭޮڭޏލڮޯޮޮޮގޭޮގލލlڍlmlKL**+ * L + +ͩɊii ,,L,LNLMnގ-ͬɌŋkKKkkkkKKJ+++*++*KkɫɌŋkJJJ + + +* + ɬȨȨȨȬȨmލގޮޯ44utvvvT22333343443S32322323S3332ޮޭތڮޮޮޯlޮޮmڮލڭޮޮޮޮޮޮޯޯޮޮލޮލގlڍllڌkKKJK* + + ++, ++ ͉ijŪ -+L,,M-,nޏN ɬɬkjkKKKkKKKK+*+++*++KūŋŋŋkJJ** + + + + + ȨɬȨɬȨގޯޮޯ44TuvvuT222333333ST4343423223333SS111ޭލޮލގڍގޯڮޭލڮގލލlڭލލލlڮޮތڭލޮޮޮޮގޮޮޮލޮލޮލlڌތތލllKlK*+** + + + +ɪiij,,,-+MM,Mnގ-ͬɫŌkkkKkkKkkKK+*++++++kkŬūŋjjK** + + + + + + ɨȨȨȨȨȨɨޯ3STUvuU32232333T3343TT43322222233333222ޮލޮޮޭޮޭލޭގެmڍڎތލlڌڍڍލڍڍޭލލތڎލڮޮޮޯޮޮގގޭލލލލތlmkkkLK+* +*) +ɩŧ M +++ɪihi ,,,,,NMLnގMɬɋkkkKkKkKkK+++ +* ++*klūŋkjKJ** + + + + + + +ȨɬȨȨȨȨȨȨޯ23TuuUS22233334T34TT3S43332222233333222ޮލޭލޭލލލڭެެތmڍlmڌڍmڌڌލލތڌڮލڍގޭڮޮޯޮޮޮޮޭޭލޮލލmڍmތmkKlKK +*+ + +ɨɨŨũ +,+ + ͊ɉij,,,L,MM+Mڏm-ͫɬŌkkKKkkKLKKK+++* +++JkŋjjJK* + + +* + + + ȨȨɨȨȨȨȨ3TUuT322333333S3TSTTTSTS33322233323S3S32ޮގޮڍެގތlڌލmڍލl֍ll֍ތmllڌތތތލލލގޭޮޮޮޮޯޮޮލލڮލލތލލތlmklKKL*++ +* ɨŨŨ +L , ͪɉii L,,MLMMLnގMͬɌklkKkKkKKKK+* +*+ +++KŋkjK**+ +* + + + + + ɬɨɨȤȨȨ3S4TT4222333343S3T4TTT44S433232223332222ޭލޭޮޭklڍڌlkkڍތkLڌlڋތlތތލލގޭލގޭޮޮޯޯޮޯޯގޮޮގޮގޮޮލލތލlmތmތllLlk+K* ++ɨŧňL ,+ ͪɉhjj,M,-,,M,MڏM-ͬɬɫŋKkKkkKkKK+++ ++* ++KkŋŋjjJ* + + + + + + + +ɨɨȨȨȨ343S43222323333343TTST4TSTT333323333333322ޮޭޭލތmތlڌllkmڋkklڌkkllmڌllڍގލڍލލގލލލގޮޭޭޮޮޮޮޯޮޮޮލޮޮގލڮލލތގmlLllLKKK+K * + +ͨɧŨňũ+, + +ͪɊiii -+,M-MLLnn-ͬͬɌŌkKKKKkKKK+++ +* ++++KkkŋkKJ** + + + + + + + + +ȬȤȨȨȨ2S333233332333434STST3T4T4SS33232223323222ގލލޭތڍl֋lk֌jkڌmlkkmllKlތklڌڍލލlލޭڍތڍލގލލލޯގޯޮޮލޭޮޮڮގލލގލލڎލލlڌތlmkllKkKKK*+ + +) ɨŨŨňh m+ ,, ɪɉhij,,,M,L-,MnM ɫɬɫɋkkKkkKKKkK+ * + + ++ +++kkkjJJ* + + + + + + + +ɬɬɬȨȨȨ323323333333TS44S4TTTTT33S32232332332ޮޭmڮlڍllk֌lڌKlKkڍlllkڍklkklތlmތmڍlmmڍތލގލڎޭތޮڍގޭލޮޭޭmڎލލڍڍލލmlތlllllklKl+KK+* ɨňL- ++ ͪɊhii ,,+MLM,,nn,ͬɫɌŋkkKKKKKKKK+* + +* + +KKkkkkJ+* + + + + + + + ȨȨȨ2232232232323334333TTT4T4T4TT333332233222ޮލڎތދkڌlڌKlklmKlllڍkLkllKlKڌllڌlލލތލlތlڌmmڍލލލލڍލތލލލލތލތڍލlڍތmlmlkmlklLkKK+J) + + +ɨŨŨhhhh m, ,, ɪɉiij L,,M,L,Mnn ɬɬɬŋlkkKKkKKKKK + + +*KklkK** + + + + + + +ɬȬȨ22133323333333S3T444TS3TTTT43232322ޭޮތmڌތkڌmkllllKklllKklLkllllllڍllllލlllڌڎlڍllmތmllލލލllڍlڍލlތlmlllllKLKKK*+* +* +ɨŨŨŇghhhhLM ,, ,Ɋiij ,,,ML-,,mn,ɫɬɌŋkKKKKKkKK+++ + + +*++KKkkKK* + + + + + + + +Ȩ2332222333323334334SSTS44TTTSSS3332222ޮޮޮލތޭތލlklKllkKkkLKlkKLlllLKllllkllllllڌmkڌތތڍތތތތތlmlތތlލlތlllllKkLLKKK+K+* + +) ɨŨŧŇhhghghh m +,, ͩɊHjj ,,+--,,,LnM ͬɬɌŌkkKKKkKKKK++ + + ++ + +*+KKKk+*** + + + + + + + +32333333333443TTTT44T43332222ޯލލތllKklkkLlkKLkkKlkKKKlKLLLllKlkklllllڍkLllllڌklkތތlllkllklKlLKLKKK+KK+*** + ɨŨgghgghggh,M ,L ++ͪɪiij,,,+ML,,+Nn,ͬɬɫɬɋŋkkJKKKKJK+*+ + + + + + +**KKJ+J + + + + + + + + ɬ \ No newline at end of file diff --git a/_pico/themes/raspberry/gridcell.bin b/_pico/themes/raspberry/gridcell.bin new file mode 100644 index 0000000000000000000000000000000000000000..3d4f90045ac3fa5b82eb52fb6323e78c5584365b GIT binary patch literal 4096 zcmb0c@_>N^fX%ZE48j$=?hpifDufvruK_e1VnSdv{fy=xECnFLX!$`*?wK=ZH2;j|pJADQh;6^%YJW^2uKhFxhkgd5 zDYpnV-|pO+VuYjp#Gs(yKm<@wz@?voL4gPWYNld|ei@KBu^5~FQL)hw7!85Z5FjrE E0I=ormjD0& literal 0 HcmV?d00001 diff --git a/_pico/themes/raspberry/gridcellPltt.bin b/_pico/themes/raspberry/gridcellPltt.bin new file mode 100644 index 0000000000000000000000000000000000000000..1a2c743e99407d84d1552bcace41417f39322fdd GIT binary patch literal 64 fcmZP|0yuGSC`u%QV0HeS;=Sefs_#|*uZKVYlvNG9 literal 0 HcmV?d00001 diff --git a/_pico/themes/raspberry/gridcellSelected.bin b/_pico/themes/raspberry/gridcellSelected.bin new file mode 100644 index 0000000000000000000000000000000000000000..053530cea324f2f8b5daac3c161facf655ce7802 GIT binary patch literal 4096 zcmb0d@_>N^fX%ZE48k*i5dmfjGcaJ+Kb;8OzkdCij!Qpr+JF7Rpbp*Zi}EIR9_K;eQ4rq72w#groh$ sprGJD1W-`GrJsR8fd~LW?OaK4? literal 0 HcmV?d00001 diff --git a/_pico/themes/raspberry/gridcellSelectedPltt.bin b/_pico/themes/raspberry/gridcellSelectedPltt.bin new file mode 100644 index 0000000000000000000000000000000000000000..77f11308df37270166543d78d4a1d6fb5d0db5b2 GIT binary patch literal 64 ecmZP|0wnWjs#>O7rblJZDV|fjxBP!S0s;W8H4g~@ literal 0 HcmV?d00001 diff --git a/_pico/themes/raspberry/scrim.bin b/_pico/themes/raspberry/scrim.bin new file mode 100644 index 0000000000000000000000000000000000000000..29b397186e907f1df18c9fe065e311542b1c606a GIT binary patch literal 336 zcmbO}efoR|hEmhvpnv-G>HX-Sy}!L3jN2igz8wndA)ve-3dtwl_iwram AT>twl_ewram :arm7i + + .twl_bss ALIGN(4) (NOLOAD) : + { + __twl_bss_start__ = .; + *(.twl_bss .twl_bss.*) + *(.twl.bss .twl.bss.*) + *.twl.*(.dynbss) + *.twl.*(.gnu.linkonce.b*) + *.twl.*(.bss*) + *.twl.*(COMMON) + . = ALIGN(4); + __twl_bss_end__ = .; + } >twl_iwram :NONE + + .twl_noinit ALIGN(4) (NOLOAD): + { + __twl_noinit_start__ = ABSOLUTE(.); + *(.twl_noinit .twl_noinit.*) + *(.twl.noinit .twl.noinit.*) + *.twl*(.noinit) + *.twl*(.noinit.*) + *.twl*(.gnu.linkonce.n.*) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + __twl_noinit_end__ = ABSOLUTE(.); + __twl_end__ = ABSOLUTE(.); + } >twl_iwram :NONE + + .crt0 : + { + KEEP (*(.crt0)) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >ewram :crt0 + + .text : + { + __arm7_lma__ = LOADADDR(.text); + __arm7_start__ = .; + KEEP (*(SORT_NONE(.init))) + *(.plt) + *(.text .stub .text.* .gnu.linkonce.t.*) + KEEP (*(.text.*personality*)) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + *(.glue_7t) *(.glue_7) *(.v4_bx) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >iwram AT>ewram :arm7 + + .fini : + { + KEEP (*(.fini)) + } >iwram AT>ewram + + .rodata : + { + *(.rodata) + *all.rodata*(*) + *(.roda) + *(.rodata.*) + *(.gnu.linkonce.r*) + SORT(CONSTRUCTORS) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >iwram AT>ewram + + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >iwram AT>ewram + + .ARM.exidx : { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } >iwram AT>ewram + + /* Ensure the __preinit_array_start label is properly aligned. We + could instead move the label definition inside the section, but + the linker would then create the section even if it turns out to + be empty, which isn't pretty. */ + . = ALIGN(32 / 8); + .init_array : + { + PROVIDE (__preinit_array_start = .); + PROVIDE (__bothinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE (__preinit_array_end = .); + + PROVIDE (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE (__init_array_end = .); + PROVIDE (__bothinit_array_end = .); + } >iwram AT>ewram + .fini_array : + { + PROVIDE (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + /* Required by pico-exitprocs.c. */ + KEEP (*(.fini_array*)) + PROVIDE (__fini_array_end = .); + } >iwram AT>ewram + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >iwram AT>ewram + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >iwram AT>ewram + + .eh_frame : + { + KEEP (*(.eh_frame)) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >iwram AT>ewram + + .gcc_except_table : + { + *(.gcc_except_table .gcc_except_table.*) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >iwram AT>ewram + .got : { *(.got.plt) *(.got) } >iwram AT>ewram + + .data ALIGN(4) : { + __data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d*) + CONSTRUCTORS + . = ALIGN(4); + } >iwram AT>ewram + + .tdata ALIGN(4) : + { + __tdata_start = ABSOLUTE(.); + *(.tdata .tdata.* .gnu.linkonce.td.*) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + __tdata_end = ABSOLUTE(.); + __data_end = . ; + } >iwram AT>ewram + + __tdata_size = __tdata_end - __tdata_start ; + + .tbss ALIGN(4) (NOLOAD) : + { + __tbss_start = ABSOLUTE(.); + *(.tbss .tbss.* .gnu.linkonce.tb.*) + *(.tcommon) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + __tbss_end = ABSOLUTE(.); + } >iwram AT>ewram + + __tbss_size = __tbss_end - __tbss_start ; + + .bss ALIGN(4) (NOLOAD) : + { + __arm7_end__ = .; + __bss_start = ABSOLUTE(.); + __bss_start__ = ABSOLUTE(.); + *(.dynbss) + *(.gnu.linkonce.b*) + *(.bss*) + *(COMMON) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + __bss_end__ = ABSOLUTE(.); + } >iwram + + .noinit (NOLOAD): + { + __noinit_start = ABSOLUTE(.); + *(.noinit .noinit.* .gnu.linkonce.n.*) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + __noinit_end = ABSOLUTE(.); + } >iwram + + /* Space reserved for the thread local storage of main() */ + .tls ALIGN(4) (NOLOAD) : + { + __tls_start = ABSOLUTE(.); + . = . + __tdata_size + __tbss_size; + __tls_end = ABSOLUTE(.); + __end__ = ABSOLUTE(.); + } >iwram + + __tls_size = __tls_end - __tls_start; + + HIDDEN(__arm7_size__ = __arm7_end__ - __arm7_start__); + HIDDEN(__arm7i_size__ = __arm7i_end__ - __arm7i_start__); + HIDDEN(__bss_size__ = __bss_end__ - __bss_start__); + HIDDEN(__twl_bss_size__ = __twl_bss_end__ - __twl_bss_start__); + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 (INFO) : { *(.comment); LINKER_VERSION; } + .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1. */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions. */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2. */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2. */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions. */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3. */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + /* DWARF 5. */ + .debug_addr 0 : { *(.debug_addr) } + .debug_line_str 0 : { *(.debug_line_str) } + .debug_loclists 0 : { *(.debug_loclists) } + .debug_macro 0 : { *(.debug_macro) } + .debug_names 0 : { *(.debug_names) } + .debug_rnglists 0 : { *(.debug_rnglists) } + .debug_str_offsets 0 : { *(.debug_str_offsets) } + .debug_sup 0 : { *(.debug_sup) } + .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) KEEP (*(.gnu.attributes)) } + .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) } + /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } +} diff --git a/arm7/dldi_ds_arm7.specs b/arm7/dldi_ds_arm7.specs new file mode 100644 index 0000000..47a284a --- /dev/null +++ b/arm7/dldi_ds_arm7.specs @@ -0,0 +1,23 @@ +%include + +%rename cc1plus blocksds_cc1plus +%rename cpp blocksds_cpp +%rename link blocksds_link + +*cpp: +-D__NDS__ -D__BLOCKSDS__ -DARM7 %(blocksds_cpp) + +*cc1_cpu: +-mcpu=arm7tdmi + +*cc1plus: +%(cpp) %(blocksds_cc1plus) + +*link: +%(blocksds_link) -T arm7/dldi_ds_arm7.ld --gc-sections --no-warn-rwx-segments + +*startfile: +%:getenv(BLOCKSDS /sys/crts/ds_arm7_crt0%O) + +*lib: +%(libgcc) diff --git a/arm7/source/Arm7State.h b/arm7/source/Arm7State.h new file mode 100644 index 0000000..b306ac5 --- /dev/null +++ b/arm7/source/Arm7State.h @@ -0,0 +1,8 @@ +#pragma once + +/// @brief Enum representing the arm7 state. +enum class Arm7State +{ + Idle, + ExitRequested +}; diff --git a/arm7/source/ExitMode.h b/arm7/source/ExitMode.h new file mode 100644 index 0000000..6c62d4a --- /dev/null +++ b/arm7/source/ExitMode.h @@ -0,0 +1,12 @@ +#pragma once + +/// @brief Enum representing the exit mode of launcher. +enum class ExitMode +{ + /// @brief Reset the system (DSi mode only). + Reset, + /// @brief Power off the system. + PowerOff, + /// @brief Launch an application through pico loader. + PicoLoader +}; diff --git a/arm7/source/common.h b/arm7/source/common.h new file mode 100644 index 0000000..c717672 --- /dev/null +++ b/arm7/source/common.h @@ -0,0 +1,2 @@ +#pragma once +#include diff --git a/arm7/source/dldi.s b/arm7/source/dldi.s new file mode 100644 index 0000000..28d95fa --- /dev/null +++ b/arm7/source/dldi.s @@ -0,0 +1,16 @@ +.global _dldi_start +.equ _dldi_start, 0x037F8000 +.global _io_dldi +.equ _io_dldi, (_dldi_start + 0x60) +.global _DLDI_startup_ptr +.equ _DLDI_startup_ptr, (_io_dldi + 0x8) +.global _DLDI_isInserted_ptr +.equ _DLDI_isInserted_ptr, (_io_dldi + 0xC) +.global _DLDI_readSectors_ptr +.equ _DLDI_readSectors_ptr, (_io_dldi + 0x10) +.global _DLDI_writeSectors_ptr +.equ _DLDI_writeSectors_ptr, (_io_dldi + 0x14) +.global _DLDI_clearStatus_ptr +.equ _DLDI_clearStatus_ptr, (_io_dldi + 0x18) +.global _DLDI_shutdown_ptr +.equ _DLDI_shutdown_ptr, (_io_dldi + 0x1C) \ No newline at end of file diff --git a/arm7/source/ipcServices/DldiIpcService.cpp b/arm7/source/ipcServices/DldiIpcService.cpp new file mode 100644 index 0000000..9315a2d --- /dev/null +++ b/arm7/source/ipcServices/DldiIpcService.cpp @@ -0,0 +1,46 @@ +#include "common.h" +#include +#include +#include "DldiIpcService.h" + +extern FN_MEDIUM_STARTUP _DLDI_startup_ptr; +extern FN_MEDIUM_READSECTORS _DLDI_readSectors_ptr; +extern FN_MEDIUM_WRITESECTORS _DLDI_writeSectors_ptr; + +void DldiIpcService::HandleMessage(u32 data) +{ + auto cmd = reinterpret_cast(data << 2); + switch (cmd->cmd) + { + case DLDI_IPC_CMD_SETUP: + SetupDldi(cmd); + break; + + case DLDI_IPC_CMD_READ_SECTORS: + ReadSectors(cmd); + break; + + case DLDI_IPC_CMD_WRITE_SECTORS: + WriteSectors(cmd); + break; + } +} + +void DldiIpcService::SetupDldi(const dldi_ipc_cmd_t* cmd) const +{ + memcpy((void*)0x037F8000, cmd->buffer, 16 * 1024); + bool result = _DLDI_startup_ptr(); + SendResponseMessage(result); +} + +void DldiIpcService::ReadSectors(const dldi_ipc_cmd_t* cmd) const +{ + _DLDI_readSectors_ptr(cmd->sector, cmd->count, cmd->buffer); + SendResponseMessage(0); +} + +void DldiIpcService::WriteSectors(const dldi_ipc_cmd_t* cmd) const +{ + _DLDI_writeSectors_ptr(cmd->sector, cmd->count, cmd->buffer); + SendResponseMessage(0); +} diff --git a/arm7/source/ipcServices/DldiIpcService.h b/arm7/source/ipcServices/DldiIpcService.h new file mode 100644 index 0000000..d5732f0 --- /dev/null +++ b/arm7/source/ipcServices/DldiIpcService.h @@ -0,0 +1,19 @@ +#pragma once +#include "ipc/ThreadIpcService.h" +#include "dldiIpcCommand.h" +#include "ipcChannels.h" + +class DldiIpcService : public ThreadIpcService +{ + u32 _threadStack[128]; + + void SetupDldi(const dldi_ipc_cmd_t* cmd) const; + void ReadSectors(const dldi_ipc_cmd_t* cmd) const; + void WriteSectors(const dldi_ipc_cmd_t* cmd) const; + +public: + DldiIpcService() + : ThreadIpcService(IPC_CHANNEL_DLDI, 6, _threadStack, sizeof(_threadStack)) { } + + void HandleMessage(u32 data) override; +}; diff --git a/arm7/source/ipcServices/DsiSdIpcService.h b/arm7/source/ipcServices/DsiSdIpcService.h new file mode 100644 index 0000000..24620fd --- /dev/null +++ b/arm7/source/ipcServices/DsiSdIpcService.h @@ -0,0 +1,19 @@ +#pragma once +#include "ipc/ThreadIpcService.h" +#include "dsiSdIpcCommand.h" +#include "ipcChannels.h" + +class DsiSdIpcService : public ThreadIpcService +{ + u32 _threadStack[128]; + + void ReadSectors(const dsisd_ipc_cmd_t* cmd) const; + void WriteSectors(const dsisd_ipc_cmd_t* cmd) const; + +public: + DsiSdIpcService() + : ThreadIpcService(IPC_CHANNEL_DSI_SD, 5, _threadStack, sizeof(_threadStack)) { } + + void Start() override; + void HandleMessage(u32 data) override; +}; diff --git a/arm7/source/ipcServices/DsiSdIpcService.twl.cpp b/arm7/source/ipcServices/DsiSdIpcService.twl.cpp new file mode 100644 index 0000000..f97a753 --- /dev/null +++ b/arm7/source/ipcServices/DsiSdIpcService.twl.cpp @@ -0,0 +1,36 @@ +#include +#include "../mmc/sdmmc.h" +#include "DsiSdIpcService.h" + +void DsiSdIpcService::Start() +{ + pico_SDMMC_init(SDMMC_DEV_CARD); + ThreadIpcService::Start(); +} + +void DsiSdIpcService::HandleMessage(u32 data) +{ + auto cmd = reinterpret_cast(data << 2); + switch (cmd->cmd) + { + case DSI_SD_IPC_CMD_READ_SECTORS: + ReadSectors(cmd); + break; + + case DSI_SD_IPC_CMD_WRITE_SECTORS: + WriteSectors(cmd); + break; + } +} + +void DsiSdIpcService::ReadSectors(const dsisd_ipc_cmd_t* cmd) const +{ + pico_SDMMC_readSectors(SDMMC_DEV_CARD, cmd->sector, cmd->buffer, cmd->count); + SendResponseMessage(0); +} + +void DsiSdIpcService::WriteSectors(const dsisd_ipc_cmd_t* cmd) const +{ + pico_SDMMC_writeSectors(SDMMC_DEV_CARD, cmd->sector, cmd->buffer, cmd->count); + SendResponseMessage(0); +} diff --git a/arm7/source/ipcServices/RtcIpcService.cpp b/arm7/source/ipcServices/RtcIpcService.cpp new file mode 100644 index 0000000..6013125 --- /dev/null +++ b/arm7/source/ipcServices/RtcIpcService.cpp @@ -0,0 +1,9 @@ +#include "common.h" +#include +#include "RtcIpcService.h" + +void RtcIpcService::HandleMessage(u32 data) +{ + rtc_readDateTime(reinterpret_cast(data << 2)); + SendResponseMessage(1); +} diff --git a/arm7/source/ipcServices/RtcIpcService.h b/arm7/source/ipcServices/RtcIpcService.h new file mode 100644 index 0000000..afe5420 --- /dev/null +++ b/arm7/source/ipcServices/RtcIpcService.h @@ -0,0 +1,14 @@ +#pragma once +#include "ipc/ThreadIpcService.h" +#include "ipcChannels.h" + +class RtcIpcService : public ThreadIpcService +{ + u32 _threadStack[128]; + +public: + RtcIpcService() + : ThreadIpcService(IPC_CHANNEL_RTC, 10, _threadStack, sizeof(_threadStack)) { } + + void HandleMessage(u32 data) override; +}; diff --git a/arm7/source/ipcServices/SoundIpcService.cpp b/arm7/source/ipcServices/SoundIpcService.cpp new file mode 100644 index 0000000..96bd176 --- /dev/null +++ b/arm7/source/ipcServices/SoundIpcService.cpp @@ -0,0 +1,49 @@ +#include "common.h" +#include +#include "soundIpcCommand.h" +#include "SoundIpcService.h" + +void SoundIpcService::OnMessageReceived(u32 data) +{ + const u32* commandList = reinterpret_cast(data); + u32 cmdCount = *commandList++; + for (u32 i = 0; i < cmdCount; i++) + { + u32 cmdValue = *commandList++; + u32 cmd = cmdValue & 0xFF; + u32 cmdArg = cmdValue >> 8; + switch (cmd) + { + case SND_IPC_CMD_START_CHANNELS: + { + u32 channelsMask = cmdArg; + for (u32 j = 0; j < 16; j++) + { + if (channelsMask & (1u << j)) + snd_startChannel(j); + } + break; + } + case SND_IPC_CMD_STOP_CHANNELS: + { + u32 channelsMask = cmdArg; + for (u32 j = 0; j < 16; j++) + { + if (channelsMask & (1u << j)) + snd_stopChannel(j); + } + break; + } + case SND_IPC_CMD_SETUP_CHANNEL: + { + u32 channel = cmdArg; + REG_SOUNDxSAD(channel) = *commandList++; + REG_SOUNDxTMR(channel) = *commandList++; + REG_SOUNDxPNT(channel) = *commandList++; + REG_SOUNDxLEN(channel) = *commandList++; + REG_SOUNDxCNT(channel) = *commandList++; + break; + } + } + } +} diff --git a/arm7/source/ipcServices/SoundIpcService.h b/arm7/source/ipcServices/SoundIpcService.h new file mode 100644 index 0000000..861a0e9 --- /dev/null +++ b/arm7/source/ipcServices/SoundIpcService.h @@ -0,0 +1,12 @@ +#pragma once +#include "ipc/IpcService.h" +#include "ipcChannels.h" + +class SoundIpcService : public IpcService +{ +public: + SoundIpcService() + : IpcService(IPC_CHANNEL_SOUND) { } + + void OnMessageReceived(u32 data) override; +}; diff --git a/arm7/source/main.cpp b/arm7/source/main.cpp new file mode 100644 index 0000000..9090ec9 --- /dev/null +++ b/arm7/source/main.cpp @@ -0,0 +1,241 @@ +#include "common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "logger/PlainLogger.h" +#include "logger/NocashOutputStream.h" +#include "logger/NullLogger.h" +#include "logger/ThreadSafeLogger.h" +#include "picoLoaderBootstrap.h" +#include "sharedMemory.h" +#include "ipcServices/DsiSdIpcService.h" +#include "ipcServices/DldiIpcService.h" +#include "ipcServices/SoundIpcService.h" +#include "ipcServices/RtcIpcService.h" +#include "ExitMode.h" +#include "Arm7State.h" +#include "mmc/tmio.h" + +static NocashOutputStream sNocashOutputStream; +static PlainLogger sPlainLogger = PlainLogger(LogLevel::All, std::unique_ptr(&sNocashOutputStream)); +static ThreadSafeLogger sThreadSafeLogger = ThreadSafeLogger(std::unique_ptr(&sPlainLogger)); + +static DsiSdIpcService sDsiSdIpcService; +static DldiIpcService sDldiIpcService; +static SoundIpcService sSoundIpcService; +static RtcIpcService sRtcIpcService; + +ILogger* gLogger = &sThreadSafeLogger; + +static rtos_event_t sVBlankEvent; +static ExitMode sExitMode; +static Arm7State sState; +static volatile u8 sMcuIrqFlag = false; + +static void vblankIrq(u32 irqMask) +{ + rtos_signalEvent(&sVBlankEvent); +} + +static void vcountIrq(u32 irqMask) +{ + SHARED_KEY_XY = REG_RCNT0_H; +} + +static void mcuIrq(u32 irq2Mask) +{ + sMcuIrqFlag = true; +} + +static void checkMcuIrq(void) +{ + // mcu only exists in DSi mode + if (isDSiMode()) + { + // check and ack the flag atomically + if (mem_swapByte(false, &sMcuIrqFlag)) + { + // check the irq mask + u32 irqMask = mcu_getIrqMask(); + if (irqMask & MCU_IRQ_RESET) + { + // power button was released + sExitMode = ExitMode::Reset; + sState = Arm7State::ExitRequested; + } + else if (irqMask & MCU_IRQ_POWER_OFF) + { + // power button was held long to trigger a power off + sExitMode = ExitMode::PowerOff; + sState = Arm7State::ExitRequested; + } + } + } +} + +static void initializeVBlankIrq() +{ + rtos_createEvent(&sVBlankEvent); + rtos_setIrqFunc(RTOS_IRQ_VBLANK, vblankIrq); + rtos_enableIrqMask(RTOS_IRQ_VBLANK); + gfx_setVBlankIrqEnabled(true); +} + +static void clearSoundRegisters() +{ + REG_SOUNDCNT = 0; + REG_SNDCAP0CNT = 0; + REG_SNDCAP1CNT = 0; + + for (int i = 0; i < 16; i++) + { + REG_SOUNDxCNT(i) = 0; + REG_SOUNDxSAD(i) = 0; + REG_SOUNDxTMR(i) = 0; + REG_SOUNDxPNT(i) = 0; + REG_SOUNDxLEN(i) = 0; + } +} + +static void initializeArm7() +{ + rtos_initIrq(); + rtos_startMainThread(); + ipc_initFifoSystem(); + + clearSoundRegisters(); + + pmic_setAmplifierEnable(true); + sys_setSoundPower(true); + + readUserSettings(); + pmic_setPowerLedBlink(PMIC_CONTROL_POWER_LED_BLINK_NONE); + + sio_setGpioSiIrq(false); + sio_setGpioMode(RCNT0_L_MODE_GPIO); + + rtc_init(); + + if (isDSiMode()) + { + TMIO_init(); + sDsiSdIpcService.Start(); + } + + sDldiIpcService.Start(); + pload_init(); + + snd_setMasterVolume(127); + snd_setMasterEnable(true); + sSoundIpcService.Start(); + sRtcIpcService.Start(); + + gfx_setVCountMatchLine(96); + rtos_setIrqFunc(RTOS_IRQ_VCOUNT, vcountIrq); + rtos_enableIrqMask(RTOS_IRQ_VCOUNT); + gfx_setVCountMatchIrqEnabled(true); + + initializeVBlankIrq(); + + if (isDSiMode()) + { + rtos_setIrq2Func(RTOS_IRQ2_MCU, mcuIrq); + rtos_enableIrq2Mask(RTOS_IRQ2_MCU); + } + + ipc_setArm7SyncBits(7); +} + +static void updateArm7IdleState() +{ + if (pload_shouldStart()) + { + sExitMode = ExitMode::PicoLoader; + sState = Arm7State::ExitRequested; + } + else + { + checkMcuIrq(); + } + + if (sState == Arm7State::ExitRequested) + { + snd_setMasterVolume(0); // mute sound + } +} + +static bool performExit(ExitMode exitMode) +{ + switch (exitMode) + { + case ExitMode::Reset: + { + mcu_setWarmBootFlag(true); + mcu_hardReset(); + break; + } + case ExitMode::PowerOff: + { + pmic_shutdown(); + break; + } + case ExitMode::PicoLoader: + { + pload_start(); + break; + } + } + + while (true); // wait infinitely for exit +} + +static void updateArm7ExitRequestedState() +{ + performExit(sExitMode); +} + +static void updateArm7() +{ + switch (sState) + { + case Arm7State::Idle: + { + updateArm7IdleState(); + break; + } + case Arm7State::ExitRequested: + { + updateArm7ExitRequestedState(); + break; + } + } +} + +int main() +{ + sState = Arm7State::Idle; + initializeArm7(); + + while (true) + { + rtos_waitEvent(&sVBlankEvent, true, true); + updateArm7(); + } + + return 0; +} diff --git a/arm7/source/mmc/mmc_spec.h b/arm7/source/mmc/mmc_spec.h new file mode 100644 index 0000000..b6adebd --- /dev/null +++ b/arm7/source/mmc/mmc_spec.h @@ -0,0 +1,235 @@ +#pragma once + +// SPDX-License-Identifier: MIT +// Copyright (c) 2023 profi200 + +// Based on JEDEC eMMC Card Product Standard V4.41. + +#include "tmio.h" + + +// Controller specific macros. Add controller specific bits here. +// MMC_CMD_[response type]_[transfer type] +// Transfer type: R = read, W = write. +#define MMC_CMD_NONE(id) (CMD_RESP_NONE | (id)) +#define MMC_CMD_R1(id) (CMD_RESP_R1 | (id)) +#define MMC_CMD_R1b(id) (CMD_RESP_R1b | (id)) +#define MMC_CMD_R2(id) (CMD_RESP_R2 | (id)) +#define MMC_CMD_R3(id) (CMD_RESP_R3 | (id)) +#define MMC_CMD_R4(id) (CMD_RESP_R4 | (id)) +#define MMC_CMD_R5(id) (CMD_RESP_R5 | (id)) +#define MMC_CMD_R1_R(id) (CMD_DATA_R | CMD_DATA_EN | CMD_RESP_R1 | (id)) +#define MMC_CMD_R1_W(id) (CMD_DATA_W | CMD_DATA_EN | CMD_RESP_R1 | (id)) + + +// Basic commands and read-stream command (class 0 and class 1). +#define MMC_GO_IDLE_STATE MMC_CMD_NONE(0u) // -, [31:0] 0x00000000 GO_IDLE_STATE, 0xF0F0F0F0 GO_PRE_IDLE_STATE, 0xFFFFFFFA BOOT_INITIATION. +#define MMC_SEND_OP_COND MMC_CMD_R3(1u) // R3, [31:0] OCR with-out busy. +#define MMC_ALL_SEND_CID MMC_CMD_R2(2u) // R2, [31:0] stuff bits. +#define MMC_SET_RELATIVE_ADDR MMC_CMD_R1(3u) // R1, [31:16] RCA [15:0] stuff bits. +#define MMC_SET_DSR MMC_CMD_NONE(4u) // -, [31:16] DSR [15:0] stuff bits. +#define MMC_SLEEP_AWAKE MMC_CMD_R1b(5u) // R1b, [31:16] RCA [15] Sleep/Awake [14:0] stuff bits. +#define MMC_SWITCH MMC_CMD_R1b(6u) // R1b, [31:26] Set to 0 [25:24] Access [23:16] Index [15:8] Value [7:3] Set to 0 [2:0] Cmd Set. +#define MMC_SELECT_CARD MMC_CMD_R1b(7u) // R1/R1b, [31:16] RCA [15:0] stuff bits. Note: "R1b while selecting from Disconnected State to Programming State." +#define MMC_DESELECT_CARD MMC_CMD_NONE(7u) // -, [31:16] RCA [15:0] stuff bits. +#define MMC_SEND_EXT_CSD MMC_CMD_R1_R(8u) // R1, [31:0] stuff bits. +#define MMC_SEND_CSD MMC_CMD_R2(9u) // R2, [31:16] RCA [15:0] stuff bits. +#define MMC_SEND_CID MMC_CMD_R2(10u) // R2, [31:16] RCA [15:0] stuff bits. +#define MMC_READ_DAT_UNTIL_STOP MMC_CMD_R1_R(11u) // R1, [31:0] data address. +#define MMC_STOP_TRANSMISSION MMC_CMD_R1b(12u) // R1/R1b, [31:16] RCA [15:1] stuff bits [0] HPI. Note: "RCA in CMD12 is used only if HPI bit is set." Note 2: "R1 for read cases and R1b for write cases." +#define MMC_SEND_STATUS MMC_CMD_R1(13u) // R1, [31:16] RCA [15:1] stuff bits [0] HPI. +#define MMC_BUSTEST_R MMC_CMD_R1_R(14u) // R1, [31:0] stuff bits. +#define MMC_GO_INACTIVE_STATE MMC_CMD_NONE(15u) // -, [31:16] RCA [15:0] stuff bits. +#define MMC_BUSTEST_W MMC_CMD_R1_W(19u) // R1, [31:0] stuff bits. + +// Block-oriented read commands (class 2). +#define MMC_SET_BLOCKLEN MMC_CMD_R1(16u) // R1, [31:0] block length. +#define MMC_READ_SINGLE_BLOCK MMC_CMD_R1_R(17u) // R1, [31:0] data address. +#define MMC_READ_MULTIPLE_BLOCK MMC_CMD_R1_R(18u) // R1, [31:0] data address. + +// Stream write commands (class 3). +#define MMC_WRITE_DAT_UNTIL_STOP MMC_CMD_R1_W(20u) // R1, [31:0] data address. + +// Block-oriented write commands (class 4). +#define MMC_SET_BLOCK_COUNT MMC_CMD_R1(23u) // R1, [31] Reliable Write Request [30:16] set to 0 [15:0] number of blocks. +#define MMC_WRITE_BLOCK MMC_CMD_R1_W(24u) // R1, [31:0] data address. +#define MMC_WRITE_MULTIPLE_BLOCK MMC_CMD_R1_W(25u) // R1, [31:0] data address. +#define MMC_PROGRAM_CID MMC_CMD_R1_W(26u) // R1, [31:0] stuff bits. +#define MMC_PROGRAM_CSD MMC_CMD_R1_W(27u) // R1, [31:0] stuff bits. + +// Block-oriented write protection commands (class 6). +#define MMC_SET_WRITE_PROT MMC_CMD_R1b(28u) // R1b, [31:0] data address. +#define MMC_CLR_WRITE_PROT MMC_CMD_R1b(29u) // R1b, [31:0] data address. +#define MMC_SEND_WRITE_PROT MMC_CMD_R1_R(30u) // R1, [31:0] write protect data address. +#define MMC_SEND_WRITE_PROT_TYPE MMC_CMD_R1_R(31u) // R1, [31:0] write protect data address. + +// Erase commands (class 5). +#define MMC_ERASE_GROUP_START MMC_CMD_R1(35u) // R1, [31:0] data address. +#define MMC_ERASE_GROUP_END MMC_CMD_R1(36u) // R1, [31:0] data address. +#define MMC_ERASE MMC_CMD_R1b(38u) // R1b, [31] Secure request [30:16] set to 0 [15] Force Garbage Collect request [14:1] set to 0 [0] Identify Write block for Erase. + +// I/O mode commands (class 9). +#define MMC_FAST_IO MMC_CMD_R4(39u) // R4, [31:16] RCA [15:15] register write flag [14:8] register address [7:0] register data. +#define MMC_GO_IRQ_STATE MMC_CMD_R5(40u) // R5, [31:0] stuff bits. + +// Lock card commands (class 7). +#define MMC_LOCK_UNLOCK MMC_CMD_R1_W(42u) // R1, [31:0] stuff bits. + +// Application-specific commands (class 8). +#define MMC_APP_CMD MMC_CMD_R1(55u) // R1, [31:16] RCA [15:0] stuff bits. +#define MMC_GEN_CMD_R MMC_CMD_R1_R(56u) // R1, [31:1] stuff bits [0] RD/WR = 1. +#define MMC_GEN_CMD_W MMC_CMD_R1_W(56u) // R1, [31:1] stuff bits [0] RD/WR = 0. + + +// 7.13 Card status. +// Type: +// E: Error bit. +// S: Status bit. +// R: Detected and set for the actual command response. +// X: Detected and set during command execution. The host can get the status by issuing a command with R1 response. +// +// Clear Condition: +// A: These bits are persistent, they are set and cleared in accordance with the card status. +// B: These bits are cleared as soon as the response (reporting the error) is sent out. +#define MMC_R1_APP_CMD (1u<<5) // S R A, The card will expect ACMD, or indication that the command has been interpreted as ACMD. +#define MMC_R1_URGENT_BKOPS (1u<<6) // S R A, If set, device needs to perform backgroundoperations urgently. Host can check EXT_CSD field BKOPS_STATUS for the detailed level. +#define MMC_R1_SWITCH_ERROR (1u<<7) // E X B, If set, the card did not switch to the expected mode as requested by the SWITCH command. +#define MMC_R1_READY_FOR_DATA (1u<<8) // S R A, Corresponds to buffer empty signalling on the bus. +#define MMC_R1_STATE_IDLE (0u<<9) // S R A +#define MMC_R1_STATE_READY (1u<<9) // S R A +#define MMC_R1_STATE_IDENT (2u<<9) // S R A +#define MMC_R1_STATE_STBY (3u<<9) // S R A +#define MMC_R1_STATE_TRAN (4u<<9) // S R A +#define MMC_R1_STATE_DATA (5u<<9) // S R A +#define MMC_R1_STATE_RCV (6u<<9) // S R A +#define MMC_R1_STATE_PRG (7u<<9) // S R A +#define MMC_R1_STATE_DIS (8u<<9) // S R A +#define MMC_R1_STATE_BTST (9u<<9) // S R A +#define MMC_R1_STATE_SLP (10u<<9) // S R A +#define MMC_R1_ERASE_RESET (1u<<13) // E R B, An erase sequence was cleared before executing because an out of erase sequence command was received (commands other than CMD35, CMD36, CMD38 or CMD13. +#define MMC_R1_WP_ERASE_SKIP (1u<<15) // E X B, Only partial address space was erased due to existing write protected blocks. +#define MMC_R1_CXD_OVERWRITE (1u<<16) // E X B, Can be either one of the following errors: - The CID register has been already written and can not be overwritten - The read only section of the CSD does not match the card content. - An attempt to reverse the copy (set as original) or permanent WP (unprotected) bits was made. +#define MMC_R1_OVERRUN (1u<<17) // E X B, The card could not sustain data programming in stream write mode. +#define MMC_R1_UNDERRUN (1u<<18) // E X B, The card could not sustain data transfer in stream read mode. +#define MMC_R1_ERROR (1u<<19) // E X B, (Undefined by the standard) A generic card error related to the (and detected during) execution of the last host command (e.g. read or write failures). +#define MMC_R1_CC_ERROR (1u<<20) // E R B, (Undefined by the standard) A card error occurred, which is not related to the host command. +#define MMC_R1_CARD_ECC_FAILED (1u<<21) // E X B, Card internal ECC was applied but failed to correct the data. +#define MMC_R1_ILLEGAL_COMMAND (1u<<22) // E R B, Command not legal for the card state. +#define MMC_R1_COM_CRC_ERROR (1u<<23) // E R B, The CRC check of the previous command failed. +#define MMC_R1_LOCK_UNLOCK_FAILED (1u<<24) // E X B, Set when a sequence or password error has been detected in lock/unlock card command. +#define MMC_R1_CARD_IS_LOCKED (1u<<25) // S R A, When set, signals that the card is locked by the host. +#define MMC_R1_WP_VIOLATION (1u<<26) // E X B, Attempt to program a write protected block. +#define MMC_R1_ERASE_PARAM (1u<<27) // E X B, An invalid selection of erase groups for erase occurred. +#define MMC_R1_ERASE_SEQ_ERROR (1u<<28) // E R B, An error in the sequence of erase commands occurred. +#define MMC_R1_BLOCK_LEN_ERROR (1u<<29) // E R B, Either the argument of a SET_BLOCKLEN command exceeds the maximum value allowed for the card, or the previously defined block length is illegal for the current command (e.g. the host issues a write command, the current block length is smaller than the card’s maximum and write partial blocks is not allowed). +#define MMC_R1_ADDRESS_MISALIGN (1u<<30) // E R/X B, The command’ s address argument (in accordance with the currently set block length) positions the first data block misaligned to the card physical blocks. A multiple block read/write operation (although started with a valid address/blocklength combination) is attempting to read or write a data block which does not align with the physical blocks of the card. +#define MMC_R1_ADDRESS_OUT_OF_RANGE (1u<<31) // E R/X B, The command’s address argument was out of the allowed range for this card. A multiple block or stream read/write operation is (although started in a valid address) attempting to read or write beyond the card capacity. + +#define MMC_R1_ERR_ALL (MMC_R1_ADDRESS_OUT_OF_RANGE | MMC_R1_ADDRESS_MISALIGN | \ + MMC_R1_BLOCK_LEN_ERROR | MMC_R1_ERASE_SEQ_ERROR | \ + MMC_R1_ERASE_PARAM | MMC_R1_WP_VIOLATION | MMC_R1_LOCK_UNLOCK_FAILED | \ + MMC_R1_COM_CRC_ERROR | MMC_R1_ILLEGAL_COMMAND | MMC_R1_CARD_ECC_FAILED | \ + MMC_R1_CC_ERROR | MMC_R1_ERROR | MMC_R1_UNDERRUN | MMC_R1_OVERRUN | \ + MMC_R1_CXD_OVERWRITE | MMC_R1_WP_ERASE_SKIP | MMC_R1_ERASE_RESET | \ + MMC_R1_SWITCH_ERROR) + +// 8.1 OCR register. +// Same bits for CMD1 argument. +#define MMC_OCR_1_7_1_95V (1u<<7) // 1.70–1.95V. +#define MMC_OCR_2_0_2_1V (1u<<8) // 2.0-2.1V. +#define MMC_OCR_2_1_2_2V (1u<<9) // 2.1-2.2V. +#define MMC_OCR_2_2_2_3V (1u<<10) // 2.2-2.3V. +#define MMC_OCR_2_3_2_4V (1u<<11) // 2.3-2.4V. +#define MMC_OCR_2_4_2_5V (1u<<12) // 2.4-2.5V. +#define MMC_OCR_2_5_2_6V (1u<<13) // 2.5-2.6V. +#define MMC_OCR_2_6_2_7V (1u<<14) // 2.6-2.7V. +#define MMC_OCR_2_7_2_8V (1u<<15) // 2.7-2.8V. +#define MMC_OCR_2_8_2_9V (1u<<16) // 2.8-2.9V. +#define MMC_OCR_2_9_3_0V (1u<<17) // 2.9-3.0V. +#define MMC_OCR_3_0_3_1V (1u<<18) // 3.0-3.1V. +#define MMC_OCR_3_1_3_2V (1u<<19) // 3.1-3.2V. +#define MMC_OCR_3_2_3_3V (1u<<20) // 3.2-3.3V. +#define MMC_OCR_3_3_3_4V (1u<<21) // 3.3-3.4V. +#define MMC_OCR_3_4_3_5V (1u<<22) // 3.4-3.5V. +#define MMC_OCR_3_5_3_6V (1u<<23) // 3.5-3.6V. +#define MMC_OCR_BYTE_MODE (0u<<29) // Access mode = byte mode. +#define MMC_OCR_SECT_MODE (2u<<29) // Access mode = sector mode. +#define MMC_OCR_READY (1u<<31) // Card power up status bit (busy). 0 = busy. + +// 7.6.1 Command sets and extended settings. +#define MMC_SWITCH_ACC_CMD_SET (0u) +#define MMC_SWITCH_ACC_SET_BITS (1u) +#define MMC_SWITCH_ACC_CLR_BITS (2u) +#define MMC_SWITCH_ACC_WR_BYTE (3u) +#define MMC_SWITCH_ARG(acc, idx, val, cmdSet) (((acc)&3u)<<24 | ((idx)&0xFFu)<<16 | ((val)&0xFFu)<<8 | ((cmdSet)&7u)) + +// 8.4 Extended CSD register. +// size in bytes, access, description. +#define EXT_CSD_SEC_BAD_BLK_MGMNT (134u) // 1, R/W, Bad Block Management mode. +#define EXT_CSD_ENH_START_ADDR (136u) // 4, R/W, Enhanced User Data Start Address. +#define EXT_CSD_ENH_SIZE_MULT (140u) // 3, R/W, Enhanced User Data Area Size. +#define EXT_CSD_GP_SIZE_MULT (143u) // 12, R/W, General Purpose Partition Size. +#define EXT_CSD_PARTITION_SETTING_COMPLETED (155u) // 1, R/W, Paritioning Setting. +#define EXT_CSD_PARTITIONS_ATTRIBUTE (156u) // 1, R/W, Partitions attribute. +#define EXT_CSD_MAX_ENH_SIZE_MULT (157u) // 3, R, Max Enhanced Area Size. +#define EXT_CSD_PARTITIONING_SUPPORT (160u) // 1, R, Partitioning Support. +#define EXT_CSD_HPI_MGMT (161u) // 1, R/W/E_P, HPI management. +#define EXT_CSD_RST_n_FUNCTION (162u) // 1, R/W, H/W reset function. +#define EXT_CSD_BKOPS_EN (163u) // 1, R/W, Enable background operations handshake. +#define EXT_CSD_BKOPS_START (164u) // 1, W/E_P, Manually start background operations. +#define EXT_CSD_WR_REL_PARAM (166u) // 1, R, Write reliability parameter register. +#define EXT_CSD_WR_REL_SET (167u) // 1, R/W, Write reliability setting register. +#define EXT_CSD_RPMB_SIZE_MULT (168u) // 1, R, RPMB Size. +#define EXT_CSD_FW_CONFIG (169u) // 1, R/W, FW configuration. +#define EXT_CSD_USER_WP (171u) // 1, R/W, R/W/C_P & R/W/E_P, User area write protection register. +#define EXT_CSD_BOOT_WP (173u) // 1, R/W & R/W/C_P, Boot area write protection register. +#define EXT_CSD_ERASE_GROUP_DEF (175u) // 1, R/W/E_P, High-density erase group definition. +#define EXT_CSD_BOOT_BUS_WIDTH (177u) // 1, R/W/E, Boot bus width1. +#define EXT_CSD_BOOT_CONFIG_PROT (178u) // 1, R/W & R/W/C_P, Boot config protection. +#define EXT_CSD_PARTITION_CONFIG (179u) // 1, R/W/E & R/W/E_P, Partition configuration. +#define EXT_CSD_ERASED_MEM_CONT (181u) // 1, R, Erased memory content. +#define EXT_CSD_BUS_WIDTH (183u) // 1, W/E_P, Bus width mode. +#define EXT_CSD_HS_TIMING (185u) // 1, R/W/E_P, High-speed interface timing. +#define EXT_CSD_POWER_CLASS (187u) // 1, R/W/E_P, Power class. +#define EXT_CSD_CMD_SET_REV (189u) // 1, R, Command set revision. +#define EXT_CSD_CMD_SET (191u) // 1, R/W/E_P, Command set. +#define EXT_CSD_EXT_CSD_REV (192u) // 1, R, Extended CSD revision. +#define EXT_CSD_CSD_STRUCTURE (194u) // 1, R, CSD structure version. +#define EXT_CSD_CARD_TYPE (196u) // 1, R, Card type. +#define EXT_CSD_OUT_OF_INTERRUPT_TIME (198u) // 1, R, Out-of-interrupt busy timing. +#define EXT_CSD_PARTITION_SWITCH_TIME (199u) // 1, R, Partition switching timing. +#define EXT_CSD_PWR_CL_52_195 (200u) // 1, R, Power class for 52MHz at 1.95V. +#define EXT_CSD_PWR_CL_26_195 (201u) // 1, R, Power class for 26MHz at 1.95V. +#define EXT_CSD_PWR_CL_52_360 (202u) // 1, R, Power class for 52MHz at 3.6V. +#define EXT_CSD_PWR_CL_26_360 (203u) // 1, R, Power class for 26MHz at 3.6V. +#define EXT_CSD_MIN_PERF_R_4_26 (205u) // 1, R, Minimum Read Performance for 4bit at 26MHz. +#define EXT_CSD_MIN_PERF_W_4_26 (206u) // 1, R, Minimum Write Performance for 4bit at 26MHz. +#define EXT_CSD_MIN_PERF_R_8_26_4_52 (207u) // 1, R, Minimum Read Performance for 8bit at 26MHz, for 4bit at 52MHz. +#define EXT_CSD_MIN_PERF_W_8_26_4_52 (208u) // 1, R, Minimum Write Performance for 8bit at 26MHz, for 4bit at 52MHz. +#define EXT_CSD_MIN_PERF_R_8_52 (209u) // 1, R, Minimum Read Performance for 8bit at 52MHz. +#define EXT_CSD_MIN_PERF_W_8_52 (210u) // 1, R, Minimum Write Performance for 8bit at 52MHz. +#define EXT_CSD_SEC_COUNT (212u) // 4, R, Sector Count. +#define EXT_CSD_S_A_TIMEOUT (217u) // 1, R, Sleep/awake timeout. +#define EXT_CSD_S_C_VCCQ (219u) // 1, R, Sleep current (VCCQ). +#define EXT_CSD_S_C_VCC (220u) // 1, R, Sleep current (VCC). +#define EXT_CSD_HC_WP_GRP_SIZE (221u) // 1, R, High-capacity write protect group size. +#define EXT_CSD_REL_WR_SEC_C (222u) // 1, R, Reliable write sector count. +#define EXT_CSD_ERASE_TIMEOUT_MULT (223u) // 1, R, High-capacity erase timeout. +#define EXT_CSD_HC_ERASE_GRP_SIZE (224u) // 1, R, High-capacity erase unit size. +#define EXT_CSD_ACC_SIZE (225u) // 1, R, Access size. +#define EXT_CSD_BOOT_SIZE_MULTI (226u) // 1, R, Boot partition size. +#define EXT_CSD_BOOT_INFO (228u) // 1, R, Boot information. +#define EXT_CSD_SEC_TRIM_MULT (229u) // 1, R, Secure TRIM Multiplier. +#define EXT_CSD_SEC_ERASE_MULT (230u) // 1, R, Secure Erase Multiplier. +#define EXT_CSD_SEC_FEATURE_SUPPORT (231u) // 1, R, Secure Feature support. +#define EXT_CSD_TRIM_MULT (232u) // 1, R, TRIM Multiplier. +#define EXT_CSD_MIN_PERF_DDR_R_8_52 (234u) // 1, R, Minimum Read Performance for 8bit at 52MHz in DDR mode. +#define EXT_CSD_MIN_PERF_DDR_W_8_52 (235u) // 1, R, Minimum Write Performance for 8bit at 52MHz in DDR mode. +#define EXT_CSD_PWR_CL_DDR_52_195 (238u) // 1, R, Power class for 52MHz, DDR at 1.95V. +#define EXT_CSD_PWR_CL_DDR_52_360 (239u) // 1, R, Power class for 52MHz, DDR at 3.6V. +#define EXT_CSD_INI_TIMEOUT_AP (241u) // 1, R, 1st initialization time after partitioning. +#define EXT_CSD_CORRECTLY_PRG_SECTORS_NUM (242u) // 4, R, Number of correctly programmed sectors. +#define EXT_CSD_BKOPS_STATUS (246u) // 1, R, Background operations status. +#define EXT_CSD_BKOPS_SUPPORT (502u) // 1, R, Background operations support. +#define EXT_CSD_HPI_FEATURES (503u) // 1, R, HPI features. +#define EXT_CSD_S_CMD_SET (504u) // 1, R, Supported Command Sets. diff --git a/arm7/source/mmc/sd_spec.h b/arm7/source/mmc/sd_spec.h new file mode 100644 index 0000000..808895a --- /dev/null +++ b/arm7/source/mmc/sd_spec.h @@ -0,0 +1,192 @@ +#pragma once + +// SPDX-License-Identifier: MIT +// Copyright (c) 2023 profi200 + +// Based on SD specification version 8.00. + +#include "tmio.h" + + +// Controller specific macros. Add controller specific bits here. +// SD_[command type]_[response type]_[transfer type] +// Command type: CMD = regular command, ACMD = Application-Specific Command. +// Transfer type: R = read, W = write. +#define SD_CMD_NONE(id) (CMD_RESP_NONE | (id)) +#define SD_CMD_R1(id) (CMD_RESP_R1 | (id)) +#define SD_CMD_R1b(id) (CMD_RESP_R1b | (id)) +#define SD_CMD_R2(id) (CMD_RESP_R2 | (id)) +#define SD_CMD_R6(id) (CMD_RESP_R6 | (id)) +#define SD_CMD_R7(id) (CMD_RESP_R7 | (id)) +#define SD_CMD_R1_R(id) (CMD_DATA_R | CMD_DATA_EN | CMD_RESP_R1 | (id)) +#define SD_CMD_R1_W(id) (CMD_DATA_W | CMD_DATA_EN | CMD_RESP_R1 | (id)) +#define SD_ACMD_R1(id) (CMD_RESP_R1 | CMD_ACMD | (id)) +#define SD_ACMD_R3(id) (CMD_RESP_R3 | CMD_ACMD | (id)) +#define SD_ACMD_R1_R(id) (CMD_DATA_R | CMD_DATA_EN | CMD_RESP_R1 | CMD_ACMD | (id)) + + +// Basic Commands (class 0). +#define SD_GO_IDLE_STATE SD_CMD_NONE(0u) // -, [31:0] stuff bits. +#define SD_ALL_SEND_CID SD_CMD_R2(2u) // R2, [31:0] stuff bits. +#define SD_SEND_RELATIVE_ADDR SD_CMD_R6(3u) // R6, [31:0] stuff bits. +#define SD_SET_DSR SD_CMD_NONE(4u) // -, [31:16] DSR [15:0] stuff bits. +#define SD_SELECT_CARD SD_CMD_R1b(7u) // R1b, [31:16] RCA [15:0] stuff bits. +#define SD_DESELECT_CARD SD_CMD_NONE(7u) // -, [31:16] RCA [15:0] stuff bits. +#define SD_SEND_IF_COND SD_CMD_R7(8u) // R7, [31:12] reserved bits [11:8] supply voltage (VHS) [7:0] check pattern. +#define SD_SEND_CSD SD_CMD_R2(9u) // R2, [31:16] RCA [15:0] stuff bits. +#define SD_SEND_CID SD_CMD_R2(10u) // R2, [31:16] RCA [15:0] stuff bits. +#define SD_VOLTAGE_SWITCH SD_CMD_R1(11u) // R1, [31:0] reserved bits (all 0). +#define SD_STOP_TRANSMISSION SD_CMD_R1b(12u) // R1b, [31:0] stuff bits. +#define SD_SEND_STATUS SD_CMD_R1(13u) // R1, [31:16] RCA [15] Send Task Status Register [14:0] stuff bits. +#define SD_SEND_TASK_STATUS SD_CMD_R1(13u) // R1, [31:16] RCA [15] Send Task Status Register [14:0] stuff bits. +#define SD_GO_INACTIVE_STATE SD_CMD_NONE(15u) // -, [31:16] RCA [15:0] reserved bits. + +// Block-Oriented Read Commands (class 2). +#define SD_SET_BLOCKLEN SD_CMD_R1(16u) // R1, [31:0] block length. +#define SD_READ_SINGLE_BLOCK SD_CMD_R1_R(17u) // R1, [31:0] data address. +#define SD_READ_MULTIPLE_BLOCK SD_CMD_R1_R(18u) // R1, [31:0] data address. +#define SD_SEND_TUNING_BLOCK SD_CMD_R1_R(19u) // R1, [31:0] reserved bits (all 0). +#define SD_SPEED_CLASS_CONTROL SD_CMD_R1b(20u) // R1b, [31:28] Speed Class Control [27:0] See command description. +#define SD_ADDRESS_EXTENSION SD_CMD_R1(22u) // R1, [31:6] reserved bits (all 0) [5:0] extended address. +#define SD_SET_BLOCK_COUNT SD_CMD_R1(23u) // R1, [31:0] Block Count. + +// Block-Oriented Write Commands (class 4). +// SET_BLOCKLEN +// SPEED_CLASS_CONTROL +// ADDRESS_EXTENSION +// SET_BLOCK_COUNT +#define SD_WRITE_BLOCK SD_CMD_R1_W(24u) // R1, [31:0] data address. +#define SD_WRITE_MULTIPLE_BLOCK SD_CMD_R1_W(25u) // R1, [31:0] data address. +#define SD_PROGRAM_CSD SD_CMD_R1_W(27u) // R1, [31:0] stuff bits. + +// Block Oriented Write Protection Commands (class 6). +#define SD_SET_WRITE_PROT SD_CMD_R1b(28u) // R1b, [31:0] data address. +#define SD_CLR_WRITE_PROT SD_CMD_R1b(29u) // R1b, [31:0] data address. +#define SD_SEND_WRITE_PROT SD_CMD_R1_R(30u) // R1, [31:0] write protect data address. + +// Erase Commands (class 5). +#define SD_ERASE_WR_BLK_START SD_CMD_R1(32u) // R1, [31:0] data address. +#define SD_ERASE_WR_BLK_END SD_CMD_R1(33u) // R1, [31:0] data address. +#define SD_ERASE SD_CMD_R1b(38u) // R1b, [31:0] Erase Function. + +// Lock Card (class 7). +// SET_BLOCKLEN +// Command 40 "Defined by DPS Spec.". +#define SD_LOCK_UNLOCK SD_CMD_R1_W(42u) // R1, [31:0] Reserved bits (Set all 0). + +// Application-Specific Commands (class 8). +#define SD_APP_CMD SD_CMD_R1(55u) // R1, [31:16] RCA [15:0] stuff bits. +#define SD_GEN_CMD_R SD_CMD_R1_R(56u) // R1, [31:1] stuff bits. [0]: RD/WR = 1. +#define SD_GEN_CMD_W SD_CMD_R1_W(56u) // R1, [31:1] stuff bits. [0]: RD/WR = 0. + +// Application Specific Commands used/reserved by SD Memory Card. +#define SD_APP_SET_BUS_WIDTH SD_ACMD_R1(6u) // R1, [31:2] stuff bits [1:0] bus width. +#define SD_APP_SD_STATUS SD_ACMD_R1_R(13u) // R1, [31:0] stuff bits. +#define SD_APP_SEND_NUM_WR_BLOCKS SD_ACMD_R1_R(22u) // R1, [31:0] stuff bits. +#define SD_APP_SET_WR_BLK_ERASE_COUNT SD_ACMD_R1(23u) // R1, [31:23] stuff bits [22:0] Number of blocks. +#define SD_APP_SD_SEND_OP_COND SD_ACMD_R3(41u) // R3, [31] reserved bit [30] HCS (OCR[30]) [29] reserved for eSD [28] XPC [27:25] reserved bits [24] S18R [23:0] VDD Voltage Window (OCR[23:0]). +#define SD_APP_SET_CLR_CARD_DETECT SD_ACMD_R1(42u) // R1, [31:1] stuff bits [0] set_cd. +#define SD_APP_SEND_SCR SD_ACMD_R1_R(51u) // R1, [31:0] stuff bits. + +// Switch Function Commands (class 10). +#define SD_SWITCH_FUNC SD_CMD_R1_R(6u) // R1, [31] Mode 0: Check function 1: Switch function [30:24] reserved (All '0') [23:20] reserved for function group 6 (0h or Fh) [19:16] reserved for function group 5 (0h or Fh) [15:12] function group 4 for PowerLimit [11:8] function group 3 for Drive Strength [7:4] function group 2 for Command System [3:0] function group 1 for Access Mode. + +// Function Extension Commands (class 11). +#define SD_READ_EXTR_SINGLE SD_CMD_R1_R(48u) // R1, [31] MIO0: Memory, 1: I/O [30:27] FNO[26] Reserved (=0) [25:9] ADDR [8:0] LEN. +#define SD_WRITE_EXTR_SINGLE SD_CMD_R1_W(49u) // R1, [31] MIO0: Memory, 1: I/O [30:27] FNO [26] MW [25:9] ADDR [8:0] LEN/MASK. +#define SD_READ_EXTR_MULTI SD_CMD_R1_R(58u) // R1, [31] MIO0: Memory, 1: I/O [30:27] FNO [26] BUS0: 512B, 1: 32KB [25:9] ADDR [8:0] BUC. +#define SD_WRITE_EXTR_MULTI SD_CMD_R1_W(59u) // R1, [31] MIO0: Memory, 1: I/O [30:27] FNO [26] BUS0: 512B, 1: 32KB [25:9] ADDR [8:0] BUC. + +// Command Queue Function Commands (class 1). +#define SD_Q_MANAGEMENT SD_CMD_R1b(43u) // R1b, [31:21] Reserved [20:16]: Task ID [3:0]: Operation Code (Abort tasks etc.). +#define SD_Q_TASK_INFO_A SD_CMD_R1(44u) // R1, [31] Reserved [30] Direction [29:24] Extended Address [23] Priority [22:21] Reserved [20:16] Task ID [15:0] Number of Blocks. +#define SD_Q_TASK_INFO_B SD_CMD_R1(45u) // R1, [31:0] Start block address. +#define SD_Q_RD_TASK SD_CMD_R1_R(46u) // R1, [31:21] Reserved [20:16] Task ID [15:0] Reserved. +#define SD_Q_WR_TASK SD_CMD_R1_W(47u) // R1, [31:21] Reserved [20:16] Task ID [15:0] Reserved. + + +// 4.10.1 Card Status. +// Type: +// E: Error bit. +// S: Status bit. +// R: Detected and set for the actual command response. +// X: Detected and set during command execution. The host can get the status by issuing a command with R1 response. +// +// Clear Condition: +// A: According to the card current status. +// B: Always related to the previous command. Reception of a valid command will clear it (with a delay of one command). +// C: Clear by read. +#define SD_R1_AKE_SEQ_ERROR (1u<<3) // E R C, Error in the sequence of the authentication process. +#define SD_R1_APP_CMD (1u<<5) // S R C, The card will expect ACMD, or an indication that the command has been interpreted as ACMD. +#define SD_R1_FX_EVENT (1u<<6) // S X A, ExtensionFunctions may set this bit to get host to deal with events. +#define SD_R1_READY_FOR_DATA (1u<<8) // S X A, Corresponds to buffer empty signaling on the bus. +#define SD_R1_STATE_IDLE (0u<<9) // S X B +#define SD_R1_STATE_READY (1u<<9) // S X B +#define SD_R1_STATE_IDENT (2u<<9) // S X B +#define SD_R1_STATE_STBY (3u<<9) // S X B +#define SD_R1_STATE_TRAN (4u<<9) // S X B +#define SD_R1_STATE_DATA (5u<<9) // S X B +#define SD_R1_STATE_RCV (6u<<9) // S X B +#define SD_R1_STATE_PRG (7u<<9) // S X B +#define SD_R1_STATE_DIS (8u<<9) // S X B +#define SD_R1_ERASE_RESET (1u<<13) // S R C, An erase sequence was cleared before executing because an out of erase sequence command was received. +#define SD_R1_CARD_ECC_DISABLED (1u<<14) // S X A, The command has been executed without using the internal ECC. +#define SD_R1_WP_ERASE_SKIP (1u<<15) // E R X C, Set when only partial address space was erased due to existing write protected blocks or the temporary or permanent write protected cardwas erased. +#define SD_R1_CSD_OVERWRITE (1u<<16) // E R X C, Can be either one of the following errors: -The read only section of the CSD does not match the card content. -An attempt to reverse the copy (set as original) or permanent WP (unprotected) bits was made. +// 17 reserved for DEFERRED_RESPONSE (Refer to eSD Addendum) +#define SD_R1_ERROR (1u<<19) // E R X C, A general or an unknown error occurred during the operation. +#define SD_R1_CC_ERROR (1u<<20) // E R X C, Internal card controller error: +#define SD_R1_CARD_ECC_FAILED (1u<<21) // E R X C, Card internal ECC was applied but failed to correct the data. +#define SD_R1_ILLEGAL_COMMAND (1u<<22) // E R B, Command not legal for the card state. +#define SD_R1_COM_CRC_ERROR (1u<<23) // E R B, The CRC check of the previous command failed. +#define SD_R1_LOCK_UNLOCK_FAILED (1u<<24) // E R X C, Set when a sequence or password error has been detected in lock/unlock card command. +#define SD_R1_CARD_IS_LOCKED (1u<<25) // S X A, When set, signals that the card is locked by the host. +#define SD_R1_WP_VIOLATION (1u<<26) // E R X C, Set when the host attempts to write to a protected block or to thetemporary or permanent write protected card. +#define SD_R1_ERASE_PARAM (1u<<27) // E R X C, An invalid selection of write-blocks for erase occurred. +#define SD_R1_ERASE_SEQ_ERROR (1u<<28) // E R C, An error in the sequence of erase commands occurred. +#define SD_R1_BLOCK_LEN_ERROR (1u<<29) // E R X C, The transferred block length is not allowed for this card, or the number of transferred bytes does not match the block length. +#define SD_R1_ADDRESS_ERROR (1u<<30) // E R X C, A misaligned address which did not match the block length was used in the command. +#define SD_R1_OUT_OF_RANGE (1u<<31) // E R X C, The command's argument was out of the allowed range for this card. + +#define SD_R1_ERR_ALL (SD_R1_OUT_OF_RANGE | SD_R1_ADDRESS_ERROR | SD_R1_BLOCK_LEN_ERROR | \ + SD_R1_ERASE_SEQ_ERROR | SD_R1_ERASE_PARAM | SD_R1_WP_VIOLATION | \ + SD_R1_LOCK_UNLOCK_FAILED | SD_R1_COM_CRC_ERROR | SD_R1_ILLEGAL_COMMAND | \ + SD_R1_CARD_ECC_FAILED | SD_R1_CC_ERROR | SD_R1_ERROR | \ + SD_R1_CSD_OVERWRITE | SD_R1_WP_ERASE_SKIP | SD_R1_AKE_SEQ_ERROR) + +// Argument bits for SEND_IF_COND (CMD8). +#define SD_CMD8_CHK_PATT (0xAAu) // Check pattern. +#define SD_CMD8_VHS_2_7_3_6V (1u<<8) // Voltage supplied (VHS) 2.7-3.6V. +#define SD_CMD8_PCIe (1u<<12) // PCIe Avail-ability. +#define SD_CMD8_PCIe_1_2V (1u<<13) // PCIe 1.2V Support. + +// 5.1 OCR register. +#define SD_OCR_2_7_2_8V (1u<<15) // 2.7-2.8V. +#define SD_OCR_2_8_2_9V (1u<<16) // 2.8-2.9V. +#define SD_OCR_2_9_3_0V (1u<<17) // 2.9-3.0V. +#define SD_OCR_3_0_3_1V (1u<<18) // 3.0-3.1V. +#define SD_OCR_3_1_3_2V (1u<<19) // 3.1-3.2V. +#define SD_OCR_3_2_3_3V (1u<<20) // 3.2-3.3V. +#define SD_OCR_3_3_3_4V (1u<<21) // 3.3-3.4V. +#define SD_OCR_3_4_3_5V (1u<<22) // 3.4-3.5V. +#define SD_OCR_3_5_3_6V (1u<<23) // 3.5-3.6V. +#define SD_OCR_S18A (1u<<24) // S18A: Switching to 1.8V Accepted. 0b: Continues current voltage signaling, 1b: Ready for switching signal voltage. +#define SD_OCR_CO2T (1u<<27) // Over 2TB Card. CCS must also be 1 if this is 1. +#define SD_OCR_UHS_II (1u<<29) // UHS-II Card Status. 0b: Non UHS-II Card, 1b: UHS-II Card. +#define SD_OCR_CCS (1u<<30) // Card Capacity Status. 0b: SDSC, 1b: SDHC or SDXC. +#define SD_OCR_READY (1u<<31) // Busy Status. 0b: On Initialization, 1b: Initialization Complete. + +// Argument bits for SEND_OP_COND (ACMD41). +// For voltage bits see OCR register above. +#define SD_ACMD41_S18R (1u<<24) // S18R: Switching to 1.8V Request. 0b: Use current signal voltage, 1b: Switch to 1.8V signal voltage. +#define SD_ACMD41_HO2T (1u<<27) // Over 2TB Supported Host. HCS must also be 1 if this is 1. +#define SD_ACMD41_XPC (1u<<28) // SDXC Power Control. 0b: Power Saving, 1b: Maximum Performance. +#define SD_ACMD41_HCS (1u<<30) // Host Capacity Support. 0b: SDSC Only Host, 1b: SDHC or SDXC Supported. + +// 4.3.10 Switch Function Command. +// mode: 0 = check function, 1 = set function +// pwr: Function group 4 Power Limit. +// driver: Function group 3 Driver Strength. +// cmd: Function group 2 Command system. +// acc: Function group 1 Access mode. +#define SD_SWITCH_FUNC_ARG(mode, pwr, driver, cmd, acc) ((mode)<<31 | 0xFFu<<16 | ((pwr)&0xFu)<<12 | ((driver)&0xFu)<<8 | ((cmd)&0xFu)<<4 | ((acc)&0xFu)) diff --git a/arm7/source/mmc/sdmmc.h b/arm7/source/mmc/sdmmc.h new file mode 100644 index 0000000..1eed04e --- /dev/null +++ b/arm7/source/mmc/sdmmc.h @@ -0,0 +1,240 @@ +#pragma once + +// SPDX-License-Identifier: MIT +// Copyright (c) 2023 profi200 + +// Possible error codes for most of the functions below. +enum +{ + SDMMC_ERR_NONE = 0u, // No error. + SDMMC_ERR_INVAL_PARAM = 1u, // Invalid parameter. + SDMMC_ERR_INITIALIZED = 2u, // The device is already initialized. + SDMMC_ERR_GO_IDLE_STATE = 3u, // GO_IDLE_STATE CMD error. + SDMMC_ERR_SEND_IF_COND = 4u, // SEND_IF_COND CMD error. + SDMMC_ERR_IF_COND_RESP = 5u, // IF_COND response pattern mismatch or unsupported voltage. + SDMMC_ERR_SEND_OP_COND = 6u, // SEND_OP_COND CMD error. + SDMMC_ERR_OP_COND_TMOUT = 7u, // Card initialization timeout. + SDMMC_ERR_VOLT_SUPPORT = 8u, // Voltage not supported. + SDMMC_ERR_ALL_SEND_CID = 9u, // ALL_SEND_CID CMD error. + SDMMC_ERR_SET_SEND_RCA = 10u, // SET/SEND_RELATIVE_ADDR CMD error. + SDMMC_ERR_SEND_CSD = 11u, // SEND_CSD CMD error. + SDMMC_ERR_SELECT_CARD = 12u, // SELECT_CARD CMD error. + SDMMC_ERR_LOCKED = 13u, // Card is locked with a password. + SDMMC_ERR_SEND_EXT_CSD = 14u, // SEND_EXT_CSD CMD error. + SDMMC_ERR_SWITCH_HS = 15u, // Error on switching to high speed mode. + SDMMC_ERR_SET_CLR_CD = 16u, // SET_CLR_CARD_DETECT CMD error. + SDMMC_ERR_SET_BUS_WIDTH = 17u, // Error on switching to a different bus width. + SDMMC_ERR_SEND_STATUS = 18u, // SEND_STATUS CMD error. + SDMMC_ERR_CARD_STATUS = 19u, // The card returned an error via its status. + SDMMC_ERR_NO_CARD = 20u, // Card unitialized or not inserted. + SDMMC_ERR_SECT_RW = 21u, // Sector read/write error. + SDMMC_ERR_WRITE_PROT = 22u, // The card is write protected. + SDMMC_ERR_SEND_CMD = 23u, // An error occured while sending a custom CMD via SDMMC_sendCommand(). + SDMMC_ERR_SET_BLOCKLEN = 24u, // SET_BLOCKLEN CMD error. + SDMMC_ERR_LOCK_UNLOCK = 25u, // LOCK_UNLOCK CMD error. + SDMMC_ERR_LOCK_UNLOCK_FAIL = 26u, // Lock/unlock operation failed (R1 status). + SDMMC_ERR_SLEEP_AWAKE = 27u // (e)MMC SLEEP_AWAKE CMD error. +}; + +// (e)MMC/SD device numbers. +enum +{ + SDMMC_DEV_CARD = 0u, // SD card/MMC. + SDMMC_DEV_eMMC = 1u, // Builtin eMMC. + + // Alias for internal use only. + SDMMC_MAX_DEV_NUM = SDMMC_DEV_eMMC +}; + +// Bit definition for SdmmcInfo.prot. +// Each bit 1 = protected. +#define SDMMC_PROT_SLIDER (1u) // SD card write protection slider. +#define SDMMC_PROT_TEMP (1u<<1) // Temporary write protection (CSD). +#define SDMMC_PROT_PERM (1u<<2) // Permanent write protection (CSD). +#define SDMMC_PROT_PASSWORD (1u<<3) // (e)MMC/SD card is password protected. + +typedef struct +{ + u8 type; // 0 = none, 1 = (e)MMC, 2 = High capacity (e)MMC, 3 = SDSC, 4 = SDHC/SDXC, 5 = SDUC. + u8 prot; // See SDMMC_PROT_... defines above for details. + u16 rca; // Relative Card Address (RCA). + u32 sectors; // Size in 512 byte units. + u32 clock; // The current clock frequency in Hz. + u32 cid[4]; // Raw CID without the CRC. + u16 ccc; // (e)MMC/SD command class support from CSD. One per bit starting at 0. + u8 busWidth; // The current bus width used to talk to the card. +} SdmmcInfo; + +typedef struct +{ + u16 cmd; // Command. T̲h̲e̲ ̲f̲o̲r̲m̲a̲t̲ ̲i̲s̲ ̲c̲o̲n̲t̲r̲o̲l̲l̲e̲r̲ ̲s̲p̲e̲c̲i̲f̲i̲c̲!̲ + u32 arg; // Command argument. + u32 resp[4]; // Card response. Length depends on command. + u32 *buf; // In/out data buffer. + u16 blkLen; // Block length. Usually 512. + u16 count; // Number of blkSize blocks to transfer. +} MmcCommand; + +// Mode bits for SDMMC_lockUnlock(). +#define SDMMC_LK_CLR_PWD (1u<<1) // Clear password. +#define SDMMC_LK_UNLOCK (0u) // Unlock. +#define SDMMC_LK_LOCK (1u<<2) // Lock. +#define SDMMC_LK_ERASE (1u<<3) // Force erase a locked (e)MMC/SD card. +#define SDMMC_LK_COP (1u<<4) // SD cards only. Card Ownership Protection operation. + +#ifdef __cplusplus +extern "C"{ +#endif +/** + * @brief Initializes a (e)MMC/SD card device. + * + * @param[in] devNum The device to initialize. + * + * @return Returns SDMMC_ERR_NONE on success or + * one of the errors listed above on failure. + */ +u32 pico_SDMMC_init(const u8 devNum); + +/** + * @brief Switches a (e)MMC/SD card device between sleep/awake mode. + * Note that SD cards don't have a true sleep mode. + * + * @param[in] devNum The device. + * @param[in] enabled The mode. true to enable sleep and false to wake up. + * + * @return Returns SDMMC_ERR_NONE on success or + * one of the errors listed above on failure. + */ +u32 pico_SDMMC_setSleepMode(const u8 devNum, const bool enabled); + +/** + * @brief Deinitializes a (e)MMC/SD card device. + * + * @param[in] devNum The device to deinitialize. + * + * @return Returns SDMMC_ERR_NONE on success or SDMMC_ERR_INVAL_PARAM on failure. + */ +u32 pico_SDMMC_deinit(const u8 devNum); + +/** + * @brief Manage password protection for a (e)MMC/SD card device. + * + * @param[in] devNum The device. + * @param[in] mode The mode of operation. See defines above. + * @param[in] pwd The password buffer pointer. + * @param[in] pwdLen The password length. Maximum 32 for password replace. Otherwise 16. + * + * @return Returns SDMMC_ERR_NONE on success or + * one of the errors listed above on failure. + */ +u32 pico_SDMMC_lockUnlock(const u8 devNum, const u8 mode, const u8 *const pwd, const u8 pwdLen); + +/** + * @brief Exports the internal device state for fast init (bootloaders ect.). + * + * @param[in] devNum The device state to export. + * @param devOut A pointer to a u8[60] array. + * + * @return Returns SDMMC_ERR_NONE on success or SDMMC_ERR_INVAL_PARAM/SDMMC_ERR_NO_CARD on failure. + */ +u32 pico_SDMMC_exportDevState(const u8 devNum, u8 devOut[64]); + +/** + * @brief Imports a device state for fast init (bootloaders ect.). + * The state should be validated for example with a checksum. + * + * @param[in] devNum The device state to import. + * @param[in] devIn A pointer to a u8[60] array. + * + * @return Returns SDMMC_ERR_NONE on success or + * SDMMC_ERR_INVAL_PARAM/SDMMC_ERR_NO_CARD/SDMMC_ERR_INITIALIZED on failure. + */ +u32 pico_SDMMC_importDevState(const u8 devNum, const u8 devIn[64]); + +/** + * @brief Outputs infos about a (e)MMC/SD card device. + * + * @param[in] devNum The device. + * @param infoOut A pointer to a SdmmcInfo struct. + * + * @return Returns SDMMC_ERR_NONE on success or SDMMC_ERR_INVAL_PARAM on failure. + */ +u32 pico_SDMMC_getDevInfo(const u8 devNum, SdmmcInfo *const infoOut); + +/** + * @brief Outputs the CID of a (e)MMC/SD card device. + * + * @param[in] devNum The device. + * @param cidOut A u32[4] pointer for storing the CID. + * + * @return Returns SDMMC_ERR_NONE on success or SDMMC_ERR_INVAL_PARAM on failure. + */ +u32 pico_SDMMC_getCid(const u8 devNum, u32 cidOut[4]); + +/** + * @brief Returns the DSTATUS bits of a (e)MMC/SD card device. See FatFs diskio.h. + * + * @param[in] devNum The device. + * + * @return Returns the DSTATUS bits or STA_NODISK | STA_NOINIT on failure. + */ +//u8 SDMMC_getDiskStatus(const u8 devNum); + +/** + * @brief Outputs the number of sectors for a (e)MMC/SD card device. + * + * @param[in] devNum The device. + * + * @return Returns the number of sectors or 0 on failure. + */ +u32 pico_SDMMC_getSectors(const u8 devNum); + +/** + * @brief Reads one or more sectors from a (e)MMC/SD card device. + * + * @param[in] devNum The device. + * @param[in] sect The start sector. + * @param buf The output buffer pointer. NULL for DMA. + * @param[in] count The number of sectors to read. + * + * @return Returns SDMMC_ERR_NONE on success or + * one of the errors listed above on failure. + */ +u32 pico_SDMMC_readSectors(const u8 devNum, u32 sect, void *const buf, const u16 count); + +/** + * @brief Writes one or more sectors to a (e)MMC/SD card device. + * + * @param[in] devNum The device. + * @param[in] sect The start sector. + * @param[in] buf The input buffer pointer. NULL for DMA. + * @param[in] count The count + * + * @return Returns SDMMC_ERR_NONE on success or + * one of the errors listed above on failure. + */ +u32 pico_SDMMC_writeSectors(const u8 devNum, u32 sect, const void *const buf, const u16 count); + +/** + * @brief Sends a custom command to a (e)MMC/SD card device. + * + * @param[in] devNum The device. + * @param cmd MMC command struct pointer (see above). + * + * @return Returns SDMMC_ERR_NONE on success or SDMMC_ERR_SEND_CMD on failure. + */ +u32 pico_SDMMC_sendCommand(const u8 devNum, MmcCommand *const mmcCmd); + +/** + * @brief Returns the R1 card status for a previously failed read/write/custom command. + * + * @param[in] devNum The device. + * + * @return Returns the R1 card status or 0 if there was either no command error or invalid devNum. + */ +u32 pico_SDMMC_getLastR1error(const u8 devNum); + +// TODO: TRIM/erase support. +#ifdef __cplusplus +} +#endif diff --git a/arm7/source/mmc/sdmmc.twl.c b/arm7/source/mmc/sdmmc.twl.c new file mode 100644 index 0000000..82482bf --- /dev/null +++ b/arm7/source/mmc/sdmmc.twl.c @@ -0,0 +1,751 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2023 profi200 + +#include +#include +#include +#include "mmc/sdmmc.h" // Includes types.h. +#include "tmio.h" +#include "mmc/mmc_spec.h" +#include "mmc/sd_spec.h" + + +// Note on INIT_CLOCK: +// 400 kHz is allowed by the specs. 523 kHz has been proven to work reliably +// for SD cards and eMMC but very early MMCs can fail at init. +// We lose about 5 ms of time on init by using 261 kHz. +#define INIT_CLOCK (400000u) // Maximum 400 kHz. +#define DEFAULT_CLOCK (20000000u) // Maximum 20 MHz. +#define HS_CLOCK (50000000u) // Maximum 50 MHz. + +// ARM7 timer clock = controller clock = CPU clock. +// swiDelay() doesn't seem to be cycle accurate meaning +// one cycle is 4 (?) CPU cycles. +#define SLEEP_MS_FUNC(ms) swi_waitByLoop(8378 * (ms)) + + +#define MMC_OCR_VOLT_MASK (MMC_OCR_3_2_3_3V) // We support 3.3V only. +#define SD_OCR_VOLT_MASK (SD_OCR_3_2_3_3V) // We support 3.3V only. +#define SD_IF_COND_ARG (SD_CMD8_VHS_2_7_3_6V | SD_CMD8_CHK_PATT) +#define SD_OP_COND_ARG (SD_OCR_VOLT_MASK) // We support 100 mA and 3.3V. Without HCS bit. +#define MMC_OP_COND_ARG (MMC_OCR_SECT_MODE | MMC_OCR_VOLT_MASK) // We support sector addressing and 3.3V. + +// Note: DEV_TYPE_NONE must be zero. +enum +{ + // Device types. + DEV_TYPE_NONE = 0u, // Unitialized/no device. + DEV_TYPE_MMC = 1u, // (e)MMC. + DEV_TYPE_MMCHC = 2u, // High capacity (e)MMC (>2 GB). + DEV_TYPE_SDSC = 3u, // SDSC. + DEV_TYPE_SDHC = 4u, // SDHC, SDXC. + DEV_TYPE_SDUC = 5u // SDUC. +}; + +#define IS_DEV_MMC(dev) ((dev) < DEV_TYPE_SDSC) + + +typedef struct +{ + TmioPort port; + u8 type; // Device type. 0 = none, 1 = (e)MMC, 2 = High capacity (e)MMC, + // 3 = SDSC, 4 = SDHC/SDXC, 5 = SDUC. + u8 prot; // Protection bits. Each bit 1 = protected. + // Bit 0 SD card slider, bit 1 temporary write protection (CSD), + // bit 2 permanent write protection (CSD) and bit 3 password protection. + u16 rca; // Relative Card Address (RCA). + u16 ccc; // (e)MMC/SD command class support from CSD. One per bit starting at 0. + u32 sectors; // Size in 512 byte units. + u32 status; // R1 card status on error. Only updated on errors. + + // Cached card infos. + u32 cid[4]; // Raw CID without the CRC. +} SdmmcDev; + +static SdmmcDev g_devs[2] = {0}; + + + +static u32 sendAppCmd(TmioPort *const port, const u16 cmd, const u32 arg, const u32 rca) +{ + // Send app CMD. Same CMD for (e)MMC/SD. + // TODO: Check the APP_CMD bit in the response? + // Linux does it but is it really necessary? SD spec 4.3.9.1. + u32 res = TMIO_sendCommand(port, MMC_APP_CMD, rca); + if(res == 0) + { + res = TMIO_sendCommand(port, cmd, arg); + } + + return res; +} + +static u32 goIdleState(TmioPort *const port) +{ + // Enter idle state before we start the init procedure. + // Works from all but inactive state. CMD is the same for (e)MMC/SD. + // For (e)MMC there are optional init paths: + // arg = 0x00000000 -> GO_IDLE_STATE. + // arg = 0xF0F0F0F0 -> GO_PRE_IDLE_STATE. + // arg = 0xFFFFFFFA -> BOOT_INITIATION. + u32 res = TMIO_sendCommand(port, MMC_GO_IDLE_STATE, 0); + if(res != 0) return SDMMC_ERR_GO_IDLE_STATE; + + return SDMMC_ERR_NONE; +} + +static u32 initIdleState(TmioPort *const port, u8 *const devTypeOut) +{ + // Tell the card what interfaces and voltages we support. + // Only SD v2 and up will respond. (e)MMC won't respond. + u32 res = TMIO_sendCommand(port, SD_SEND_IF_COND, SD_IF_COND_ARG); + if(res == 0) + { + // If the card supports the interfaces and voltages + // it should echo back the check pattern and set the + // support bits. + // Since we don't support anything but the + // standard SD interface at 3.3V we can check + // the whole response at once. + if(port->resp[0] != SD_IF_COND_ARG) return SDMMC_ERR_IF_COND_RESP; + } + else if(res != STATUS_ERR_CMD_TIMEOUT) return SDMMC_ERR_SEND_IF_COND; // Card responded but an error occured. + + // Send the first app CMD. If this times out it's (e)MMC. + // If SEND_IF_COND timed out tell the SD card we are a v1 host. + const u32 opCondArg = SD_OP_COND_ARG | (res<<8 ^ SD_ACMD41_HCS); // Caution! Controller specific hack. + u8 devType = DEV_TYPE_SDSC; + res = sendAppCmd(port, SD_APP_SD_SEND_OP_COND, opCondArg, 0); + if(res == STATUS_ERR_CMD_TIMEOUT) devType = DEV_TYPE_MMC; // Continue with (e)MMC init. + else if(res != 0) return SDMMC_ERR_SEND_OP_COND; // Unknown error. + + if(devType == DEV_TYPE_MMC) // (e)MMC. + { + // Loop until a timeout of 1 second or the card is ready. + u32 tries = 200; + u32 ocr; + while(1) + { + res = TMIO_sendCommand(port, MMC_SEND_OP_COND, MMC_OP_COND_ARG); + if(res != 0) return SDMMC_ERR_SEND_OP_COND; + + ocr = port->resp[0]; + if(!--tries || (ocr & MMC_OCR_READY)) break; + + // Linux uses 10 ms but the card doesn't become ready faster + // when polling with delay. Use 5 ms as compromise so not much + // time is wasted when the card becomes ready in the middle of the delay. + SLEEP_MS_FUNC(5); + } + + // (e)MMC didn't finish init within 1 second. + if(tries == 0) return SDMMC_ERR_OP_COND_TMOUT; + + // Check if the (e)MMC supports the voltage and if it's high capacity. + if(!(ocr & MMC_OCR_VOLT_MASK)) return SDMMC_ERR_VOLT_SUPPORT; // Voltage not supported. + if(ocr & MMC_OCR_SECT_MODE) devType = DEV_TYPE_MMCHC; // 7.4.3. + } + else // SD card. + { + // Loop until a timeout of 1 second or the card is ready. + u32 tries = 200; + u32 ocr; + while(1) + { + ocr = port->resp[0]; + if(!--tries || (ocr & SD_OCR_READY)) break; + + // Linux uses 10 ms but the card doesn't become ready faster + // when polling with delay. Use 5 ms as compromise so not much + // time is wasted when the card becomes ready in the middle of the delay. + SLEEP_MS_FUNC(5); + + res = sendAppCmd(port, SD_APP_SD_SEND_OP_COND, opCondArg, 0); + if(res != 0) return SDMMC_ERR_SEND_OP_COND; + } + + // SD card didn't finish init within 1 second. + if(tries == 0) return SDMMC_ERR_OP_COND_TMOUT; + + if(!(ocr & SD_OCR_VOLT_MASK)) return SDMMC_ERR_VOLT_SUPPORT; // Voltage not supported. + if(ocr & SD_OCR_CCS) devType = DEV_TYPE_SDHC; + } + + *devTypeOut = devType; + + return SDMMC_ERR_NONE; +} + +static u32 initReadyState(SdmmcDev *const dev) +{ + TmioPort *const port = &dev->port; + + // SD card voltage switch sequence goes here if supported. + + // Get the CID. CMD is the same for (e)MMC/SD. + u32 res = TMIO_sendCommand(port, MMC_ALL_SEND_CID, 0); + if(res != 0) return SDMMC_ERR_ALL_SEND_CID; + memcpy(dev->cid, port->resp, 16); + + return SDMMC_ERR_NONE; +} + +static u32 initIdentState(SdmmcDev *const dev, const u8 devType, u32 *const rcaOut) +{ + TmioPort *const port = &dev->port; + + u32 rca; + if(IS_DEV_MMC(devType)) // (e)MMC. + { + // Set the RCA of the (e)MMC to 1. 0 is reserved. + // The RCA is in the upper 16 bits of the argument. + rca = 1; + u32 res = TMIO_sendCommand(port, MMC_SET_RELATIVE_ADDR, rca<<16); + if(res != 0) return SDMMC_ERR_SET_SEND_RCA; + } + else // SD card. + { + // Ask the SD card to send its RCA. + u32 res = TMIO_sendCommand(port, SD_SEND_RELATIVE_ADDR, 0); + if(res != 0) return SDMMC_ERR_SET_SEND_RCA; + + // RCA in upper 16 bits. Discards lower status bits of R6 response. + rca = port->resp[0]>>16; + } + + dev->rca = rca; + *rcaOut = rca<<16; + + return SDMMC_ERR_NONE; +} + +// Based on UNSTUFF_BITS from linux/drivers/mmc/core/sd.c. +// Extracts up to 32 bits from a u32[4] array. +static inline u32 extractBits(const u32 resp[4], const u32 start, const u32 size) +{ + const u32 mask = (size < 32 ? 1u<>shift; + if(size + shift > 32) + res |= resp[off - 1]<<((32u - shift) & 31u); + + return res & mask; +} + +static void parseCsd(SdmmcDev *const dev, const u8 devType, u8 *const spec_vers_out) +{ + // Note: The MSBs are in csd[0]. + const u32 *const csd = dev->port.resp; + + const u8 structure = extractBits(csd, 126, 2); // [127:126] + *spec_vers_out = extractBits(csd, 122, 4); // [125:122] All 0 for SD cards. + dev->ccc = extractBits(csd, 84, 12); // [95:84] + u32 sectors = 0; + if(structure == 0 || devType == DEV_TYPE_MMC) // structure = 0 is CSD version 1.0. + { + const u32 read_bl_len = extractBits(csd, 80, 4); // [83:80] + const u32 c_size = extractBits(csd, 62, 12); // [73:62] + const u32 c_size_mult = extractBits(csd, 47, 3); // [49:47] + + // For SD cards with CSD 1.0 and <=2 GB (e)MMC this calculation is used. + // Note: READ_BL_LEN is at least 9. + // Modified/simplified to calculate sectors instead of bytes. + sectors = (c_size + 1)<<(c_size_mult + 2 + read_bl_len - 9); + } + else if(devType != DEV_TYPE_MMCHC) + { + // SD CSD version 3.0 format. + // For version 2.0 this is 22 bits however the upper bits + // are reserved and zero filled so this is fine. + const u32 c_size = extractBits(csd, 48, 28); // [75:48] + + // Calculation for SD cards with CSD >1.0. + sectors = (c_size + 1)<<10; + } + // Else for high capacity (e)MMC the sectors will be read later from EXT_CSD. + dev->sectors = sectors; + + // Parse temporary and permanent write protection bits. + u8 prot = extractBits(csd, 12, 1)<<1; // [12:12] Not checked by Linux. + prot |= extractBits(csd, 13, 1)<<2; // [13:13] + dev->prot |= prot; +} + +static u32 initStandbyState(SdmmcDev *const dev, const u8 devType, const u32 rca, u8 *const spec_vers_out) +{ + TmioPort *const port = &dev->port; + + // Get the CSD. CMD is the same for (e)MMC/SD. + u32 res = TMIO_sendCommand(port, MMC_SEND_CSD, rca); + if(res != 0) return SDMMC_ERR_SEND_CSD; + parseCsd(dev, devType, spec_vers_out); + + // CMD is the same for (e)MMC/SD however both R1 and R1b responses are used. + // We assume R1b and hope it doesn't time out. + res = TMIO_sendCommand(port, MMC_SELECT_CARD, rca); + if(res != 0) return SDMMC_ERR_SELECT_CARD; + + // The SD card spec mentions that we should check the lock bit in the + // response to CMD7 to identify cards requiring a password to unlock. + // Same seems to apply for (e)MMC. + // Same bit for (e)MMC/SD R1 card status. + dev->prot |= (port->resp[0] & MMC_R1_CARD_IS_LOCKED)>>22; // Bit 3. + + return SDMMC_ERR_NONE; +} + +// TODO: Set the timeout based on clock speed (Tmio uses SDCLK for timeouts). +// The tmio driver sets a sane default but we should calculate it anyway. +static u32 initTranState(SdmmcDev *const dev, const u8 devType, const u32 rca, const u8 spec_vers) +{ + TmioPort *const port = &dev->port; + + if(IS_DEV_MMC(devType)) // (e)MMC. + { + // EXT_CSD, non-1 bit bus width and HS timing are only + // supported by (e)MMC SPEC_VERS 4.1 and higher. + if(spec_vers > 3) // Version 4.1–4.2–4.3 or higher. + { + // The (e)MMC spec says to check the card status after a SWITCH CMD (7.6.1). + // I think we can get away without checking this because support for HS timing + // and 4 bit bus width is mandatory for this spec version. If the card is + // non-standard we will encounter errors on the next CMD anyway. + // Switch to 4 bit bus mode. + const u32 busWidthArg = MMC_SWITCH_ARG(MMC_SWITCH_ACC_WR_BYTE, EXT_CSD_BUS_WIDTH, 1, 0); + u32 res = TMIO_sendCommand(port, MMC_SWITCH, busWidthArg); + if(res != 0) return SDMMC_ERR_SET_BUS_WIDTH; + TMIO_setBusWidth(port, 4); + + // We should also check in the EXT_CSD the power budget for the card. + // Nintendo seems to leave it on default (no change). + + if(devType == DEV_TYPE_MMCHC) + { + // Note: The EXT_CSD is normally read before touching HS timing and bus width. + // We can take advantage of the faster data transfer with this order. + alignas(4) u8 ext_csd[512]; + TMIO_setBuffer(port, (u32*)ext_csd, 1); + res = TMIO_sendCommand(port, MMC_SEND_EXT_CSD, 0); + if(res != 0) return SDMMC_ERR_SEND_EXT_CSD; + + // Get sector count from EXT_CSD only if sector addressing is used because + // byte addressed (e)MMC may set sector count to 0. + dev->sectors = ext_csd[EXT_CSD_SEC_COUNT + 3]<<24 | ext_csd[EXT_CSD_SEC_COUNT + 2]<<16 | + ext_csd[EXT_CSD_SEC_COUNT + 1]<<8 | ext_csd[EXT_CSD_SEC_COUNT + 0]; + } + } + } + else // SD card. + { + // Remove DAT3 pull-up. Linux doesn't do it but the SD spec recommends it. + u32 res = sendAppCmd(port, SD_APP_SET_CLR_CARD_DETECT, 0, rca); // arg = 0 removes the pull-up. + if(res != 0) return SDMMC_ERR_SET_CLR_CD; + + // Switch to 4 bit bus mode. + res = sendAppCmd(port, SD_APP_SET_BUS_WIDTH, 2, rca); // arg = 2 is 4 bit bus width. + if(res != 0) return SDMMC_ERR_SET_BUS_WIDTH; + TMIO_setBusWidth(port, 4); + } + + // SD: The description for CMD SET_BLOCKLEN says 512 bytes is the default. + // (e)MMC: The description for READ_BL_LEN (CSD) says 512 bytes is the default. + // So it's not required to set the block length. + + return SDMMC_ERR_NONE; +} + +u32 pico_SDMMC_init(const u8 devNum) +{ + if(devNum > SDMMC_MAX_DEV_NUM) return SDMMC_ERR_INVAL_PARAM; + + SdmmcDev *const dev = &g_devs[devNum]; + if(dev->type != DEV_TYPE_NONE) return SDMMC_ERR_INITIALIZED; + + // Check SD card write protection slider. + if(devNum == SDMMC_DEV_CARD) + dev->prot = !TMIO_cardWritable(); + + // Init port, enable clock output and wait 74 clocks. + TmioPort *const port = &dev->port; + TMIO_initPort(port, devNum); + TMIO_powerupSequence(port); // Setup continuous clock and wait 74 clocks. + + u32 res = goIdleState(port); + if(res != SDMMC_ERR_NONE) return res; + + // (e)MMC/SD now in idle state (idle). + u8 devType; + res = initIdleState(port, &devType); + if(res != SDMMC_ERR_NONE) return res; + + // Stop clock at idle, init clock. + TMIO_setClock(port, INIT_CLOCK); + + // (e)MMC/SD now in ready state (ready). + res = initReadyState(dev); + if(res != SDMMC_ERR_NONE) return res; + + // (e)MMC/SD now in identification state (ident). + u32 rca; + res = initIdentState(dev, devType, &rca); + if(res != SDMMC_ERR_NONE) return res; + + // (e)MMC/SD now in stand-by state (stby). + // Maximum at this point would be 20 MHz for (e)MMC and 25 for SD. + // SD: We can increase the clock after end of identification state. + // TODO: eMMC spec section 7.6 + // "Until the contents of the CSD register is known by the host, + // the fPP clock rate must remain at fOD. (See Section 12.7 on page 176.)" + // Since the absolute minimum clock rate is 20 MHz and we are in push-pull + // mode already can we cheat and switch to <=20 MHz before getting the CSD? + // Note: This seems to be working just fine in all tests. + TMIO_setClock(port, DEFAULT_CLOCK); + + u8 spec_vers; + res = initStandbyState(dev, devType, rca, &spec_vers); + if(res != SDMMC_ERR_NONE) return res; + + // (e)MMC/SD now in transfer state (tran). + res = initTranState(dev, devType, rca, spec_vers); + if(res != SDMMC_ERR_NONE) return res; + + // Only set dev type on successful init. + dev->type = devType; + + return SDMMC_ERR_NONE; +} + +u32 pico_SDMMC_setSleepMode(const u8 devNum, const bool enabled) +{ + if(devNum > SDMMC_MAX_DEV_NUM) return SDMMC_ERR_INVAL_PARAM; + + SdmmcDev *const dev = &g_devs[devNum]; + TmioPort *const port = &dev->port; + const u32 rca = (u32)dev->rca<<16; + const u8 devType = dev->type; + if(enabled) + { + // Deselect card to go back to stand-by state. + // CMD is the same for (e)MMC/SD. + u32 res = TMIO_sendCommand(port, MMC_DESELECT_CARD, 0); + if(res != 0) return SDMMC_ERR_SELECT_CARD; + + // Only (e)MMC can go into true sleep mode. + if(IS_DEV_MMC(devType)) + { + // Switch (e)MMC into sleep mode. + res = TMIO_sendCommand(port, MMC_SLEEP_AWAKE, rca | 1u<<15); + if(res != 0) return SDMMC_ERR_SLEEP_AWAKE; + // TODO: Power down eMMC. This is confirmed working on 3DS. + } + } + else + { + if(IS_DEV_MMC(devType)) + { + // TODO: Power up eMMC. This is confirmed working on 3DS. + // Wake (e)MMC up from sleep mode. + u32 res = TMIO_sendCommand(port, MMC_SLEEP_AWAKE, rca); + if(res != 0) return SDMMC_ERR_SLEEP_AWAKE; + } + + // Select card to go back to transfer state. + // CMD is the same for (e)MMC/SD. + u32 res = TMIO_sendCommand(port, MMC_SELECT_CARD, rca); + if(res != 0) return SDMMC_ERR_SELECT_CARD; + } + + return SDMMC_ERR_NONE; +} + +// TODO: Is there any "best practice" way of deinitializing cards? +// Kick the card back into idle state maybe? +// Linux seems to deselect cards on "suspend". +u32 pico_SDMMC_deinit(const u8 devNum) +{ + if(devNum > SDMMC_MAX_DEV_NUM) return SDMMC_ERR_INVAL_PARAM; + + memset(&g_devs[devNum], 0, sizeof(SdmmcDev)); + + return SDMMC_ERR_NONE; +} + +u32 pico_SDMMC_lockUnlock(const u8 devNum, const u8 mode, const u8 *const pwd, const u8 pwdLen) +{ + // Password length is maximum 16 bytes except when replacing a password. + if(devNum > SDMMC_MAX_DEV_NUM || pwdLen > 32) return SDMMC_ERR_INVAL_PARAM; + + // Set block length on (e)MMC/SD side and host. + // Same CMD for (e)MMC/SD. + SdmmcDev *const dev = &g_devs[devNum]; + TmioPort *const port = &dev->port; + const u32 blockLen = (mode != SDMMC_LK_ERASE ? 2 + pwdLen : 1); + u32 res = TMIO_sendCommand(port, MMC_SET_BLOCKLEN, blockLen); + if(res != 0) return SDMMC_ERR_SET_BLOCKLEN; + TMIO_setBlockLen(port, blockLen); + + do + { + // Prepare lock/unlock data block. + alignas(4) u8 buf[36] = {0}; // Size multiple of 4 (TMIO driver limitation). + buf[0] = mode; + buf[1] = pwdLen; + memcpy(&buf[2], pwd, pwdLen); + + // Dirty hack to extend the data timeout to a bit over 4 minutes with TMIO controller. + // We need 3 minutes minimum for erase. + const u16 clk_ctrl_backup = port->sd_clk_ctrl; + TMIO_setClock(port, 130913); + + // Note: Command class 7 support is mandatory for (e)MMC. Not for SD cards until 2.00. + // Same CMD for (e)MMC/SD. + TMIO_setBuffer(port, (u32*)buf, 1); + res = TMIO_sendCommand(port, MMC_LOCK_UNLOCK, 0); + port->sd_clk_ctrl = clk_ctrl_backup; // Undo the data timeout hack. + if(res != 0) + { + res = SDMMC_ERR_LOCK_UNLOCK; + break; + } + + // Restore default block length and get the R1 status. + // Same CMD for (e)MMC/SD. + res = TMIO_sendCommand(port, MMC_SET_BLOCKLEN, 512); + if(res != 0) + { + res = SDMMC_ERR_SET_BLOCKLEN; + break; + } + TMIO_setBlockLen(port, 512); + + // Check if lock/unlock worked. + // Same bit for (e)MMC/SD R1 card status. + const u32 status = port->resp[0]; + if(status & MMC_R1_LOCK_UNLOCK_FAILED) + res = SDMMC_ERR_LOCK_UNLOCK_FAIL; + + // Update lock status. + const u8 prot = dev->prot & ~(1u<<3); + dev->prot = prot | (status>>22 & 1u<<3); + } while(0); + + return res; +} + +// People should not mess with the state which is the reason +// why the struct is not exposed directly. +static_assert(sizeof(SdmmcDev) == 64, "Wrong SDMMC dev export/import size."); +u32 pico_SDMMC_exportDevState(const u8 devNum, u8 devOut[64]) +{ + if(devNum > SDMMC_MAX_DEV_NUM) return SDMMC_ERR_INVAL_PARAM; + + // Check if the device is initialized. + const SdmmcDev *const dev = &g_devs[devNum]; + if(dev->type == DEV_TYPE_NONE) return SDMMC_ERR_NO_CARD; + + memcpy(devOut, dev, 64); + + return SDMMC_ERR_NONE; +} + +u32 pico_SDMMC_importDevState(const u8 devNum, const u8 devIn[64]) +{ + if(devNum > SDMMC_MAX_DEV_NUM) return SDMMC_ERR_INVAL_PARAM; + + // Make sure there is a card inserted. + if(devNum == SDMMC_DEV_CARD && !TMIO_cardDetected()) return SDMMC_ERR_NO_CARD; + + // Check if the device is initialized. + SdmmcDev *const dev = &g_devs[devNum]; + if(dev->type != DEV_TYPE_NONE) return SDMMC_ERR_INITIALIZED; + + memcpy(dev, devIn, 64); + + // Update write protection slider state just in case. + dev->prot |= !TMIO_cardWritable(); + + return SDMMC_ERR_NONE; +} + +// TODO: Less controller dependent code. +u32 pico_SDMMC_getDevInfo(const u8 devNum, SdmmcInfo *const infoOut) +{ + if(devNum > SDMMC_MAX_DEV_NUM) return SDMMC_ERR_INVAL_PARAM; + + const SdmmcDev *const dev = &g_devs[devNum]; + const TmioPort *const port = &dev->port; + + infoOut->type = dev->type; + infoOut->prot = dev->prot; + infoOut->rca = dev->rca; + infoOut->sectors = dev->sectors; + + const u32 clkSetting = port->sd_clk_ctrl & 0xFFu; + infoOut->clock = TMIO_HCLK / (clkSetting ? clkSetting<<2 : 2); + + memcpy(infoOut->cid, dev->cid, 16); + infoOut->ccc = dev->ccc; + infoOut->busWidth = (port->sd_option & OPTION_BUS_WIDTH1 ? 1 : 4); + + return SDMMC_ERR_NONE; +} + +u32 pico_SDMMC_getCid(const u8 devNum, u32 cidOut[4]) +{ + if(devNum > SDMMC_MAX_DEV_NUM) return SDMMC_ERR_INVAL_PARAM; + + if(cidOut != NULL) memcpy(cidOut, g_devs[devNum].cid, 16); + + return SDMMC_ERR_NONE; +} + +/*#include "fatfs/ff.h" // Needed for the "byte" type used in diskio.h. +#include "fatfs/diskio.h" +u8 SDMMC_getDiskStatus(const u8 devNum) +{ + if(devNum > SDMMC_MAX_DEV_NUM) return STA_NODISK | STA_NOINIT; + + u8 status = 0; + if(devNum == SDMMC_DEV_CARD) + status = (TMIO_cardDetected() == true ? 0 : STA_NODISK | STA_NOINIT); + + const SdmmcDev *const dev = &g_devs[devNum]; + status |= (dev->prot != 0 ? STA_PROTECT : 0); + if(dev->type == DEV_TYPE_NONE) + status |= STA_NOINIT;*/ + + // "Not valid if STA_NODISK is set." + /*if(status & STA_NODISK) + status &= ~STA_PROTECT;*/ + +// return status; +//} + +u32 pico_SDMMC_getSectors(const u8 devNum) +{ + if(devNum > SDMMC_MAX_DEV_NUM) return 0; + + return g_devs[devNum].sectors; +} + +static u32 updateStatus(SdmmcDev *const dev, const bool stopTransmission) +{ + TmioPort *const port = &dev->port; + + // MMC_STOP_TRANSMISSION: Same CMD for (e)MMC/SD. Relies on the driver returning a proper response. + // MMC_SEND_STATUS: Same CMD for (e)MMC/SD but the argument format differs slightly. + u32 res; + if(stopTransmission) res = TMIO_sendCommand(port, MMC_STOP_TRANSMISSION, 0); + else res = TMIO_sendCommand(port, MMC_SEND_STATUS, (u32)dev->rca<<16); + dev->status = (res == 0 ? port->resp[0] : 0); // Don't update the status with stale data. + + return res; +} + +// Note: On multi-block read from the last 2 sectors there are no errors reported by the controller +// however the R1 card status may report ADDRESS_OUT_OF_RANGE on next(?) status read. +// This error is normal for (e)MMC and can be ignored. +u32 pico_SDMMC_readSectors(const u8 devNum, u32 sect, void *const buf, const u16 count) +{ + if(devNum > SDMMC_MAX_DEV_NUM || count == 0) return SDMMC_ERR_INVAL_PARAM; + + // Check if the device is initialized. + SdmmcDev *const dev = &g_devs[devNum]; + const u8 devType = dev->type; + if(devType == DEV_TYPE_NONE) return SDMMC_ERR_NO_CARD; + + // Set destination buffer and sector count. + TmioPort *const port = &dev->port; + TMIO_setBuffer(port, buf, count); + + // Read a single 512 bytes block. Same CMD for (e)MMC/SD. + // Read multiple 512 bytes blocks. Same CMD for (e)MMC/SD. + const u16 readCmd = (count == 1 ? MMC_READ_SINGLE_BLOCK : MMC_READ_MULTIPLE_BLOCK); + if(devType == DEV_TYPE_MMC || devType == DEV_TYPE_SDSC) sect *= 512; // Byte addressing. + u32 res = TMIO_sendCommand(port, readCmd, sect); + if(res != 0) + { + // On error in the middle of multi-block reads the card will be stuck + // in data state and we need to send STOP_TRANSMISSION to bring it + // back to tran state. + // Otherwise for single-block reads just update the status. + updateStatus(dev, count > 1); + + return SDMMC_ERR_SECT_RW; + } + + return SDMMC_ERR_NONE; +} + +// Note: On multi-block write to the last 2 sectors there are no errors reported by the controller +// however the R1 card status may report ADDRESS_OUT_OF_RANGE on next(?) status read. +// This error is normal for (e)MMC and can be ignored. +u32 pico_SDMMC_writeSectors(const u8 devNum, u32 sect, const void *const buf, const u16 count) +{ + if(devNum > SDMMC_MAX_DEV_NUM || count == 0) return SDMMC_ERR_INVAL_PARAM; + + // Check if the device is initialized. + SdmmcDev *const dev = &g_devs[devNum]; + const u8 devType = dev->type; + if(devType == DEV_TYPE_NONE) return SDMMC_ERR_NO_CARD; + + // Check if the device is write protected. + if(dev->prot != 0) return SDMMC_ERR_WRITE_PROT; + + // Set source buffer and sector count. + TmioPort *const port = &dev->port; + TMIO_setBuffer(port, (void*)buf, count); + + // Write a single 512 bytes block. Same CMD for (e)MMC/SD. + // Write multiple 512 bytes blocks. Same CMD for (e)MMC/SD. + const u16 writeCmd = (count == 1 ? MMC_WRITE_BLOCK : MMC_WRITE_MULTIPLE_BLOCK); + if(devType == DEV_TYPE_MMC || devType == DEV_TYPE_SDSC) sect *= 512; // Byte addressing. + const u32 res = TMIO_sendCommand(port, writeCmd, sect); + if(res != 0) + { + // On error in the middle of multi-block writes the card will be stuck + // in data state and we need to send STOP_TRANSMISSION to bring it + // back to tran state. + // Otherwise for single-block writes just update the status. + updateStatus(dev, count > 1); + + return SDMMC_ERR_SECT_RW; + } + + return SDMMC_ERR_NONE; +} + +u32 pico_SDMMC_sendCommand(const u8 devNum, MmcCommand *const mmcCmd) +{ + if(devNum > SDMMC_MAX_DEV_NUM) return SDMMC_ERR_INVAL_PARAM; + + SdmmcDev *const dev = &g_devs[devNum]; + TmioPort *const port = &dev->port; + TMIO_setBlockLen(port, mmcCmd->blkLen); + TMIO_setBuffer(port, mmcCmd->buf, mmcCmd->count); + + const u32 res = TMIO_sendCommand(port, mmcCmd->cmd, mmcCmd->arg); + TMIO_setBlockLen(port, 512); // Restore default block length. + if(res != 0) + { + updateStatus(dev, false); + return SDMMC_ERR_SEND_CMD; + } + + memcpy(mmcCmd->resp, port->resp, 16); + + return SDMMC_ERR_NONE; +} + +u32 pico_SDMMC_getLastR1error(const u8 devNum) +{ + if(devNum > SDMMC_MAX_DEV_NUM) return 0; + + SdmmcDev *const dev = &g_devs[devNum]; + const u32 status = dev->status; + dev->status = 0; + + return status; +} diff --git a/arm7/source/mmc/tmio.h b/arm7/source/mmc/tmio.h new file mode 100644 index 0000000..a1d34df --- /dev/null +++ b/arm7/source/mmc/tmio.h @@ -0,0 +1,414 @@ +#pragma once + +// SPDX-License-Identifier: MIT +// Copyright (c) 2023 profi200 + +#include +#include +#include +#include +#include +#include + + +// For simplicity we will name the accessible 2 controllers 1 and 2. +// The real controller number is in the comment. +#define TMIO1_REGS_BASE (0x04004800u) // Controller 1. +#define TMIO2_REGS_BASE (0x04004A00u) // Controller 2. + +#define TMIO_HCLK (33513982u) // In Hz. + +typedef struct +{ + vu16 sd_cmd; // 0x000 + vu16 sd_portsel; // 0x002 + vu32 sd_arg; // 0x004 SD_ARG0 and SD_ARG1 combined. + vu16 sd_stop; // 0x008 + vu16 sd_blockcount; // 0x00A + const vu32 sd_resp[4]; // 0x00C SD_RESP0-7 16 bit reg pairs combined. + vu32 sd_status; // 0x01C SD_STATUS1 and SD_STATUS2 combined. + vu32 sd_status_mask; // 0x020 SD_STATUS1_MASK and SD_STATUS2_MASK combined. + vu16 sd_clk_ctrl; // 0x024 + vu16 sd_blocklen; // 0x026 + vu16 sd_option; // 0x028 Card detect timer, data timeout and bus width. + u8 _0x2a[2]; + const vu32 sd_err_status; // 0x02C SD_ERR_STATUS1 and SD_ERR_STATUS2 combined. + vu16 sd_fifo; // 0x030 + u8 _0x32[2]; + vu16 sdio_mode; // 0x034 + vu16 sdio_status; // 0x036 + vu16 sdio_status_mask; // 0x038 + u8 _0x3a[0x9e]; + vu16 dma_ext_mode; // 0x0D8 + u8 _0xda[6]; + vu16 soft_rst; // 0x0E0 + const vu16 revision; // 0x0E2 Controller version/revision? + u8 _0xe4[0xe]; + vu16 unkF2; // 0x0F2 Power related? Default 0. Other values do nothing? + vu16 ext_sdio_irq; // 0x0F4 Port 1/2/3 SDIO IRQ control. + const vu16 ext_wrprot; // 0x0F6 Apparently for eMMC. + vu16 ext_cdet; // 0x0F8 Card detect status. + vu16 ext_cdet_dat3; // 0x0FA DAT3 card detect status. + vu16 ext_cdet_mask; // 0x0FC Card detect mask (IRQ). + vu16 ext_cdet_dat3_mask; // 0x0FE DAT3 card detect mask (IRQ). + vu16 sd_fifo32_cnt; // 0x100 + u8 _0x102[2]; + vu16 sd_blocklen32; // 0x104 + u8 _0x106[2]; + vu16 sd_blockcount32; // 0x108 + u8 _0x10a[2]; + vu32 sd_fifo32; // 0x10C Note: This is in the FIFO region on ARM11 (3DS). +} Tmio; + +#ifdef __cplusplus +extern "C" +{ +#endif +static_assert(offsetof(Tmio, sd_fifo32) == 0x10C, "Error: Member sd_fifo32 of Tmio is not at offset 0x10C!"); + +__attribute__((always_inline)) static inline Tmio* getTmioRegs(const u8 controller) +{ + return (controller == 0 ? (Tmio*)TMIO1_REGS_BASE : (Tmio*)TMIO2_REGS_BASE); +} + +__attribute__((always_inline)) static inline vu32* getTmioFifo(Tmio *const regs) +{ + return ®s->sd_fifo32; +} + +#ifdef __cplusplus +} +#endif +// REG_SD_CMD +// Auto response supported commands: +// CMD0, CMD2, CMD3 (only SD?), CMD7 (only select?), CMD9, CMD10, CMD12, CMD13, +// CMD16, CMD17, CMD18, CMD25, CMD28, CMD55, ACMD6, ACMD23, ACMD42, ACMD51. +// +// When using auto response leave bits 11-13 unset (zero). + +// Bit 0-5 command index. +#define CMD_ACMD (1u<<6) // Application command. +#define CMD_RESP_AUTO (0u) // Response type auto. Only works with certain commands. +#define CMD_RESP_NONE (3u<<8) // Response type none. +#define CMD_RESP_R1 (4u<<8) // Response type R1 48 bit. +#define CMD_RESP_R5 (CMD_RESP_R1) // Response type R5 48 bit. +#define CMD_RESP_R6 (CMD_RESP_R1) // Response type R6 48 bit. +#define CMD_RESP_R7 (CMD_RESP_R1) // Response type R7 48 bit. +#define CMD_RESP_R1b (5u<<8) // Response type R1b 48 bit + busy. +#define CMD_RESP_R5b (CMD_RESP_R1b) // Response type R5b 48 bit + busy. +#define CMD_RESP_R2 (6u<<8) // Response type R2 136 bit. +#define CMD_RESP_R3 (7u<<8) // Response type R3 48 bit OCR without CRC. +#define CMD_RESP_R4 (CMD_RESP_R3) // Response type R4 48 bit OCR without CRC. +#define CMD_RESP_MASK (CMD_RESP_R3) +#define CMD_DATA_EN (1u<<11) // Data transfer enable. +#define CMD_DATA_R (1u<<12) // Data transfer direction read. +#define CMD_DATA_W (0u) // Data transfer direction write. +#define CMD_MULTI_DATA (1u<<13) // Multi block transfer (auto STOP_TRANSMISSION). +#define CMD_SEC_SDIO (1u<<14) // Security/SDIO command. + +// REG_SD_PORTSEL +#define PORTSEL_P0 (0u) // Controller port 0. +#define PORTSEL_P1 (1u) // Controller port 1. +#define PORTSEL_P2 (2u) // Controller port 2. +#define PORTSEL_P3 (3u) // Controller port 3. +#define PORTSEL_MASK (PORTSEL_P3) +// Bit 8-9 number of supported ports? +#define PORTSEL_UNK10 (1u<<10) // Unknown writable bit 10? + +// REG_SD_STOP +#define STOP_STOP (1u) // Abort data transfer and send STOP_TRANSMISSION CMD. +#define STOP_AUTO_STOP (1u<<8) // Automatically send STOP_TRANSMISSION on multi-block transfer end. + +// REG_SD_STATUS1/2 Write 0 to acknowledge a bit. +// REG_SD_STATUS1/2_MASK (M) = Maskable bit. 1 = disabled. +// Unmaskable bits act as status only, don't trigger IRQs and can't be acknowledged. +#define STATUS_RESP_END (1u) // (M) Response end. +#define STATUS_DATA_END (1u<<2) // (M) Data transfer end (triggers after last block). +#define STATUS_REMOVE (1u<<3) // (M) Card got removed. +#define STATUS_INSERT (1u<<4) // (M) Card got inserted. Set at the same time as DETECT. +#define STATUS_DETECT (1u<<5) // Card detect status (SD_OPTION detection timer). 1 = inserted. +#define STATUS_NO_WRPROT (1u<<7) // Write protection slider unlocked (low). +#define STATUS_DAT3_REMOVE (1u<<8) // (M) Card DAT3 got removed (low). +#define STATUS_DAT3_INSERT (1u<<9) // (M) Card DAT3 got inserted (high). +#define STATUS_DAT3_DETECT (1u<<10) // Card DAT3 status. 1 = inserted. +#define STATUS_ERR_CMD_IDX (1u<<16) // (M) Bad CMD index in response. +#define STATUS_ERR_CRC (1u<<17) // (M) Bad CRC in response. +#define STATUS_ERR_STOP_BIT (1u<<18) // (M) Stop bit error. Failed to recognize response frame end? +#define STATUS_ERR_DATA_TIMEOUT (1u<<19) // (M) Response data timeout. +#define STATUS_ERR_RX_OVERF (1u<<20) // (M) Receive FIFO overflow. +#define STATUS_ERR_TX_UNDERF (1u<<21) // (M) Send FIFO underflow. +#define STATUS_ERR_CMD_TIMEOUT (1u<<22) // (M) Response start bit timeout. +#define STATUS_SD_BUSY (1u<<23) // SD card signals busy if this bit is 0 (DAT0 held low). +#define STATUS_RX_RDY (1u<<24) // (M) FIFO ready for read. +#define STATUS_TX_REQ (1u<<25) // (M) FIFO write request. +// Bit 27 is maskable. Purpose unknown. +// Bit 29 exists (not maskable). Signals when clock divider changes are allowed? +#define STATUS_CMD_BUSY (1u<<30) // Command register busy. +#define STATUS_ERR_ILL_ACC (1u<<31) // (M) Illegal access error. TODO: What does that mean? + +#define STATUS_MASK_ALL (0xFFFFFFFFu) +#define STATUS_MASK_DEFAULT ((1u<<27) | STATUS_TX_REQ | STATUS_RX_RDY | \ + STATUS_DAT3_INSERT | STATUS_DAT3_REMOVE) +#define STATUS_MASK_ERR (STATUS_ERR_ILL_ACC | STATUS_ERR_CMD_TIMEOUT | STATUS_ERR_TX_UNDERF | \ + STATUS_ERR_RX_OVERF | STATUS_ERR_DATA_TIMEOUT | STATUS_ERR_STOP_BIT | \ + STATUS_ERR_CRC | STATUS_ERR_CMD_IDX) + +// REG_SD_CLK_CTRL +#define SD_CLK_DIV_2 (0u) // Clock divider 2. +#define SD_CLK_DIV_4 (1u) // Clock divider 4. +#define SD_CLK_DIV_8 (1u<<1) // Clock divider 8. +#define SD_CLK_DIV_16 (1u<<2) // Clock divider 16. +#define SD_CLK_DIV_32 (1u<<3) // Clock divider 32. +#define SD_CLK_DIV_64 (1u<<4) // Clock divider 64. +#define SD_CLK_DIV_128 (1u<<5) // Clock divider 128. +#define SD_CLK_DIV_256 (1u<<6) // Clock divider 256. +#define SD_CLK_DIV_512 (1u<<7) // Clock divider 512. +#define SD_CLK_EN (1u<<8) // Clock enable. +#define SD_CLK_PWR_SAVE (1u<<9) // Disables clock on idle. +// Bit 10 is writable... at least according to gbatek (can't confirm). Purpose unknown. + +// Outputs the matching divider for clk. +// Shift the output right by 2 to get the value for REG_SD_CLK_CTRL. +#define TMIO_CLK2DIV(clk) \ +({ \ + u32 __shift = 1; \ + while((clk) < TMIO_HCLK>>__shift) ++__shift; \ + 1u<<__shift; \ +}) + +// Clock off by default. +// Nearest possible for 400 kHz is 261.827984375 kHz. +#define SD_CLK_DEFAULT (TMIO_CLK2DIV(400000)>>2) + +// REG_SD_OPTION +// Note on card detection time: +// The card detection timer starts only on inserting cards (including cold boot with inserted card) +// and when mapping ports between controllers. Card power doesn't have any effect on the timer. +// +// Bit 0-3 card detect timer 0x400<sd_clk_ctrl = SD_CLK_PWR_SAVE | SD_CLK_EN | TMIO_CLK2DIV(clk)>>2; +} + +/** + * @brief Sets the transfer block length for a tmio port. + * + * @param port A pointer to the port struct. + * @param[in] blockLen The block length. Caution: Provide a buffer with multiple of 4 size regardless of block length. + */ +__attribute__((always_inline)) static inline void TMIO_setBlockLen(TmioPort *const port, u16 blockLen) +{ + if(blockLen > 512) blockLen = 512; + + port->sd_blocklen = blockLen; +} + +/** + * @brief Sets the bus width for a tmio port. + * + * @param port A pointer to the port struct. + * @param[in] width The bus width. + */ +__attribute__((always_inline)) static inline void TMIO_setBusWidth(TmioPort *const port, const u8 width) +{ + port->sd_option = (width == 4 ? OPTION_BUS_WIDTH4 : OPTION_BUS_WIDTH1) | + OPTION_UNK14 | OPTION_DEFAULT_TIMINGS; +} + +/** + * @brief Sets a transfer buffer for a tmio port. + * + * @param port A pointer to the port struct. + * @param buf The buffer pointer. + * @param[in] blocks The number of blocks to transfer. + */ +__attribute__((always_inline)) static inline void TMIO_setBuffer(TmioPort *const port, void *buf, const u16 blocks) +{ + port->buf = buf; + port->blocks = blocks; +} + +#ifdef __cplusplus +} +#endif diff --git a/arm7/source/mmc/tmio.twl.c b/arm7/source/mmc/tmio.twl.c new file mode 100644 index 0000000..d1bc160 --- /dev/null +++ b/arm7/source/mmc/tmio.twl.c @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2023 profi200 + +#include +#include "tmio.h" + + +// Using atomic load/store produces better code than volatile +// but still ensures that the status is always read from memory. +#define GET_STATUS(ptr) atomic_load_explicit((ptr), memory_order_relaxed) +#define SET_STATUS(ptr, val) atomic_store_explicit((ptr), (val), memory_order_relaxed) + +// ARM7 timer clock = controller clock = CPU clock. +// swiDelay() doesn't seem to be cycle accurate meaning +// one cycle is 4 (?) CPU cycles. +#define INIT_DELAY_FUNC() swi_waitByLoop(TMIO_CLK2DIV(400000u) * 74 / 4) + + +static u32 g_status[2] = {0}; + +static rtos_event_t sSdEvent; + +__attribute__((always_inline)) static inline u8 port2Controller(const u8 portNum) +{ + return portNum / 2; +} + +static void tmio1Isr(u32 irqMask) // SD/eMMC. +{ + Tmio *const regs = getTmioRegs(0); + g_status[0] |= regs->sd_status; + regs->sd_status = STATUS_CMD_BUSY; // Never acknowledge STATUS_CMD_BUSY. + rtos_signalEvent(&sSdEvent); + + // TODO: Some kind of event to notify the main loop for remove/insert. +} + +static void tmio2Isr(u32 irqMask) // WiFi SDIO. +{ + Tmio *const regs = getTmioRegs(1); + g_status[1] |= regs->sd_status; + regs->sd_status = STATUS_CMD_BUSY; // Never acknowledge STATUS_CMD_BUSY. +} + +void TMIO_init(void) +{ + rtos_createEvent(&sSdEvent); + // Register ISR and enable IRQs. + rtos_setIrq2Func(RTOS_IRQ2_SDMMC, tmio1Isr); + rtos_setIrq2Func(RTOS_IRQ2_SDIO, tmio2Isr); + rtos_enableIrq2Mask(RTOS_IRQ2_SDMMC); + rtos_enableIrq2Mask(RTOS_IRQ2_SDIO); + + // Reset all controllers. + for(u32 i = 0; i < 2; i++) + { + // Setup 32 bit FIFO. + Tmio *const regs = getTmioRegs(i); + regs->sd_fifo32_cnt = FIFO32_CLEAR | FIFO32_EN; + regs->sd_blocklen32 = 512; + regs->sd_blockcount32 = 1; + regs->dma_ext_mode = DMA_EXT_DMA_MODE; + + // Reset. Unlike similar controllers no delay is needed. + // Resets the following regs: + // REG_SD_STOP, REG_SD_RESP0-7, REG_SD_STATUS1-2, REG_SD_ERR_STATUS1-2, + // REG_SD_CLK_CTRL, REG_SD_OPTION, REG_SDIO_STATUS. + regs->soft_rst = SOFT_RST_RST; + regs->soft_rst = SOFT_RST_NORST; + + regs->sd_portsel = PORTSEL_P0; + regs->sd_blockcount = 1; + regs->sd_status_mask = STATUS_MASK_DEFAULT; + regs->sd_clk_ctrl = SD_CLK_DEFAULT; + regs->sd_blocklen = 512; + regs->sd_option = OPTION_BUS_WIDTH1 | OPTION_UNK14 | OPTION_DEFAULT_TIMINGS; + regs->ext_cdet_mask = EXT_CDET_MASK_ALL; + regs->ext_cdet_dat3_mask = EXT_CDET_DAT3_MASK_ALL; + + // Disable SDIO. + regs->sdio_mode = 0; + regs->sdio_status_mask = SDIO_STATUS_MASK_ALL; + regs->ext_sdio_irq = EXT_SDIO_IRQ_MASK_ALL; + } +} + +void TMIO_deinit(void) +{ + rtos_disableIrq2Mask(RTOS_IRQ2_SDMMC); + rtos_setIrq2Func(RTOS_IRQ2_SDMMC, NULL); + rtos_disableIrq2Mask(RTOS_IRQ2_SDIO); + rtos_setIrq2Func(RTOS_IRQ2_SDIO, NULL); + + // Mask all IRQs. + for(u32 i = 0; i < 2; i++) + { + // 32 bit FIFO IRQs. + Tmio *const regs = getTmioRegs(i); + regs->sd_fifo32_cnt = 0; // FIFO and all IRQs disabled/masked. + + // Regular IRQs. + regs->sd_status_mask = STATUS_MASK_ALL; + + // SDIO IRQs. + regs->sdio_status_mask = SDIO_STATUS_MASK_ALL; + } +} + +void TMIO_initPort(TmioPort *const port, const u8 portNum) +{ + // Reset port state. + port->portNum = portNum; + port->sd_clk_ctrl = SD_CLK_DEFAULT; + port->sd_blocklen = 512; + port->sd_option = OPTION_BUS_WIDTH1 | OPTION_UNK14 | OPTION_DEFAULT_TIMINGS; +} + +// TODO: What if we get rid of setPort() and only use one port per controller? +static void setPort(Tmio *const regs, const TmioPort *const port) +{ + // TODO: Can we somehow prevent all these reg writes each time? + // Maybe some kind of dirty flag + active port check? + regs->sd_portsel = port->portNum % 2u; + regs->sd_clk_ctrl = port->sd_clk_ctrl; + const u16 blocklen = port->sd_blocklen; + regs->sd_blocklen = blocklen; + regs->sd_option = port->sd_option; + regs->sd_blocklen32 = blocklen; +} + +bool TMIO_cardDetected(void) +{ + return getTmioRegs(0)->sd_status & STATUS_DETECT; +} + +bool TMIO_cardWritable(void) +{ + return getTmioRegs(0)->sd_status & STATUS_NO_WRPROT; +} + +void TMIO_powerupSequence(TmioPort *const port) +{ + port->sd_clk_ctrl = SD_CLK_EN | SD_CLK_DEFAULT; + setPort(getTmioRegs(port2Controller(port->portNum)), port); + INIT_DELAY_FUNC(); +} + +static void getResponse(const Tmio *const regs, TmioPort *const port, const u16 cmd) +{ + // We could check for response type none as well but it's not worth it. + if((cmd & CMD_RESP_MASK) != CMD_RESP_R2) + { + port->resp[0] = regs->sd_resp[0]; + } + else // 136 bit R2 responses need special treatment... + { + u32 resp[4]; + for(u32 i = 0; i < 4; i++) resp[i] = regs->sd_resp[i]; + + port->resp[0] = resp[3]<<8 | resp[2]>>24; + port->resp[1] = resp[2]<<8 | resp[1]>>24; + port->resp[2] = resp[1]<<8 | resp[0]>>24; + port->resp[3] = resp[0]<<8; // TODO: Add the missing CRC7 and bit 0? + } +} + +// Note: Using STATUS_DATA_END to detect transfer end doesn't work reliably +// because STATUS_DATA_END fires before we even read anything from FIFO +// on single block read transfer. +static void doCpuTransfer(Tmio *const regs, const u16 cmd, u8 *buf, const u32 *const statusPtr) +{ + const u32 blockLen = regs->sd_blocklen; + u32 blockCount = regs->sd_blockcount; + vu32 *const fifo = getTmioFifo(regs); + if(cmd & CMD_DATA_R) + { + while((GET_STATUS(statusPtr) & STATUS_MASK_ERR) == 0 && blockCount > 0) + { + if(regs->sd_fifo32_cnt & FIFO32_FULL) // RX ready. + { + const u8 *const blockEnd = buf + blockLen; + do + { + if((uintptr_t)buf % 4 == 0) + { + *((u32*)buf) = *fifo; + } + else + { + const u32 tmp = *fifo; + buf[0] = tmp; + buf[1] = tmp>>8; + buf[2] = tmp>>16; + buf[3] = tmp>>24; + } + buf += 4; + } while(buf < blockEnd); + + blockCount--; + } + else rtos_waitEvent(&sSdEvent, false, true); + } + } + else + { + // TODO: Write first block ahead of time? + // gbatek Command/Param/Response/Data at bottom of page. + while((GET_STATUS(statusPtr) & STATUS_MASK_ERR) == 0 && blockCount > 0) + { + if(!(regs->sd_fifo32_cnt & FIFO32_NOT_EMPTY)) // TX request. + { + const u8 *const blockEnd = buf + blockLen; + do + { + if((uintptr_t)buf % 4 == 0) + { + *fifo = *((u32*)buf); + } + else + { + u32 tmp = buf[0]; + tmp |= (u32)buf[1]<<8; + tmp |= (u32)buf[2]<<16; + tmp |= (u32)buf[3]<<24; + *fifo = tmp; + } + buf += 4; + } while(buf < blockEnd); + + blockCount--; + } + else rtos_waitEvent(&sSdEvent, false, true); + } + } +} + +u32 TMIO_sendCommand(TmioPort *const port, const u16 cmd, const u32 arg) +{ + const u8 controller = port2Controller(port->portNum); + Tmio *const regs = getTmioRegs(controller); + + // Clear status before sending another command. + u32 *const statusPtr = &g_status[controller]; + SET_STATUS(statusPtr, 0); + + setPort(regs, port); + const u16 blocks = port->blocks; + regs->sd_blockcount = blocks; // sd_blockcount32 doesn't need to be set. + regs->sd_stop = STOP_AUTO_STOP; // Auto STOP_TRANSMISSION (CMD12) on multi-block transfer. + regs->sd_arg = arg; + + // We don't need FIFO IRQs when using DMA. buf = NULL means DMA. + u8 *buf = port->buf; + u16 f32Cnt = FIFO32_CLEAR | FIFO32_EN; + if(buf != NULL) f32Cnt |= (cmd & CMD_DATA_R ? FIFO32_FULL_IE : FIFO32_NOT_EMPTY_IE); + regs->sd_fifo32_cnt = f32Cnt; + regs->sd_cmd = (blocks > 1 ? CMD_MULTI_DATA | cmd : cmd); // Start. + + // TODO: Benchmark if this order is ideal? + // Response end comes immediately after the + // command so we need to check before __wfi(). + // On error response end still fires. + while((GET_STATUS(statusPtr) & STATUS_RESP_END) == 0) rtos_waitEvent(&sSdEvent, false, true); + getResponse(regs, port, cmd); + + if((cmd & CMD_DATA_EN) != 0) + { + // If we have to transfer data do so now. + if(buf != NULL) doCpuTransfer(regs, cmd, buf, statusPtr); + + // Wait for data end if needed. + // On error data end still fires. + while((GET_STATUS(statusPtr) & STATUS_DATA_END) == 0) rtos_waitEvent(&sSdEvent, false, true); + } + + // STATUS_CMD_BUSY is no longer set at this point. + + return GET_STATUS(statusPtr) & STATUS_MASK_ERR; +} diff --git a/arm7/source/picoLoaderBootstrap.cpp b/arm7/source/picoLoaderBootstrap.cpp new file mode 100644 index 0000000..65dd595 --- /dev/null +++ b/arm7/source/picoLoaderBootstrap.cpp @@ -0,0 +1,39 @@ +#include "common.h" +#include +#include +#include +#include "ipcChannels.h" +#include "picoLoader7.h" +#include "picoLoaderBootstrap.h" + +typedef void (*pico_loader_7_func_t)(void); + +static volatile bool sShouldStart = false; + +static void ipcMessageHandler(u32 channel, u32 data, void* arg) +{ + if (data == 1) + { + sShouldStart = true; + } +} + +void pload_init() +{ + ipc_setChannelHandler(IPC_CHANNEL_LOADER, ipcMessageHandler, nullptr); +} + +bool pload_shouldStart() +{ + return sShouldStart; +} + +void pload_start() +{ + snd_setMasterEnable(false); + rtos_disableIrqs(); + REG_IME = 0; + auto header7 = (pload_header7_t*)0x06000000; + header7->dldiDriver = (void*)0x037F8000; + ((pico_loader_7_func_t)header7->entryPoint)(); +} \ No newline at end of file diff --git a/arm7/source/picoLoaderBootstrap.h b/arm7/source/picoLoaderBootstrap.h new file mode 100644 index 0000000..daff948 --- /dev/null +++ b/arm7/source/picoLoaderBootstrap.h @@ -0,0 +1,5 @@ +#pragma once + +void pload_init(); +bool pload_shouldStart(); +void pload_start(); diff --git a/arm9/data/NotoSansJP-Medium-10.nft2 b/arm9/data/NotoSansJP-Medium-10.nft2 new file mode 100644 index 0000000000000000000000000000000000000000..d0c8fa6117eeefac57ef31b306a4a11830ea3ca1 GIT binary patch literal 17852 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 XDF&6Jj3+~E1sDQWvw(~OEeHSrY7m#8 literal 0 HcmV?d00001 diff --git a/arm9/data/NotoSansJP-Medium-11.nft2 b/arm9/data/NotoSansJP-Medium-11.nft2 new file mode 100644 index 0000000000000000000000000000000000000000..fdde0ae380a0e5f5f2083f467151a41588bf04bd GIT binary patch literal 20220 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 literal 0 HcmV?d00001 diff --git a/arm9/data/NotoSansJP-Medium-7_5.nft2 b/arm9/data/NotoSansJP-Medium-7_5.nft2 new file mode 100644 index 0000000000000000000000000000000000000000..94e5ade9d8f5932caf97e52463562382849d3f36 GIT binary patch literal 12432 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 literal 0 HcmV?d00001 diff --git a/arm9/data/NotoSansJP-Regular-10.nft2 b/arm9/data/NotoSansJP-Regular-10.nft2 new file mode 100644 index 0000000000000000000000000000000000000000..4bba3727364609856b11f0d0c3a12d804305052a GIT binary patch literal 17508 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�Y68OaR5U5gq}?1~vwUWeg09xgeti z0xS%S3mA9>cpF$F0yq@7mB2x;1XLh{+<6tWP65;i2QBFa%}TH^Ffc9%O*G&#U_QVo zQ@~!puDXFqfQex#ND`z2)NEY@aww>zXHZg7Vqlmt6O`#eqc&&GoY4V~phD)fz|ECS zpcD)pXaQ}AWGF0U*u6U%G}ghuaOljLGcpVer69jVfZBYrph^f7UZA0BF9uL%iez9o zS;J`A8L z%Rp(Xk`SbEzyLBN0aT2DDjkp_28GqDb}mI3s@V>FrlV(qP7A< z3ur;(6p-PNh7~Kgz6EDNP(>caz%YS}L17gG!xdIg1qd2Q5#(lIxCnA_E66{d3GAi&eOkiEW%HY5SDh}Zl zNchhRvYmo#miajoJ(s7!7zBCa`T_VQ}CE)gauUrn44<0)tlKX{J-vOyJxP zN-dxn@FQpLf^q}MD+UaW3<5J4H!?7YF>EwA&d%^~jVQRSzyRrogY)o&vv)u>2Ll5r zca|}4WMC*~-l%Y#gW=&CG3X!{sQnIFstOuOgeeA@fBeiD(C|2DJpwNSLjeZ^!xct` zLS2RcM2>d?sqsvbKU9$U0$A(gm4o z%EfTdb`lFi^dV-32^{AT58yKcAs4+Bvst0hKGcbS(NzVxy3=EzQ zLWj?sJHr7U8WiC2Y-X6i>bZe=34^Brw}65b10%yk(6B9o=LAmBqCGYg#YJGnOd!Ra z2*rDL?*2Bsrt?#vN@v@Sqv#z4M;4k0pFF)UiOYMqs#v9Yo6 zid9{Lf(IEG4s6?fnt|cKE-(pl-|;hN-mzXG$qR3!&}1ch6-2 zb#WLBco-OtFfnYC2KNdiLG7qb(hQrVLGA1oMutd+1RjAojG&a_6u`v5Fd4M|ss&D@ zO<-1Ju$sUeZNM|(80$sm2{sG@ppq3du?NaQpscRNz`&@*V8ElraE!_7xFo|ZaY$`> z;>?*IP{RiW3kps-xqCNg=mlJo&YO9Yg@@q)BPX~)7X@zK8$eg_fc$*w%$d1X3=Cyf z3=Ezk;DBym2ag_u>RHGD8z|{8@Gwkx4QeSJ0JW4r#KzaV_h}0-Fz~=)m4S(&;S9KK z&NzXUfx#10$9sbEHmJ-14U=CwbLNaK$j+l@&VX8aWgBeAyvv|rUmFI7E1)JNsO=6iFOY#@15*_GxRsQYl=kf2z1wi>mL-#m6t^-k zOkrja(B@_kkOs9cm>2_23vnMX=wZ9S_?57UZowHdXM&>*WW2*l z*2zjXpfw)_9H2@{88q<00BV#m^su5Xitb?rm7)wqAcrV`(+xppP%97Wy6RRQ1LjE{ zpmhOh!3+~vCa{54!!BaTWLN-(4QvR&FbyoUfGL3!BH6}}aE6^hX$j+YV;cbmhgr;E HV;C3!fb*!m literal 0 HcmV?d00001 diff --git a/arm9/gfx/backIcon.grit b/arm9/gfx/backIcon.grit new file mode 100644 index 0000000..9392dda --- /dev/null +++ b/arm9/gfx/backIcon.grit @@ -0,0 +1,7 @@ +# tile format +-gt + +# graphics bit depth is 4 (16 color) +-gB4 + +-p! \ No newline at end of file diff --git a/arm9/gfx/backIcon.png b/arm9/gfx/backIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..5ad0cf1b5de4517f1dfa4c499def80704f980a0c GIT binary patch literal 172 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdrU~ojgN0I5=)U^nbv>z+e#I z6XFU|BOsutsAy=Zf=Ts=^#ct(+fVNwM5 UOUI|zK=v?ry85}Sb4q9e0AEcz5dZ)H literal 0 HcmV?d00001 diff --git a/arm9/gfx/bannerListIcon.grit b/arm9/gfx/bannerListIcon.grit new file mode 100644 index 0000000..9392dda --- /dev/null +++ b/arm9/gfx/bannerListIcon.grit @@ -0,0 +1,7 @@ +# tile format +-gt + +# graphics bit depth is 4 (16 color) +-gB4 + +-p! \ No newline at end of file diff --git a/arm9/gfx/bannerListIcon.png b/arm9/gfx/bannerListIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..f85b1fddd191f20f4f3c3dedded19334f058f53b GIT binary patch literal 184 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdrU~ojgN0I5=)U^nbv>z+e#I z6XMFiz#u3nsHCK1Y;5e};u0DfnwFMUUS8hO(J^!8%vGya?cTln%$YNH@7{g)?%n_Y z|EmQgKz5jTx;TbZ%t=mYV4U~=zj=e?A08fg1)hKW^8fz-=TAJa)PafTj=|9%TR!JB iWIey<5Ma&2z_4bzf|vR|t~v$=1_n=8KbLh*2~7Z?+(D-R literal 0 HcmV?d00001 diff --git a/arm9/gfx/bannerListItemBg0.grit b/arm9/gfx/bannerListItemBg0.grit new file mode 100644 index 0000000..60becf7 --- /dev/null +++ b/arm9/gfx/bannerListItemBg0.grit @@ -0,0 +1,5 @@ +# tile format +-gt + +# graphics bit depth is 4 (16 color) +-gB4 diff --git a/arm9/gfx/bannerListItemBg0.png b/arm9/gfx/bannerListItemBg0.png new file mode 100644 index 0000000000000000000000000000000000000000..f2bd2da015d167e8345cabec22996ea5f3a026a2 GIT binary patch literal 256 zcmeAS@N?(olHy`uVBq!ia0y~yU~m9o7G?$ph6wkZw+svn%*9TgAsieWw;%dHU|?V{ z2=EDU{m;O#mBDE*gV#9*!+Q*h&lx1PN?3kp;P}oUa7{vBij(11CxM_T63J5p6t8hG z{AYMSsmOqVfuY9J#WAE}PI7_*(}wBcC0m*mITl=W`1xt?`~Q#G99XwjEOF%0`uLJv zv*W1t|M`uzc8sht&p9;8mPDvQiKxJ-e@k1q1g?E$5!vutAV|P% zg@n#o#RUzv7VQp!F%Q>hu&{OA$Oy=cm+N8|2#)P(n#j!Hyft6uh7g(jh@@V4km{4ax-O)@Nhn5U|?YIboFyt=akR{0QmGZ=Kufz literal 0 HcmV?d00001 diff --git a/arm9/gfx/bannerListItemBg2.grit b/arm9/gfx/bannerListItemBg2.grit new file mode 100644 index 0000000..9392dda --- /dev/null +++ b/arm9/gfx/bannerListItemBg2.grit @@ -0,0 +1,7 @@ +# tile format +-gt + +# graphics bit depth is 4 (16 color) +-gB4 + +-p! \ No newline at end of file diff --git a/arm9/gfx/bannerListItemBg2.png b/arm9/gfx/bannerListItemBg2.png new file mode 100644 index 0000000000000000000000000000000000000000..2cea087d8ff9ed0cc41d1870bb44695071800ad4 GIT binary patch literal 248 zcmeAS@N?(olHy`uVBq!ia0y~yU{GLSU~phzW?*3GT#zxBfq{X!*vT`5gM;JtL;nX1 z3=9SVJ|V9E85p)QIPGQdI>%smk3sP{gTz(|%kK;v-x&n1NeE1FGTiDU5Hv+1d5VDI zH4cXV4DTlu889#~lz6&0hE&W+PHgXw?#BJMxA z4#Ge7%f>!n+jZbFlS!j~3#*jJb)$^43)*zQ6tc(0{9=iHps~wAi+R-n`C^mC^tUUR z>RluQSnlcYxeEn^goJ!^@O!;*;X+xBorxj~7#QYnu@dC;+?>e3z`)??>gTe~DWM4f DR$o;N literal 0 HcmV?d00001 diff --git a/arm9/gfx/bottomSheetBg.grit b/arm9/gfx/bottomSheetBg.grit new file mode 100644 index 0000000..d6fc647 --- /dev/null +++ b/arm9/gfx/bottomSheetBg.grit @@ -0,0 +1,18 @@ +# tile format +-gt + +# tile reduction by tiles, palette and hflip/vflip +-mRtpf +-ma2 + +# graphics bit depth is 4 (16 color) +-gB4 + +-p +-pn16 + +# map layout standard bg format +-mLs + +#-gzl +#-mzl \ No newline at end of file diff --git a/arm9/gfx/bottomSheetBg.png b/arm9/gfx/bottomSheetBg.png new file mode 100644 index 0000000000000000000000000000000000000000..8432e13f6863ca260236a56fd3e291e6e7db2f3d GIT binary patch literal 438 zcmeAS@N?(olHy`uVBq!ia0y~yU}RumU}9ilW?*2Lzh(Ui1_lP^VkgfK4i1jn5B(o7 zFfbSd_=LDJFfa%R2q-El8X6ipIXMLd1tljZ7Z(@z^z_V`GiUAEwR`vOJ$LThy?giG zzkmP#|NjfM7IPUG7>zt#978JRyuG$D(bP0l+XkK!XS$8 literal 0 HcmV?d00001 diff --git a/arm9/gfx/carouselMask.grit b/arm9/gfx/carouselMask.grit new file mode 100644 index 0000000..409fe40 --- /dev/null +++ b/arm9/gfx/carouselMask.grit @@ -0,0 +1,3 @@ +-gb +-gB8 +-p! \ No newline at end of file diff --git a/arm9/gfx/carouselMask.png b/arm9/gfx/carouselMask.png new file mode 100644 index 0000000000000000000000000000000000000000..6837562a6ab431232fc8ec831c6ee24e9b8019c5 GIT binary patch literal 1221 zcmeAS@N?(olHy`uVBq!ia0y~yU{GLSU=ZS928oF2Brq^AFc&*{hH!9j+hH0R;k3AOQu6P@o9~hEQM$1x`@l1qDG+ z5CsLvP>>A;#ZXWU1uam}0|ir{U=9>4g@UzEuoVjSLcuX8I0psSpx_=9Jcok!Q1BfJ z{zJjk$L`?_3=FBBE{-7;bCQ2FvgG-67ziXdFmt?Q6?;*n!Bv)#Ftttd$AkuFsZ|eJ z8T~3ZG>LQVcFxFMJ$=^84&zL7LA$^8}ha-z3 iYXvhWi@~G$Aq)&h^_*wUE4-ls3Up6bKbLh*2~7Y6o4i8+ literal 0 HcmV?d00001 diff --git a/arm9/gfx/chipFilled.grit b/arm9/gfx/chipFilled.grit new file mode 100644 index 0000000..b37877e --- /dev/null +++ b/arm9/gfx/chipFilled.grit @@ -0,0 +1,5 @@ +# tile format +-gt + +# graphics bit depth is 4 (16 color) +-gB4 \ No newline at end of file diff --git a/arm9/gfx/chipFilled.png b/arm9/gfx/chipFilled.png new file mode 100644 index 0000000000000000000000000000000000000000..1c9d48cd1eecce195d3fe3f384010806f50cf762 GIT binary patch literal 197 zcmeAS@N?(olHy`uVBq!ia0y~yU~phyU{GLTW?*0l*qpqbfq{X!*vT`5gM;JtL;nX1 z3=9SVJ|V6Q{~7)>Ffc#}hJR`dHyL<7GxB~|%Mirixc5DSz()o?#;O*^jsA?Ad>Q{S zCS1Df!N9=a=;`7ZQZXkvpz+e#I z6XFU|BOsutsAyDs`^W5Tb!-C7j58T0 kH)*FOtZ2_rXJTM5sS?wR6Iah=U|?YIboFyt=akR{0EW>)2LJ#7 literal 0 HcmV?d00001 diff --git a/arm9/gfx/folderCover.grit b/arm9/gfx/folderCover.grit new file mode 100644 index 0000000..9fa72ea --- /dev/null +++ b/arm9/gfx/folderCover.grit @@ -0,0 +1,3 @@ +-gb +-gB8 +-p \ No newline at end of file diff --git a/arm9/gfx/folderCover.png b/arm9/gfx/folderCover.png new file mode 100644 index 0000000000000000000000000000000000000000..9f8313593fc8509230f37681dd77561e1317f4a0 GIT binary patch literal 1040 zcmeAS@N?(olHy`uVBq!ia0y~yU}#`qU`XI#W?*1=w4|$=fq{X!*vT`5gM;JtL;nX1 z49pAxJ|V6QqhK@yhFu6aIXQWGc?AUpMMXs=CnvYGwDk1!Oqnue&YU?*moDA9b?e@} zdygGEcJAD{>GSWYGB7ag@pN$vshIQjh9h6I1CMjyyZSo}Z@09r>d@U#kmVckC+2{E zr^%&?A7bZ`!>S>g`IgM3GiSLXj{catL5i{SkM9HKEk1QERjt_$S&YB9)-mqNbcZ3`b86@IYbDPmvzU|F_jEB%wvJ!>@}4YXy6e-!^Yp{E ydWRoaxJJ(YU(LdG^XGk1{%pSBII?LAWIiw#RvDMS-?a24NZixa&t;ucLK6T~o^@&f literal 0 HcmV?d00001 diff --git a/arm9/gfx/gamesIcon.grit b/arm9/gfx/gamesIcon.grit new file mode 100644 index 0000000..9392dda --- /dev/null +++ b/arm9/gfx/gamesIcon.grit @@ -0,0 +1,7 @@ +# tile format +-gt + +# graphics bit depth is 4 (16 color) +-gB4 + +-p! \ No newline at end of file diff --git a/arm9/gfx/gamesIcon.png b/arm9/gfx/gamesIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..4546d8fa1d24d693f0d1b903b29a019663ce7105 GIT binary patch literal 227 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdrU~ojgN0I5=)U^nbv>z+e#I z6XMFiz#u3nsHCK1Y;5e};u0DfnwFMUUS8hO(J^!8%vGya?cTln%$YNH@7{g)?%n_Y z|EmQgKz77?x;TbZ%t=l-z$3@2{jSUZt zoK=}US`w`ehjq-$X!GXz`u~4@`i0br6CXMn9s3>o5_kV#TQKWT7TeqFTs%BJzs(+Q b6lVB%LRu%UA+VHzfq}u()z4*}Q$iB}hvHSW literal 0 HcmV?d00001 diff --git a/arm9/gfx/hGridIcon.grit b/arm9/gfx/hGridIcon.grit new file mode 100644 index 0000000..9392dda --- /dev/null +++ b/arm9/gfx/hGridIcon.grit @@ -0,0 +1,7 @@ +# tile format +-gt + +# graphics bit depth is 4 (16 color) +-gB4 + +-p! \ No newline at end of file diff --git a/arm9/gfx/hGridIcon.png b/arm9/gfx/hGridIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..e719b04a8b86c8bd4bee29ef9e64646ab268eb9d GIT binary patch literal 173 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdrU~ojgN0I5=)U^nbv>z+e#I z6XMFiz#u3nsHCK1Y;5e};u0DfnwFMUUS8hO(J^!8%vGya?cTln%$YNH@7{g)?%n_Y z|EmQgKz3+)x;TbZ%t=mgVBz+e#I z6XFU|BOsutsAy#-@X^ z3`rYu6B2eZBz>?lFj&K4n8vs?a-IpKzc~rSZN(<983H{})*Q zHM9I%%$<;N&|r()-Kgr4_CyQW!^IN!(kHZW%N!7Fk!a-UZd3LUY?ce$!p_k6ibIqw T?CEv}1_lOCS3j3^P6QU+f0x literal 0 HcmV?d00001 diff --git a/arm9/gfx/iconCell2.grit b/arm9/gfx/iconCell2.grit new file mode 100644 index 0000000..60becf7 --- /dev/null +++ b/arm9/gfx/iconCell2.grit @@ -0,0 +1,5 @@ +# tile format +-gt + +# graphics bit depth is 4 (16 color) +-gB4 diff --git a/arm9/gfx/iconCell2.png b/arm9/gfx/iconCell2.png new file mode 100644 index 0000000000000000000000000000000000000000..dfeacf0c6f7f114b2fa76a32cb841042a895d823 GIT binary patch literal 368 zcmeAS@N?(olHy`uVBq!ia0y~yU~m9o7G?$ph6wkZw+svn%*9TgAsieWw;%dHU|?V{ z2=EDU{r>$s0}L!(x^(T@wOhAt-Me@1v17;1ojZ5!+O>Q4?md70{Qdj)-@kwV|NsBg zR;FqO28K7DE{-7;bKYKYWBue? z%HY0T*W~iqGgJ8QxM=)PR{HVrhwKN#V>6s4C@{24X>eMw$99IOyXVKL{0~z!KEG04 zFJh!D!x^fb(R;!mzgb*jK?mbm4&F->m{>C!zVj8RYF$y)lXAK8dzDogqknTTzeD;- zy9URaXYyWrk7Rx^)D`eOc^@w&&@Q6E#5;+ZbzWMdiZs`pHL_3dcXD|%hiu7VzHgju zaQN+?Y`2&CYg}Kgs5rWOU39$O^RJhue2z@W|IB&JdTwd)ZT4#ohRJXCFfI}^t}cBT Qcpel^p00i_>zopr0QLczhX4Qo literal 0 HcmV?d00001 diff --git a/arm9/gfx/iconCell3.grit b/arm9/gfx/iconCell3.grit new file mode 100644 index 0000000..60becf7 --- /dev/null +++ b/arm9/gfx/iconCell3.grit @@ -0,0 +1,5 @@ +# tile format +-gt + +# graphics bit depth is 4 (16 color) +-gB4 diff --git a/arm9/gfx/iconCell3.png b/arm9/gfx/iconCell3.png new file mode 100644 index 0000000000000000000000000000000000000000..91026fa0f5500591eca395cdece9f211750ca73e GIT binary patch literal 325 zcmeAS@N?(olHy`uVBq!ia0y~yU~m9o7G?$ph6wkZw+svn%*9TgAsieWw;%dHU|?V{ z2=EDU{m;O#mBDE*gV#9*!+Q*h&lx1PN?3kp;P}oUa7{vBij(11CxM_T63J5p6t8hG z{AYMSsmOqVf#IO1i(^Q|oa6)rrVZ1>OSUvC3T)ZFefsppr>4KJ{V&h@XLskSMc?e% zc692*|C9?pxS!L5E6MQb|Nr|NpB(ymL*$QMgZ2Z9e&2)l*>*Gq>G&~NDaou`z%u*K z|Nrkqv_CA^E4nyO!fBs`+(UyaPD0h++%+_gMm79#oWj56^+wGfVhrI2-ZlFkOmvYv zD%ZAF;*5hvg2vf{Aq4`7%ojQy$b4D!V~XY^Mjjr~b6rje3pkdx`rMKXaH!PK>TsAW e&$^g_kKt_O{WJxsNd*iH3=E#GelF{r5}E*C$$d8f literal 0 HcmV?d00001 diff --git a/arm9/gfx/largeDSCardIcon.grit b/arm9/gfx/largeDSCardIcon.grit new file mode 100644 index 0000000..9392dda --- /dev/null +++ b/arm9/gfx/largeDSCardIcon.grit @@ -0,0 +1,7 @@ +# tile format +-gt + +# graphics bit depth is 4 (16 color) +-gB4 + +-p! \ No newline at end of file diff --git a/arm9/gfx/largeDSCardIcon.png b/arm9/gfx/largeDSCardIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..6e16e963742fb4f932ddcd963929df2b47f896ae GIT binary patch literal 203 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I7G?$phQ^Te;|vT8%*9TgAsieWw;%dHU|?V{ z2=EDUWnf?s6ckiaQZhC+c5!hD4Gm38ODiug@95~5IdkT!RjYRI-hJlGnY(xIzI*rX z|NsBh0umrQ+&x_!Ln`JZCn)e(y!+qYyr0*h@!x-s2kZe4>^2>UUvQva#6YV-^8+Je zhro|^y$|fM52Q7uCb04?aJ?eWdSyjTu!GALV+ICJ{u<`nCqsJ~7#J8lUHx3vIVCg! E07#NYFaQ7m literal 0 HcmV?d00001 diff --git a/arm9/gfx/largeFileIcon.grit b/arm9/gfx/largeFileIcon.grit new file mode 100644 index 0000000..9392dda --- /dev/null +++ b/arm9/gfx/largeFileIcon.grit @@ -0,0 +1,7 @@ +# tile format +-gt + +# graphics bit depth is 4 (16 color) +-gB4 + +-p! \ No newline at end of file diff --git a/arm9/gfx/largeFileIcon.png b/arm9/gfx/largeFileIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..13b37b70c3a403b657280e7712dc46856679d343 GIT binary patch literal 4189 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 B^<)45 literal 0 HcmV?d00001 diff --git a/arm9/gfx/largeFolderIcon.grit b/arm9/gfx/largeFolderIcon.grit new file mode 100644 index 0000000..9392dda --- /dev/null +++ b/arm9/gfx/largeFolderIcon.grit @@ -0,0 +1,7 @@ +# tile format +-gt + +# graphics bit depth is 4 (16 color) +-gB4 + +-p! \ No newline at end of file diff --git a/arm9/gfx/largeFolderIcon.png b/arm9/gfx/largeFolderIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..ee5d6b2f26c40e96981ddbadb42fa5d3ffdb55a4 GIT binary patch literal 204 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I7G?$phQ^Te;|vT8%*9TgAsieWw;%dHU|?V{ z2=EDUWnf?s6ckiaQZhC+c5!hD4Gm38ODiug@95~5IdkT!RjYRI-hJlGnY(xIzI*rX z|NsBh0umrQJUm?-Ln`JZCoEulQ1So&|In1ifBzj48dN^WsT44HG93B;Uo@71>z~;I z&OiSf^*->&KKNfCzN7!le{Sv+2?kaH_DMno0zrHXOLl3*b>p9N+z+e#I z6XFU|BOsutsAySY0RcrtMMFbFCnu+%prGXB3y7X`M4-wzjdv9Og zZ`|PfUi#uU$pxu`yq!(Y7_w%c_y8ZEs%KHN+ vfBrdN{@{^)K*j!RPwUtW;?aWK-1dO^hqykM3--2EpycA|>gTe~DWM4fC+y3> literal 0 HcmV?d00001 diff --git a/arm9/gfx/moviesIcon.grit b/arm9/gfx/moviesIcon.grit new file mode 100644 index 0000000..9392dda --- /dev/null +++ b/arm9/gfx/moviesIcon.grit @@ -0,0 +1,7 @@ +# tile format +-gt + +# graphics bit depth is 4 (16 color) +-gB4 + +-p! \ No newline at end of file diff --git a/arm9/gfx/moviesIcon.png b/arm9/gfx/moviesIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..8e00a59b2273c714ab65bb2f406da5734e555ad7 GIT binary patch literal 203 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdrU~ojgN0I5=)U^nbv>z+e#I z6XMFiz#u3nsHCK1Y;5e};u0DfnwFMUUS8hO(J^!8%vGya?cTln%$YNH@7{g)?%n_Y z|EmQgKz6u$x;TbZ%t=mgV5&8DP`SJ>cJ>zO2A+3^s#uqCJFv}f*u!+0xr4){>%F~z zO~Ty+2ly|2eEgMN!z+e#I z6XMFiz#u3nsHCK1Y;5e};u0DfnwFMUUS8hO(J^!8%vGya?cTln%$YNH@7{g)?%n_Y z|EmQgKz4X}x;TbZ%t=l-z&1naoG-)0oc!t5bqt0-M6Nt*ko@yIPt;kWkB3KqjajYv zsb0s!>iLb=C64UKIe#F#i1Ek=RxitjCF>HlPH$vn$aNOW6`6e1ih+TF!PC{xWt~$( F69B9EN^SrE literal 0 HcmV?d00001 diff --git a/arm9/gfx/picturesIcon.grit b/arm9/gfx/picturesIcon.grit new file mode 100644 index 0000000..9392dda --- /dev/null +++ b/arm9/gfx/picturesIcon.grit @@ -0,0 +1,7 @@ +# tile format +-gt + +# graphics bit depth is 4 (16 color) +-gB4 + +-p! \ No newline at end of file diff --git a/arm9/gfx/picturesIcon.png b/arm9/gfx/picturesIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..a4f442b6e2d61167b57ec5c38c381928abd92537 GIT binary patch literal 215 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdrU~ojgN0I5=)U^nbv>z+e#I z6XMFiz#u3nsHCK1Y;5e};u0DfnwFMUUS8hO(J^!8%vGya?cTln%$YNH@7{g)?%n_Y z|EmQgKz0Osx;TbZ%t=l-z##Mg;DW}+c!ndXe-0erUsP$#CMw+F;$dKLk5-)IWp*fs Qfq{X+)78&qol`;+0HYsMpa1{> literal 0 HcmV?d00001 diff --git a/arm9/gfx/recentIcon.grit b/arm9/gfx/recentIcon.grit new file mode 100644 index 0000000..9392dda --- /dev/null +++ b/arm9/gfx/recentIcon.grit @@ -0,0 +1,7 @@ +# tile format +-gt + +# graphics bit depth is 4 (16 color) +-gB4 + +-p! \ No newline at end of file diff --git a/arm9/gfx/recentIcon.png b/arm9/gfx/recentIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..483f1c57f51dc4428db1b48546e248b85a13e43d GIT binary patch literal 222 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdrU~ojgN0I5=)U^nbv>z+e#I z6XFU|BOsutsAyz+e#I z6XFU|BOsutsAy(-_uXzxEth0wzI>XAjqO_{7tb7NiC=jJ z1^x*MHD@+@?mp<((D+bsJ=0}YOEwl}J=Tyr++jS|`OF0pSq}wqFfi0Vlheq%qg%+p Pz`)??>gTe~DWM4fqIXL> literal 0 HcmV?d00001 diff --git a/arm9/gfx/smallHeartIcon.grit b/arm9/gfx/smallHeartIcon.grit new file mode 100644 index 0000000..9392dda --- /dev/null +++ b/arm9/gfx/smallHeartIcon.grit @@ -0,0 +1,7 @@ +# tile format +-gt + +# graphics bit depth is 4 (16 color) +-gB4 + +-p! \ No newline at end of file diff --git a/arm9/gfx/smallHeartIcon.png b/arm9/gfx/smallHeartIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..c56fdce5b744d5998a15b519404821ae267f98f9 GIT binary patch literal 207 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdrU~ojgN0I5=)U^nbv>z+e#I z6XFU|BOsutsAyz+e#I z6XFU|BOsutsAyzopr E0B>DTJ^%m! literal 0 HcmV?d00001 diff --git a/arm9/gfx/sortNameAscendingIcon.grit b/arm9/gfx/sortNameAscendingIcon.grit new file mode 100644 index 0000000..9392dda --- /dev/null +++ b/arm9/gfx/sortNameAscendingIcon.grit @@ -0,0 +1,7 @@ +# tile format +-gt + +# graphics bit depth is 4 (16 color) +-gB4 + +-p! \ No newline at end of file diff --git a/arm9/gfx/sortNameAscendingIcon.png b/arm9/gfx/sortNameAscendingIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..87340c4e1238bb334adc096a760150f6dd6dc0ca GIT binary patch literal 204 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 Y^TY$}1zXi~?}Iwfp00i_>zopr0GHMZh5!Hn literal 0 HcmV?d00001 diff --git a/arm9/gfx/unknownCover.grit b/arm9/gfx/unknownCover.grit new file mode 100644 index 0000000..9fa72ea --- /dev/null +++ b/arm9/gfx/unknownCover.grit @@ -0,0 +1,3 @@ +-gb +-gB8 +-p \ No newline at end of file diff --git a/arm9/gfx/unknownCover.png b/arm9/gfx/unknownCover.png new file mode 100644 index 0000000000000000000000000000000000000000..7759eb483bfad03c2fabce81d80d7908c6fe7b55 GIT binary patch literal 1257 zcmeAS@N?(olHy`uVBq!ia0y~yU}#`qU`XI#W?*1=w4|$=fq{X!*vT`5gM;JtL;nX1 z49pAxJ|V6QqhK@yhD8WCIXQWGc?AUpMMXs=CnslTXBQV2S65fJw6yf}^h}vDWzL*A zOP4NPyLRo?ty}l*-FxiVv2*9n?aVpO&%nSq)zif>q+-t7n}Pk!3Oo!C{{ENeb_`h7 zaPw_i;j=Q%Exo;wmA8ZUduD3i3wu}^+`uHVfPq!RfsrfVz*7cIiN{HT%s$V`d2^cl z4kT?e7$ni!3-`t#vrsDo^gjG+n`o%$|GI6~42) zaS_>hiNSVK~HS_NZg^dVsi30@n9<6h0e^H=S>4oaV%u6{1-oD!Mz+e#I z6XMFiz#u3nsHCK1Y;5e};u0DfnwFMUUS8hO(J^!8%vGya?cTln%$YNH@7{g)?%n_Y z|EmQgKz77?x;TbZ%t=mgU|9cqJCB3Ob5qu>vlWizFtV||o3`i7fdf+y z>@#Rr>~3rfZ;W*8bDZATIKQzmQ@!I!Xu<zopr0Q=5SD*ylh literal 0 HcmV?d00001 diff --git a/arm9/gfx/vGridIcon.grit b/arm9/gfx/vGridIcon.grit new file mode 100644 index 0000000..9392dda --- /dev/null +++ b/arm9/gfx/vGridIcon.grit @@ -0,0 +1,7 @@ +# tile format +-gt + +# graphics bit depth is 4 (16 color) +-gB4 + +-p! \ No newline at end of file diff --git a/arm9/gfx/vGridIcon.png b/arm9/gfx/vGridIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..e6c11e4cdce89f1e0d2aa96f67ffd4921106b473 GIT binary patch literal 179 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdrU~ojgN0I5=)U^nbv>z+e#I z6XMFiz#u3nsHCK1Y;5e};u0DfnwFMUUS8hO(J^!8%vGya?cTln%$YNH@7{g)?%n_Y z|EmQgKz8VRx;TbZ%t=l-!1m+c$G^ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "animation/Animator.h" +#include "gui/materialDesign.h" +#include "themes/material/MaterialColorSchemeFactory.h" +#include "core/math/ColorConverter.h" +#include "core/math/RgbMixer.h" +#include "gui/GraphicsContext.h" +#include "romBrowser/views/ChipView.h" +#include "picoLoaderBootstrap.h" +#include "romBrowser/DisplayMode/RomBrowserDisplayModeFactory.h" +#include "romBrowser/Theme/Material/MaterialThemeFileIconFactory.h" +#include "romBrowser/views/NdsGameDetailsBottomSheetView.h" +#include "romBrowser/views/DisplaySettingsBottomSheetView.h" +#include "bgm/AudioStreamPlayer.h" +#include "bgm/BgmService.h" +#include "themes/ThemeInfoFactory.h" +#include "themes/ThemeFactory.h" +#include "gui/Gx.h" +#include "splashTop.h" +#include "App.h" + +#define SPLASH_FRAMES 44 + +App::App(IAppSettingsService& appSettingsService, IBgmService& bgmService) + : _mainObjPltt(GFX_PLTT_OBJ_MAIN) + , _mainObjVram(GFX_OBJ_MAIN) + , _mainObjDialogVram(GFX_OBJ_MAIN, 128 * 1024) + , _subObjVram(GFX_OBJ_SUB) + , _textureVram((vu16*)0x06860000) + , _texturePaletteVram((vu16*)0x6880000) + , _mainVramContext(nullptr, &_mainObjVram, &_textureVram, &_texturePaletteVram) + , _subVramContext(nullptr, &_subObjVram, nullptr, nullptr) + , _appSettingsService(appSettingsService) + , _bgmService(bgmService) + , _inputProvider(&_inputSource) + , _inputRepeater(&_inputProvider, + InputKey::DpadLeft | InputKey::DpadRight | InputKey::DpadUp | InputKey::DpadDown, + 25, 8) + , _romBrowserController(&appSettingsService, &_ioTaskQueue, &_bgTaskQueue) + , _displaySettingsBottomSheetViewModel(&_romBrowserController) + , _romBrowserBottomScreenViewModel(&_romBrowserController) + , _dialogPresenter(&_focusManager, &_mainObjDialogVram) { } + +void App::InitVramMapping() const +{ + mem_setVramAMapping(MEM_VRAM_AB_TEX_SLOT_1); + mem_setVramBMapping(MEM_VRAM_AB_MAIN_OBJ_00000); + mem_setVramCMapping(MEM_VRAM_C_SUB_BG_00000); + mem_setVramDMapping(MEM_VRAM_D_TEX_SLOT_0); + mem_setVramEMapping(MEM_VRAM_E_TEX_PLTT_SLOT_0123); + mem_setVramFMapping(MEM_VRAM_FG_MAIN_BG_00000); + mem_setVramGMapping(MEM_VRAM_FG_MAIN_BG_04000); + mem_setVramHMapping(MEM_VRAM_H_SUB_BG_EXT_PLTT_SLOT_0123); + mem_setVramIMapping(MEM_VRAM_I_SUB_OBJ_00000); +} + +void App::DisplaySplashScreen() const +{ + dma_ntrCopy32(3, splashTopTiles, GFX_BG_SUB, splashTopTilesLen); + dma_ntrCopy32(3, splashTopMap, (u8*)GFX_BG_SUB + 0x3000, splashTopMapLen); + mem_setVramHMapping(MEM_VRAM_H_LCDC); + dma_ntrCopy32(3, splashTopPal, (void*)0x0689A000, splashTopPalLen); + mem_setVramHMapping(MEM_VRAM_H_SUB_BG_EXT_PLTT_SLOT_0123); + + VBlank::Wait(); + + sys_setMainEngineToBottomScreen(); + REG_DISPCNT_SUB = 0x40211015; + REG_BG1HOFS_SUB = 0; + REG_BG1VOFS_SUB = 0; + REG_BG1CNT_SUB = 0x0680; + REG_DISPCNT_SUB |= 1 << 9; + REG_BLDCNT_SUB = 0x3D42; + REG_BLDALPHA_SUB = 0x10; + REG_MASTER_BRIGHT_SUB = 0; +} + +void App::LoadTheme() +{ + ThemeInfoFactory themeInfoFactory; + auto themeInfo = themeInfoFactory.CreateFromThemeFolder(_appSettingsService.GetAppSettings().theme); + if (!themeInfo) + { + LOG_DEBUG("Failed to load theme '%s'. Using fallback theme.\n", _appSettingsService.GetAppSettings().theme.GetString()); + themeInfo = themeInfoFactory.CreateFallbackTheme(); + } + _theme = ThemeFactory().CreateFromThemeInfo(themeInfo.get()); + themeInfo.reset(); + _theme->LoadRomBrowserResources(_mainVramContext, _subVramContext); + _topBackground = _theme->CreateRomBrowserTopBackground(); + _topBackground->LoadResources(*_theme, _subVramContext); + _bottomBackground = _theme->CreateRomBrowserBottomBackground(); + _bottomBackground->LoadResources(*_theme, _mainVramContext); + + _materialThemeFileIconFactory = std::make_unique( + &_theme->GetMaterialColorScheme(), _theme->GetFontRepository()); +} + +void App::VCountIrq() +{ + _mainObjPltt.VCount(); +} + +void App::Run() +{ + InitVramMapping(); + DisplaySplashScreen(); + gx_init(); + + _chipViewVram = ChipView::UploadGraphics(_mainObjVram); + _iconButtonViewVram = IconButton2DView::UploadGraphics(_mainObjVram); + + mem_setVramEMapping(MEM_VRAM_E_LCDC); + _rgb6Palette.UploadGraphics(_mainVramContext); + mem_setVramEMapping(MEM_VRAM_E_TEX_PLTT_SLOT_0123); + + _dialogPresenter.InitVram(); + + LoadTheme(); + + _ioTaskQueue.StartThread(1, _ioTaskThreadStack, sizeof(_ioTaskThreadStack)); + _bgTaskQueue.StartThread(2, _bgTaskThreadStack, sizeof(_bgTaskThreadStack)); + + StoreVramState(_vramStateBeforeMakeBottomScreenView); + + _romBrowserBottomScreenView = std::make_unique( + &_romBrowserBottomScreenViewModel, + RomBrowserDisplayModeFactory().GetRomBrowserDisplayMode( + _romBrowserController.GetRomBrowserDisplaySettings().layout), + _materialThemeFileIconFactory.get(), + _theme->GetRomBrowserViewFactory(), + &_vblankTextureLoader); + _romBrowserBottomScreenView->InitVram(_mainVramContext); + + StoreVramState(_vramStateAfterMakeBottomScreenView); + + const auto& materialColorScheme = _theme->GetMaterialColorScheme(); + + auto scrimBlendColor = Rgb<8, 8, 8>( + materialColorScheme.inverseOnSurface.r + (materialColorScheme.scrim.r - materialColorScheme.inverseOnSurface.r) * 5 / 16, + materialColorScheme.inverseOnSurface.g + (materialColorScheme.scrim.g - materialColorScheme.inverseOnSurface.g) * 5 / 16, + materialColorScheme.inverseOnSurface.b + (materialColorScheme.scrim.b - materialColorScheme.inverseOnSurface.b) * 5 / 16); + + RgbMixer::MakeGradientPalette((u16*)GFX_PLTT_BG_MAIN, scrimBlendColor, materialColorScheme.GetColor(md::sys::color::surfaceContainerLow)); + + GFX_PLTT_BG_MAIN[0] = ColorConverter::ToGBGR565(materialColorScheme.inverseOnSurface); + GFX_PLTT_BG_MAIN[31] = ColorConverter::ToGBGR565(materialColorScheme.scrim); + REG_DISPCNT = 0x211F1B; + REG_BG0HOFS = 0; + REG_BG0VOFS = 0; + REG_BG0CNT = 3; + + Gx::MtxMode(GX_MTX_MODE_PROJECTION); + mtx43_t orthoMtx = + { + 2048, 0, 0, + 0, -21845, 0, + 0, 0, 4096 >> 5, + -4096, 4096, 0 + }; + Gx::MtxLoad43(&orthoMtx); + + _vcountIrqStarted = false; + rtos_disableIrqMask(RTOS_IRQ_VCOUNT); + rtos_setIrqFunc(RTOS_IRQ_VCOUNT, [] (u32 mask) { ((App*)gProcessManager.GetRunningProcess())->VCountIrq(); }); + + LOG_DEBUG("Amount of main obj vram used: %d\n", _mainObjVram.GetState()); + + _ioTaskQueue.Enqueue([this] (const vu8& cancelRequested) + { + _bgmService.StartBgmFromConfig(); + return TaskResult::Completed(); + }); + + _fadeAnimator = Animator(16, 0, 16, &md::sys::motion::easing::linear); + + MainLoop(); + + _bgmService.StopBgm(); + rtos_disableIrqMask(RTOS_IRQ_VCOUNT); + rtos_setIrqFunc(RTOS_IRQ_VCOUNT, nullptr); +} + +void App::MainLoop() +{ + bool fadeIn = true; + int fadeWaitFrames = SPLASH_FRAMES; + while (true) + { + Update(); + Draw(); + VBlank::Wait(); + VBlank(); + if (_exit) + { + bool fadeComplete = _fadeAnimator.Update(); + REG_MASTER_BRIGHT = 0x4000 | _fadeAnimator.GetValue(); + REG_MASTER_BRIGHT_SUB = 0x4000 | _fadeAnimator.GetValue(); + if (fadeComplete) + { + break; + } + } + else if (fadeIn) + { + if (fadeWaitFrames) + { + fadeWaitFrames--; + REG_BLDALPHA_SUB = 16; + REG_MASTER_BRIGHT = 0x4010; + } + else + { + bool fadeComplete = _fadeAnimator.Update(); + if (fadeComplete) + { + fadeIn = false; + REG_BLDCNT_SUB = 0; + REG_DISPCNT_SUB &= ~(1 << 9); + REG_MASTER_BRIGHT = 0; + } + else + { + int fade = _fadeAnimator.GetValue(); + REG_BLDALPHA_SUB = ((16 - fade) << 8) | fade; + REG_MASTER_BRIGHT = 0x4000 | fade; + } + } + } + } +} + +void App::Exit() +{ + _fadeAnimator.Goto(16, 16, &md::sys::motion::easing::linear); + _exit = true; +} + +void App::HandleTrigger(RomBrowserStateTrigger trigger, RomBrowserState newState) +{ + switch (trigger) + { + case RomBrowserStateTrigger::None: + case RomBrowserStateTrigger::Launch: + { + break; + } + case RomBrowserStateTrigger::ShowGameInfo: + { + HandleShowGameInfoTrigger(); + break; + } + case RomBrowserStateTrigger::HideGameInfo: + { + HandleHideGameInfoTrigger(); + break; + } + case RomBrowserStateTrigger::ShowDisplaySettings: + { + HandleShowDisplaySettingsTrigger(); + break; + } + case RomBrowserStateTrigger::HideDisplaySettings: + { + HandleHideDisplaySettingsTrigger(); + break; + } + case RomBrowserStateTrigger::Navigate: + { + HandleNavigateTrigger(); + break; + } + case RomBrowserStateTrigger::FolderLoadDone: + { + HandleFolderLoadDoneTrigger(); + break; + } + case RomBrowserStateTrigger::ChangeDisplayMode: + { + _changeDisplayMode = true; + break; + } + } +} + +void App::HandleShowGameInfoTrigger() +{ + auto gameInfoDialog = std::make_unique( + &_romBrowserController, &_theme->GetMaterialColorScheme(), _theme->GetFontRepository()); + gameInfoDialog->SetGraphics(_chipViewVram); + _dialogPresenter.ShowDialog(std::move(gameInfoDialog)); +} + +void App::HandleHideGameInfoTrigger() +{ + _dialogPresenter.CloseDialog(); + if (!_dialogPresenter.GetOldFocus()) + _romBrowserBottomScreenView->Focus(_focusManager); +} + +void App::HandleShowDisplaySettingsTrigger() +{ + auto displaySettingsDialog = std::make_unique( + &_displaySettingsBottomSheetViewModel, &_theme->GetMaterialColorScheme(), _theme->GetFontRepository()); + displaySettingsDialog->SetGraphics(_iconButtonViewVram); + _dialogPresenter.ShowDialog(std::move(displaySettingsDialog)); +} + +void App::HandleHideDisplaySettingsTrigger() +{ + _dialogPresenter.CloseDialog(); + if (!_dialogPresenter.GetOldFocus()) + _romBrowserBottomScreenView->Focus(_focusManager); +} + +void App::HandleNavigateTrigger() +{ + if (!_romBrowserBottomScreenView->IsAppBarFocused(_focusManager)) + _focusManager.Unfocus(); +} + +void App::HandleFolderLoadDoneTrigger() +{ + _romBrowserTopScreenView.reset(); + RestoreVramState(_vramStateAfterMakeBottomScreenView); + auto displayMode = RomBrowserDisplayModeFactory().GetRomBrowserDisplayMode( + _romBrowserController.GetRomBrowserDisplaySettings().layout); + _romBrowserTopScreenView = std::make_unique( + _romBrowserController.GetRomBrowserViewModel(), + displayMode, + _materialThemeFileIconFactory.get(), + _theme->GetRomBrowserViewFactory()); + _romBrowserTopScreenView->InitVram(_subVramContext); + _romBrowserBottomScreenView->RomBrowserViewModelInvalidated(_mainVramContext); + if (!_focusManager.GetCurrentFocus()) + _romBrowserBottomScreenView->Focus(_focusManager); +} + +void App::HandleChangeDisplayModeTrigger(RomBrowserState newState) +{ + _dialogPresenter.ClearOldFocus(); + RestoreVramState(_vramStateBeforeMakeBottomScreenView); + auto displayMode = RomBrowserDisplayModeFactory().GetRomBrowserDisplayMode( + _romBrowserController.GetRomBrowserDisplaySettings().layout); + _romBrowserBottomScreenView = std::make_unique( + &_romBrowserBottomScreenViewModel, + displayMode, + _materialThemeFileIconFactory.get(), + _theme->GetRomBrowserViewFactory(), + &_vblankTextureLoader); + _romBrowserBottomScreenView->InitVram(_mainVramContext); + StoreVramState(_vramStateAfterMakeBottomScreenView); + _romBrowserTopScreenView = std::make_unique( + _romBrowserController.GetRomBrowserViewModel(), + displayMode, + _materialThemeFileIconFactory.get(), + _theme->GetRomBrowserViewFactory()); + _romBrowserTopScreenView->InitVram(_subVramContext); + _romBrowserBottomScreenView->RomBrowserViewModelInvalidated(_mainVramContext); + if (newState == RomBrowserState::Browser) + _romBrowserBottomScreenView->Focus(_focusManager); +} + +bool App::IsRomBrowserVisible() const +{ + const auto& stateMachine = _romBrowserController.GetStateMachine(); + auto curState = stateMachine.GetCurrentState(); + return curState == RomBrowserState::Browser + || curState == RomBrowserState::GameInfo + || curState == RomBrowserState::DisplaySettings + || curState == RomBrowserState::Launching; +} + +void App::Update() +{ + const auto& stateMachine = _romBrowserController.GetStateMachine(); + _romBrowserController.Update(); + auto curState = stateMachine.GetCurrentState(); + if (_changeDisplayMode) + { + HandleChangeDisplayModeTrigger(curState); + _changeDisplayMode = false; + } + if (stateMachine.HasStateChanged()) + { + HandleTrigger(stateMachine.GetLastTrigger(), curState); + } + + bool isRomBrowserVisible = IsRomBrowserVisible(); + if (isRomBrowserVisible && !_exit && curState != RomBrowserState::Launching) + { + _focusManager.Update(_inputRepeater); + } + + if (_topBackground) + _topBackground->Update(); + if (_bottomBackground) + _bottomBackground->Update(); + + _dialogPresenter.Update(); + + _romBrowserBottomScreenView->Update(); + if (isRomBrowserVisible) + { + _romBrowserTopScreenView->Update(); + _romBrowserController.GetRomBrowserViewModel()->SetIconFrameCounter( + _romBrowserController.GetRomBrowserViewModel()->GetIconFrameCounter() + 1); + } +} + +void App::Draw() +{ + gx_reset(); + Gx::Viewport(0, 0, 255, 191); + Gx::MtxMode(GX_MTX_MODE_POSITION_VECTOR); + Gx::MtxIdentity(); + + GraphicsContext mainGraphicsContext + { + &_mainOam, + &_mainObjPltt, + &_rgb6Palette + }; + GraphicsContext subGraphicsContext + { + &_subOam, + &_subObjPltt, + nullptr + }; + + _mainOam.Clear(); + _subOam.Clear(); + _mainObjPltt.Reset(); + _subObjPltt.Reset(); + mainGraphicsContext.SetPriority(3); + subGraphicsContext.SetPriority(2); + + if (_topBackground) + _topBackground->Draw(subGraphicsContext); + if (_bottomBackground) + _bottomBackground->Draw(mainGraphicsContext); + + if (!_changeDisplayMode && IsRomBrowserVisible()) + { + _romBrowserTopScreenView->Draw(subGraphicsContext); + } + + _dialogPresenter.ApplyClipArea(mainGraphicsContext); + if (!_changeDisplayMode) + { + _romBrowserBottomScreenView->Draw(mainGraphicsContext); + } + mainGraphicsContext.ResetClipArea(); + + _dialogPresenter.Draw(mainGraphicsContext); + + _mainObjPltt.EndOfFrame(); + + Gx::SwapBuffers(GX_XLU_SORT_MANUAL, GX_DEPTH_MODE_Z); +} + +void App::VBlank() +{ + dma_ntrStopDirect(0); // stop hblank dma + _inputProvider.Sample(); + _inputRepeater.Update(); + _mainOam.Apply(GFX_OAM_MAIN); + _subOam.Apply(GFX_OAM_SUB); + _subObjPltt.Apply(GFX_PLTT_OBJ_SUB); + + if (!_vcountIrqStarted) + { + rtos_ackIrqMask(RTOS_IRQ_VCOUNT); + rtos_enableIrqMask(RTOS_IRQ_VCOUNT); + _vcountIrqStarted = true; + } + _mainObjPltt.VBlank(); + + if (_topBackground) + _topBackground->VBlank(); + if (_bottomBackground) + _bottomBackground->VBlank(); + + _dialogPresenter.VBlank(); + + if (IsRomBrowserVisible()) + { + _romBrowserTopScreenView->VBlank(); + } + _romBrowserBottomScreenView->VBlank(); + + _vblankTextureLoader.VBlank(); +} + +void App::StoreVramState(VramState& vramState) const +{ + vramState._mainObjVramState = _mainObjVram.GetState(); + vramState._texVramState = _textureVram.GetState(); + vramState._texPlttVramState = _texturePaletteVram.GetState(); + vramState._subObjVramState = _subObjVram.GetState(); +} + +void App::RestoreVramState(const VramState& vramState) +{ + _mainObjVram.SetState(vramState._mainObjVramState); + _textureVram.SetState(vramState._texVramState); + _texturePaletteVram.SetState(vramState._texPlttVramState); + _subObjVram.SetState(vramState._subObjVramState); +} diff --git a/arm9/source/App.h b/arm9/source/App.h new file mode 100644 index 0000000..fe7ced3 --- /dev/null +++ b/arm9/source/App.h @@ -0,0 +1,129 @@ +#pragma once +#include "common.h" +#include +#include "services/settings/IAppSettingsService.h" +#include "bgm/IBgmService.h" +#include "services/process/IProcess.h" +#include "gui/SimplePaletteManager.h" +#include "gui/AdvancedPaletteManager.h" +#include "gui/OamManager.h" +#include "gui/VramContext.h" +#include "gui/AscendingStackVramManager.h" +#include "gui/DescendingStackVramManager.h" +#include "material/scheme/scheme.h" +#include "gui/input/PadInputSource.h" +#include "gui/input/SampledInputProvider.h" +#include "gui/input/InputRepeater.h" +#include "gui/VBlankTextureLoader.h" +#include "gui/Rgb6Palette.h" +#include "core/task/TaskQueue.h" +#include "themes/material/MaterialColorScheme.h" +#include "romBrowser/viewModels/RomBrowserBottomScreenViewModel.h" +#include "romBrowser/viewModels/DisplaySettingsViewModel.h" +#include "romBrowser/views/RomBrowserBottomScreenView.h" +#include "romBrowser/views/RomBrowserTopScreenView.h" +#include "romBrowser/views/IconButton2DView.h" +#include "romBrowser/views/ChipView.h" +#include "romBrowser/Theme/Material/MaterialThemeFileIconFactory.h" +#include "romBrowser/RomBrowserController.h" +#include "DialogPresenter.h" +#include "themes/ITheme.h" +#include "core/SharedPtr.h" +#include "animation/Animator.h" + +class alignas(32) App : public IProcess +{ +public: + App(IAppSettingsService& appSettingsService, IBgmService& bgmService); + + void Run() override; + void Exit() override; + +private: + struct VramState + { + u32 _subObjVramState; + u32 _mainObjVramState; + u32 _texVramState; + u32 _texPlttVramState; + }; + + AdvancedPaletteManager<64> _mainObjPltt; + OamManager _mainOam; + AscendingStackVramManager _mainObjVram; + DescendingStackVramManager _mainObjDialogVram; + OamManager _subOam; + SimplePaletteManager _subObjPltt; + AscendingStackVramManager _subObjVram; + AscendingStackVramManager _textureVram; + AscendingStackVramManager _texturePaletteVram; + VBlankTextureLoader _vblankTextureLoader; + VramContext _mainVramContext; + VramContext _subVramContext; + Rgb6Palette _rgb6Palette; + Animator _fadeAnimator; + + TaskQueue<32, 32> _ioTaskQueue; + u32 _ioTaskThreadStack[2048 / 4]; + TaskQueue<32, 32> _bgTaskQueue; + u32 _bgTaskThreadStack[2048 / 4]; + + std::unique_ptr _theme; + std::unique_ptr _topBackground; + std::unique_ptr _bottomBackground; + + IAppSettingsService& _appSettingsService; + IBgmService& _bgmService; + volatile bool _exit = false; + + PadInputSource _inputSource; + SampledInputProvider _inputProvider; + InputRepeater _inputRepeater; + + std::unique_ptr _romBrowserBottomScreenView; + std::unique_ptr _romBrowserTopScreenView; + + RomBrowserController _romBrowserController; + + DisplaySettingsViewModel _displaySettingsBottomSheetViewModel; + + FocusManager _focusManager; + + std::unique_ptr _materialThemeFileIconFactory; + + RomBrowserBottomScreenViewModel _romBrowserBottomScreenViewModel; + + DialogPresenter _dialogPresenter; + + VramState _vramStateBeforeMakeBottomScreenView; + VramState _vramStateAfterMakeBottomScreenView; + bool _changeDisplayMode = false; + + ChipView::VramToken _chipViewVram; + IconButton2DView::VramToken _iconButtonViewVram; + + bool _vcountIrqStarted = false; + + void InitVramMapping() const; + void DisplaySplashScreen() const; + void LoadTheme(); + void VCountIrq(); + void HandleTrigger(RomBrowserStateTrigger trigger, RomBrowserState newState); + void HandleShowGameInfoTrigger(); + void HandleHideGameInfoTrigger(); + void HandleShowDisplaySettingsTrigger(); + void HandleHideDisplaySettingsTrigger(); + void HandleNavigateTrigger(); + void HandleFolderLoadDoneTrigger(); + void HandleChangeDisplayModeTrigger(RomBrowserState newState); + + bool IsRomBrowserVisible() const; + + void MainLoop(); + void Update(); + void Draw(); + void VBlank(); + + void StoreVramState(VramState& vramState) const; + void RestoreVramState(const VramState& vramState); +}; \ No newline at end of file diff --git a/arm9/source/DialogPresenter.cpp b/arm9/source/DialogPresenter.cpp new file mode 100644 index 0000000..263ea25 --- /dev/null +++ b/arm9/source/DialogPresenter.cpp @@ -0,0 +1,155 @@ +#include "common.h" +#include +#include +#include "gui/StackVramManager.h" +#include "gui/VramContext.h" +#include "bottomSheetBg.h" +#include "scrim.h" +#include "gui/materialDesign.h" +#include "gui/GraphicsContext.h" +#include "DialogPresenter.h" + +DialogPresenter::DialogPresenter(FocusManager* focusManager, StackVramManager* vramManager) + : _focusManager(focusManager), _vramManager(vramManager) + , _scrimAnimator(0), _yAnimator(192) +{ + _baseVramState = _vramManager->GetState(); +} + +void DialogPresenter::ShowDialog(std::unique_ptr dialog) +{ + if (!_nextDialog) + _nextDialog = std::move(dialog); +} + +void DialogPresenter::CloseDialog() +{ + if (!_currentDialog || _curState != State::BottomSheetVisible) + return; + + _newState = State::BottomSheetClosing; +} + +void DialogPresenter::Update() +{ + if (_curState != _newState) + { + _curState = _newState; + switch (_curState) + { + case State::BottomSheetVisible: + { + _scrimAnimator.Goto(5, md::sys::motion::duration::short2, + &md::sys::motion::easing::linear); + _yAnimator.Goto(32, md::sys::motion::duration::long2, + &md::sys::motion::easing::emphasizedDecelerate); + _oldFocus = _focusManager->GetCurrentFocus(); + _currentDialog->Focus(*_focusManager); + break; + } + case State::BottomSheetClosing: + { + _scrimAnimator.Goto(0, md::sys::motion::duration::short4, + &md::sys::motion::easing::emphasizedAccelerate); + _yAnimator.Goto(192, md::sys::motion::duration::short4, + &md::sys::motion::easing::emphasizedAccelerate); + if (_oldFocus) + { + _focusManager->Focus(_oldFocus); + _oldFocus = nullptr; + } + break; + } + default: + { + break; + } + } + } + switch (_curState) + { + case State::Idle: + { + if (!_currentDialog && _nextDialog) + { + _currentDialog = std::move(_nextDialog); + _initVram = true; + _newState = State::BottomSheetVisible; + } + break; + } + case State::BottomSheetVisible: + { + if (!_yAnimator.IsFinished()) + _yAnimator.Update(); + break; + } + case State::BottomSheetClosing: + { + if (!_yAnimator.IsFinished()) + { + _yAnimator.Update(); + } + else + { + _newState = State::Idle; + _currentDialog.reset(); + } + break; + } + } + if (_currentDialog) + { + _currentDialog->SetPosition(_currentDialog->GetPosition().x, _yAnimator.GetValue()); + _currentDialog->Update(); + } +} + +void DialogPresenter::ApplyClipArea(GraphicsContext& graphicsContext) const +{ + if (_currentDialog) + { + graphicsContext.SetClipArea(_currentDialog->GetFullyCoveredArea(), true); + } +} + +void DialogPresenter::VBlank() +{ + REG_BG1VOFS = -_yAnimator.GetValue(); + + if (_initVram && _currentDialog) + { + _vramManager->SetState(_baseVramState); + _currentDialog->InitVram(VramContext(nullptr, _vramManager, nullptr, nullptr)); + _initVram = false; + } + + if (_currentDialog) + _currentDialog->VBlank(); + + if (!_scrimAnimator.IsFinished()) + { + _scrimAnimator.Update(); + int scrimBlend = _scrimAnimator.GetValue(); + REG_BLDALPHA = ((16 - scrimBlend) << 8) | scrimBlend; + } +} + +void DialogPresenter::InitVram() +{ + dma_ntrCopy32(3, scrimTiles, (vu8*)BG_GFX, scrimTilesLen); + dma_ntrCopy32(3, bottomSheetBgTiles, (vu8*)BG_GFX + 64, bottomSheetBgTilesLen); + dma_ntrCopy32(3, bottomSheetBgMap, (vu8*)BG_GFX + 0x4000, bottomSheetBgMapLen); + dma_ntrCopy32(3, scrimMap, (vu8*)BG_GFX + 0x5000, scrimMapLen); + + REG_BG1CNT = BG_32x64 | BG_PRIORITY_1 | BG_COLOR_16 | BG_MAP_BASE(8) | BG_TILE_BASE(0); + REG_BG1HOFS = 0; + REG_BG1VOFS = 0; + + REG_BG2CNT = BG_32x32 | BG_PRIORITY_2 | BG_COLOR_16 | BG_MAP_BASE(10) | BG_TILE_BASE(0); + REG_BG2HOFS = 0; + REG_BG2VOFS = 0; + + REG_BLDCNT = 0x3944; + REG_BLDALPHA = (16 << 8) | 0; +} \ No newline at end of file diff --git a/arm9/source/DialogPresenter.h b/arm9/source/DialogPresenter.h new file mode 100644 index 0000000..7a22c03 --- /dev/null +++ b/arm9/source/DialogPresenter.h @@ -0,0 +1,76 @@ +#pragma once +#include +#include "animation/Animator.h" +#include "gui/views/DialogView.h" + +class StackVramManager; +class FocusManager; + +/// @brief Class for displaying dialogs. +class DialogPresenter +{ +public: + DialogPresenter(FocusManager* focusManager, StackVramManager* vramManager); + + /// @brief Requests to show the given dialog. + /// @param dialog The dialog to show. + void ShowDialog(std::unique_ptr dialog); + + /// @brief Closes the current dialog. + void CloseDialog(); + + /// @brief Updates the dialog presenter. + void Update(); + + /// @brief Applies the clip area of the currently displayed dialog, + /// or does nothing if no dialog is being shown. + /// @param graphicsContext The graphics context to apply to. + void ApplyClipArea(GraphicsContext& graphicsContext) const; + + /// @brief If a dialog is currently being shown, draws the dialog. + /// @param graphicsContext The graphics context to use. + void Draw(GraphicsContext& graphicsContext) + { + if (_currentDialog) + _currentDialog->Draw(graphicsContext); + } + + /// @brief Performs vblank processes for the displayed dialog. + void VBlank(); + + /// @brief Initializes vram that is needed for showing dialogs. + void InitVram(); + + /// @brief Clears the focus that was stored when a dialog was opened. + void ClearOldFocus() + { + _oldFocus = nullptr; + } + + /// @brief Gets the focus that was stored when a dialog was opened. + /// @return The view that was focused when the current dialog was opened. + constexpr View* GetOldFocus() const + { + return _oldFocus; + } + +private: + enum class State + { + Idle, + BottomSheetVisible, + BottomSheetClosing + }; + + FocusManager* _focusManager; + StackVramManager* _vramManager; + u32 _baseVramState; + std::unique_ptr _currentDialog; + std::unique_ptr _nextDialog; + bool _initVram = false; + View* _oldFocus = nullptr; + Animator _scrimAnimator; + Animator _yAnimator; + State _curState = State::Idle; + State _newState = State::Idle; +}; diff --git a/arm9/source/PicoLoaderProcess.cpp b/arm9/source/PicoLoaderProcess.cpp new file mode 100644 index 0000000..acb281e --- /dev/null +++ b/arm9/source/PicoLoaderProcess.cpp @@ -0,0 +1,13 @@ +#include "common.h" +#include +#include "picoLoaderBootstrap.h" +#include "PicoLoaderProcess.h" + +void PicoLoaderProcess::Run() +{ + REG_MASTER_BRIGHT = 0x401F; + REG_MASTER_BRIGHT_SUB = 0x401F; + REG_DISPCNT = 0; + REG_DISPCNT_SUB = 0; + pload_start(); +} diff --git a/arm9/source/PicoLoaderProcess.h b/arm9/source/PicoLoaderProcess.h new file mode 100644 index 0000000..c83d3eb --- /dev/null +++ b/arm9/source/PicoLoaderProcess.h @@ -0,0 +1,13 @@ +#pragma once +#include "services/process/IProcess.h" + +class PicoLoaderProcess : public IProcess +{ +public: + void Run() override; + + void Exit() override + { + // This process never exits + } +}; diff --git a/arm9/source/VBlank.cpp b/arm9/source/VBlank.cpp new file mode 100644 index 0000000..1a25e7f --- /dev/null +++ b/arm9/source/VBlank.cpp @@ -0,0 +1,4 @@ +#include "common.h" +#include "VBlank.h" + +rtos_event_t VBlank::sEvent; diff --git a/arm9/source/VBlank.h b/arm9/source/VBlank.h new file mode 100644 index 0000000..9cc86ec --- /dev/null +++ b/arm9/source/VBlank.h @@ -0,0 +1,25 @@ +#pragma once +#include + +/// @brief Helper class for waiting for vblank. +class VBlank +{ +public: + static void Init() + { + rtos_createEvent(&sEvent); + } + + static void Wait(bool waitNew = true, bool clearAfter = true) + { + rtos_waitEvent(&sEvent, waitNew, clearAfter); + } + + static void NotifyIrq() + { + rtos_signalEvent(&sEvent); + } + +private: + static rtos_event_t sEvent; +}; \ No newline at end of file diff --git a/arm9/source/animation/Animator.cpp b/arm9/source/animation/Animator.cpp new file mode 100644 index 0000000..7bf5d5f --- /dev/null +++ b/arm9/source/animation/Animator.cpp @@ -0,0 +1,35 @@ +#include "common.h" +#include "core/math/fixed.h" +#include "Animator.h" + +template <> +bool Animator::Update() +{ + if (++_frame >= _duration) + { + _frame = _duration; + _value = _to; + return true; + } + + auto relativePos = _curve->Compute(_frame * _invDuration); + _value = _from + (relativePos.LongMul(_to - _from) + 0.5).Int(); + + return false; +} + +template <> +bool Animator>::Update() +{ + if (++_frame >= _duration) + { + _frame = _duration; + _value = _to; + return true; + } + + auto relativePos = _curve->Compute(_frame * _invDuration); + _value = _from + fix32<12>(relativePos.LongMul(_to - _from)); + + return false; +} diff --git a/arm9/source/animation/Animator.h b/arm9/source/animation/Animator.h new file mode 100644 index 0000000..a567550 --- /dev/null +++ b/arm9/source/animation/Animator.h @@ -0,0 +1,50 @@ +#pragma once +#include "Curve.h" + +template +class Animator +{ +public: + constexpr Animator() + : _from(), _to(), _value(), _duration(0), _frame(0), _curve(nullptr) { } + + /// @brief Constructs an Animator with the given initialValue. + /// @param initialValue The initial value for the animator. + constexpr Animator(const T& initialValue) + : _from(initialValue), _to(initialValue), _value(initialValue), _duration(0), _frame(0), _curve(nullptr) { } + + /// @brief Constructs an Animator that animates from from to to with the given duration and curve. + /// @param from The start value. + /// @param to The end value. + /// @param duration The duration of the animation. + /// @param curve The curve to use for the animation. + constexpr Animator(const T& from, const T& to, u32 duration, const Curve* curve) + : _from(from), _to(to), _value(_from), _duration(duration), _invDuration(fix32<26>(1) / duration), _frame(0), _curve(curve) { } + + bool Update(); + + constexpr const T& GetValue() const { return _value; } + constexpr const T& GetTargetValue() const { return _to; } + constexpr u32 GetDuration() const { return _duration; } + constexpr u32 GetFrame() const { return _frame; } + constexpr bool IsFinished() const { return _frame == _duration; } + + /// @brief Restarts this animator from the current value to a new target + /// with the given duration and curve. + /// @param target The new target value. + /// @param duration The duration of the animation. + /// @param curve The curve to use. + void Goto(const T& target, u32 duration, const Curve* curve) + { + *this = Animator(_value, target, duration, curve); + } + +private: + T _from; + T _to; + T _value; + u32 _duration; + fix32<26> _invDuration; + u32 _frame; + const Curve* _curve; +}; diff --git a/arm9/source/animation/CubicBezierCurve.h b/arm9/source/animation/CubicBezierCurve.h new file mode 100644 index 0000000..e1d7b17 --- /dev/null +++ b/arm9/source/animation/CubicBezierCurve.h @@ -0,0 +1,48 @@ +#pragma once +#include "Curve.h" + +class CubicBezierCurve : public Curve +{ + using fx = fix16<13>; + + const fx _cx; + const fx _bx; + const fx _ax; + + const fx _cy; + const fx _by; + const fx _ay; + + [[gnu::noinline]] + static constexpr fix32<26> SampleCurve(fx ax, fx bx, fx cx, fx t) + { + return ((ax * t + bx) * t + cx).LongMul(t); + } + +public: + constexpr CubicBezierCurve(fx p1x, fx p1y, fx p2x, fx p2y) + : _cx(3 * p1x) + , _bx(3 * (p2x - p1x) - _cx) + , _ax(1 - _cx - _bx) + , _cy(3 * p1y) + , _by(3 * (p2y - p1y) - _cy) + , _ay(1 - _cy - _by) { } + + [[gnu::noinline]] + fix32<26> Compute(fix32<26> t) const override + { + fx start = 0.0; + fx end = 1.0; + while (true) + { + const fx midpoint = (start + end) >> 1; + const fix32<26> estimate = SampleCurve(_ax, _bx, _cx, midpoint); + if ((t - estimate).Abs() < 0.001) + return SampleCurve(_ay, _by, _cy, midpoint); + if (estimate < t) + start = midpoint; + else + end = midpoint; + } + } +}; \ No newline at end of file diff --git a/arm9/source/animation/Curve.h b/arm9/source/animation/Curve.h new file mode 100644 index 0000000..a3dc035 --- /dev/null +++ b/arm9/source/animation/Curve.h @@ -0,0 +1,8 @@ +#pragma once +#include "common.h" + +class Curve +{ +public: + virtual fix32<26> Compute(fix32<26> t) const = 0; +}; \ No newline at end of file diff --git a/arm9/source/animation/Interpolator.h b/arm9/source/animation/Interpolator.h new file mode 100644 index 0000000..ed48c97 --- /dev/null +++ b/arm9/source/animation/Interpolator.h @@ -0,0 +1,20 @@ +#pragma once +#include "common.h" + +class Interpolator +{ +public: + template + static constexpr T InterpolateLinear(T a, T b, T t) + { + return a + (b - a) * t; + } + + template + static constexpr fixed InterpolateCubic( + fixed cf0, fixed cf1, + fixed cf2, fixed cf3, fixed t) + { + return ((((cf0 * t + cf1) * t) + cf2) * t) + cf3; + } +}; \ No newline at end of file diff --git a/arm9/source/animation/LinearCurve.h b/arm9/source/animation/LinearCurve.h new file mode 100644 index 0000000..49b9ff8 --- /dev/null +++ b/arm9/source/animation/LinearCurve.h @@ -0,0 +1,11 @@ +#pragma once +#include "Curve.h" + +class LinearCurve : public Curve +{ +public: + fix32<26> Compute(fix32<26> t) const override + { + return t; + } +}; \ No newline at end of file diff --git a/arm9/source/animation/ThreePointCubicBezierCurve.h b/arm9/source/animation/ThreePointCubicBezierCurve.h new file mode 100644 index 0000000..f27df63 --- /dev/null +++ b/arm9/source/animation/ThreePointCubicBezierCurve.h @@ -0,0 +1,48 @@ +#pragma once +#include "Curve.h" +#include "CubicBezierCurve.h" + +class ThreePointCubicBezierCurve : public Curve +{ + using fx = fix16<13>; + + fix32<26> _midPointX; + fix32<26> _midPointY; + fix32<26> _invMidPointX; + fix32<26> _invOneMinusMidPointX; + + const CubicBezierCurve _firstCurve; + const CubicBezierCurve _secondCurve; + +public: + constexpr ThreePointCubicBezierCurve( + fx a1x, fx a1y, fx b1x, fx b1y, + fx midpointx, fx midpointy, + fx a2x, fx a2y, fx b2x, fx b2y) + : _midPointX(midpointx) + , _midPointY(midpointy) + , _invMidPointX(fix32<26>(1) / midpointx) + , _invOneMinusMidPointX(fix32<26>(1) / (1 - midpointx)) + , _firstCurve( + a1x / midpointx, + a1y / midpointy, + b1x / midpointx, + b1y / midpointy) + , _secondCurve( + (a2x - midpointx) / (1 - midpointx), + (a2y - midpointy) / (1 - midpointy), + (b2x - midpointx) / (1 - midpointx), + (b2y - midpointy) / (1 - midpointy)) { } + + [[gnu::noinline]] + fix32<26> Compute(fix32<26> t) const override + { + if (t < _midPointX) + return _firstCurve.Compute(t * _invMidPointX) * _midPointY; + else + { + fix32<26> scaledT = (t - _midPointX) * _invOneMinusMidPointX; + return _secondCurve.Compute(scaledT) * (1 - _midPointY) + _midPointY; + } + } +}; \ No newline at end of file diff --git a/arm9/source/bgm/AudioStreamPlayer.cpp b/arm9/source/bgm/AudioStreamPlayer.cpp new file mode 100644 index 0000000..fa8c241 --- /dev/null +++ b/arm9/source/bgm/AudioStreamPlayer.cpp @@ -0,0 +1,140 @@ +#include "common.h" +#include +#include +#include +#include +#include "ipcChannels.h" +#include "AudioStreamPlayer.h" + +/// @brief Hardware timer used for tracking audio blocks. +#define AUDIO_STREAM_PLAYER_TIMER 3 + +/// @brief Number of blocks that are not overwritten +/// between the read and write pointer to prevent +/// touching the current playing position. +#define AUDIO_STREAM_PLAYER_SAFETY_BLOCKS 4 + +/// @brief Priority of the stream thread. +#define AUDIO_STREAM_PLAYER_THREAD_PRIORITY 15 + +AudioStreamPlayer* AudioStreamPlayer::sCurrentPlayer = nullptr; + +AudioStreamPlayer::AudioStreamPlayer() +{ + rtos_createMutex(&_mutex); + rtos_createEvent(&_event); + DC_FlushRange(&_soundStopCmdList, sizeof(_soundStopCmdList)); +} + +bool AudioStreamPlayer::StartPlaybackIntern(std::unique_ptr audioStream) +{ + if (_isPlaying) + StopPlaybackIntern(); + + _audioStream = std::move(audioStream); + + // fill buffer + _readBlock = 0; + _writeBlock = 0; + for (u32 i = 0; i < AUDIO_STREAM_PLAYER_RING_BLOCKS - 1; i++) + { + FillRingBlock(i); + _writeBlock++; + } + + u32 rate = _audioStream->GetSampleRate(); + u32 timer = -((33513982 + rate) / (rate * 2)); + _soundStartCmdList.leftSetup.timer = timer; + _soundStartCmdList.rightSetup.timer = timer; + DC_FlushRange(&_soundStartCmdList, sizeof(_soundStartCmdList)); + + sCurrentPlayer = this; + + rtos_disableIrqMask(RTOS_IRQ_TIMER(AUDIO_STREAM_PLAYER_TIMER)); + rtos_ackIrqMask(RTOS_IRQ_TIMER(AUDIO_STREAM_PLAYER_TIMER)); + rtos_setIrqFunc(RTOS_IRQ_TIMER(AUDIO_STREAM_PLAYER_TIMER), [] (u32 irqMask) + { + u32 readBlock = sCurrentPlayer->_readBlock; + if (++readBlock == AUDIO_STREAM_PLAYER_RING_BLOCKS) + readBlock = 0; + sCurrentPlayer->_readBlock = readBlock; + rtos_signalEvent(&sCurrentPlayer->_event); + }); + tmr_configure(AUDIO_STREAM_PLAYER_TIMER, TMCNT_H_CLK_SYS_DIV_256, timer << 1, true); + + rtos_createThread(&_thread, AUDIO_STREAM_PLAYER_THREAD_PRIORITY, [] (void* arg) + { + static_cast(arg)->ThreadMain(); + }, this, _threadStack, sizeof(_threadStack)); + + tmr_start(AUDIO_STREAM_PLAYER_TIMER); + rtos_enableIrqMask(RTOS_IRQ_TIMER(AUDIO_STREAM_PLAYER_TIMER)); + + _isPlaying = true; + rtos_wakeupThread(&_thread); + ipc_sendFifoMessage(IPC_CHANNEL_SOUND, (u32)&_soundStartCmdList); + + return true; +} + +void AudioStreamPlayer::StopPlaybackIntern() +{ + if (!_isPlaying) + return; + + _isPlaying = false; + rtos_wakeupThread(&_thread); + ipc_sendFifoMessage(IPC_CHANNEL_SOUND, (u32)&_soundStopCmdList); + tmr_stop(AUDIO_STREAM_PLAYER_TIMER); + rtos_disableIrqMask(RTOS_IRQ_TIMER(AUDIO_STREAM_PLAYER_TIMER)); + rtos_joinThread(&_thread); + sCurrentPlayer = nullptr; + _audioStream.reset(); +} + +void AudioStreamPlayer::ThreadMain() +{ + do + { + bool doUpdate = true; + while(_isPlaying && doUpdate) + { + rtos_lockMutex(&_mutex); + { + u32 writeBlock = _writeBlock; + int freeBlocks = _readBlock - writeBlock - 1; + if (freeBlocks < 0) + freeBlocks += AUDIO_STREAM_PLAYER_RING_BLOCKS; + + if (freeBlocks > AUDIO_STREAM_PLAYER_SAFETY_BLOCKS) + { + FillRingBlock(writeBlock); + if (++writeBlock == AUDIO_STREAM_PLAYER_RING_BLOCKS) + writeBlock = 0; + _writeBlock = writeBlock; + } + else + { + doUpdate = false; + } + } + rtos_unlockMutex(&_mutex); + } + + if (!_isPlaying) + break; + + rtos_waitEvent(&_event, false, true); + } while(_isPlaying); +} + +void AudioStreamPlayer::FillRingBlock(u32 block) +{ + s16* blockPtrL = &_audioRingL[block][0]; + s16* blockPtrR = &_audioRingR[block][0]; + + _audioStream->ReadSamples(blockPtrL, blockPtrR, AUDIO_STREAM_PLAYER_BLOCK_SAMPLES); + + DC_FlushRange(&blockPtrL, AUDIO_STREAM_PLAYER_BLOCK_SAMPLES * sizeof(s16)); + DC_FlushRange(&blockPtrR, AUDIO_STREAM_PLAYER_BLOCK_SAMPLES * sizeof(s16)); +} diff --git a/arm9/source/bgm/AudioStreamPlayer.h b/arm9/source/bgm/AudioStreamPlayer.h new file mode 100644 index 0000000..7e9bb93 --- /dev/null +++ b/arm9/source/bgm/AudioStreamPlayer.h @@ -0,0 +1,106 @@ +#pragma once +#include "common.h" +#include +#include +#include +#include +#include <../../libtwl7/include/libtwl/sound/soundChannel.h> +#include "fat/File.h" +#include "soundIpcCommand.h" +#include "IAudioStreamPlayer.h" + +#define AUDIO_STREAM_PLAYER_RING_BLOCKS 32 +#define AUDIO_STREAM_PLAYER_BLOCK_SAMPLES 256 + +/// @brief Class implementing an audio stream player. +class alignas(32) AudioStreamPlayer : public IAudioStreamPlayer +{ +public: + AudioStreamPlayer(); + + ~AudioStreamPlayer() + { + StopPlayback(); + } + + bool StartPlayback(std::unique_ptr audioStream) override + { + bool result; + rtos_lockMutex(&_mutex); + { + result = StartPlaybackIntern(std::move(audioStream)); + } + rtos_unlockMutex(&_mutex); + return result; + } + + void StopPlayback() override + { + rtos_lockMutex(&_mutex); + { + StopPlaybackIntern(); + } + rtos_unlockMutex(&_mutex); + } + +private: + struct alignas(32) SoundStartCmdList + { + u32 cmdCount; + snd_ipc_cmd_setup_channel_t leftSetup; + snd_ipc_cmd_setup_channel_t rightSetup; + u32 startChannels; + }; + + struct alignas(32) SoundStopCmdList + { + u32 cmdCount; + u32 stopChannels; + }; + + s16 _audioRingL[AUDIO_STREAM_PLAYER_RING_BLOCKS][AUDIO_STREAM_PLAYER_BLOCK_SAMPLES] alignas(32); + s16 _audioRingR[AUDIO_STREAM_PLAYER_RING_BLOCKS][AUDIO_STREAM_PLAYER_BLOCK_SAMPLES] alignas(32); + SoundStartCmdList _soundStartCmdList alignas(32) + { + 3, + { + SND_IPC_CMD_SETUP_CHANNEL, + 0, + _audioRingL, + 0, + 0, + sizeof(_audioRingL) >> 2, + SOUNDCNT_VOLUME(127) | SOUNDCNT_MODE_LOOP | SOUNDCNT_FORMAT_PCM16 + }, + { + SND_IPC_CMD_SETUP_CHANNEL, + 1, + _audioRingR, + 0, + 0, + sizeof(_audioRingR) >> 2, + SOUNDCNT_VOLUME(127) | SOUNDCNT_PAN(127) | SOUNDCNT_MODE_LOOP | SOUNDCNT_FORMAT_PCM16 + }, + (0b11 << 8) | SND_IPC_CMD_START_CHANNELS + }; + SoundStopCmdList _soundStopCmdList alignas(32) + { + 1, + (0b11 << 8) | SND_IPC_CMD_STOP_CHANNELS + }; + u32 _threadStack[2048 / sizeof(u32)] alignas(32); + rtos_mutex_t _mutex; + rtos_thread_t _thread; + rtos_event_t _event; + volatile bool _isPlaying = false; + volatile u8 _readBlock; + volatile u8 _writeBlock; + std::unique_ptr _audioStream; + + static AudioStreamPlayer* sCurrentPlayer; + + bool StartPlaybackIntern(std::unique_ptr audioStream); + void StopPlaybackIntern(); + void ThreadMain(); + void FillRingBlock(u32 block); +}; diff --git a/arm9/source/bgm/BcstmAudioStream.cpp b/arm9/source/bgm/BcstmAudioStream.cpp new file mode 100644 index 0000000..ac92956 --- /dev/null +++ b/arm9/source/bgm/BcstmAudioStream.cpp @@ -0,0 +1,327 @@ +#include "common.h" +#include +#include +#include +#include "BcstmAudioStream.h" + +bool BcstmAudioStream::Open(const TCHAR* filePath) +{ + if (_audioFile.Open(filePath, FA_OPEN_EXISTING | FA_READ) != FR_OK) + return false; + + return TryLoadBcstm(); +} + +bool BcstmAudioStream::Open(const FastFileRef& fastFileRef) +{ + _audioFile.Open(fastFileRef, FA_OPEN_EXISTING | FA_READ); + return TryLoadBcstm(); +} + +bool BcstmAudioStream::TryLoadBcstm() +{ + if (_audioFile.CreateClusterTable(_clusterTable, sizeof(_clusterTable)) != FR_OK) + return false; + + bcstm_header_t header; + u32 bytesRead = 0; + if (_audioFile.Read(&header, sizeof(bcstm_header_t), bytesRead) != FR_OK || + bytesRead != sizeof(bcstm_header_t)) + { + return false; + } + + if (_audioFile.Seek(header.infoBlockRef.offset) != FR_OK) + return false; + + bcstm_info_t info; + if (_audioFile.Read(&info, sizeof(bcstm_info_t), bytesRead) != FR_OK || + bytesRead != sizeof(bcstm_info_t)) + { + return false; + } + + if (_audioFile.Seek(header.infoBlockRef.offset + 8 + info.streamInfoRef.offset) != FR_OK) + return false; + + if (_audioFile.Read(&_streamInfo, sizeof(bcstm_info_stream_t), bytesRead) != FR_OK || + bytesRead != sizeof(bcstm_info_stream_t)) + { + return false; + } + + _dataOffset = header.dataBlockRef.offset + 8 + _streamInfo.dataRef.offset; + + _channels = std::min(_streamInfo.nrChannels, 2); + + if (_streamInfo.format == BCSTM_FORMAT_DSP_ADPCM) + { + u32 tableSize = sizeof(bcstm_ref_table_t) + sizeof(bcstm_ref_t) * (_streamInfo.nrChannels - 1); + auto channelInfoRefTab = std::unique_ptr(new u8[tableSize]); + + if (_audioFile.Seek(header.infoBlockRef.offset + 8 + info.channelInfoRef.offset) != FR_OK) + return false; + + if (_audioFile.Read(channelInfoRefTab.get(), tableSize, bytesRead) != FR_OK || + bytesRead != tableSize) + { + return false; + } + + auto channelInfoRefTabPtr = reinterpret_cast(channelInfoRefTab.get()); + for (u32 i = 0; i < _channels; i++) + { + u32 offset = header.infoBlockRef.offset + 8 + info.channelInfoRef.offset + channelInfoRefTabPtr->references[i].offset; + if (_audioFile.Seek(offset) != FR_OK) + return false; + + bcstm_info_channel_t channel; + if (_audioFile.Read(&channel, sizeof(bcstm_info_channel_t), bytesRead) != FR_OK || + bytesRead != sizeof(bcstm_info_channel_t)) + { + return false; + } + + if (_audioFile.Seek(offset + channel.codecInfoRef.offset) != FR_OK) + return false; + + if (_audioFile.Read(&_dspAdpcmInfo[i], sizeof(bcstm_dspadpcm_t), bytesRead) != FR_OK || + bytesRead != sizeof(bcstm_dspadpcm_t)) + { + return false; + } + } + } + + for (u32 i = 0; i < _channels; i++) + { + _adpcmBlocks[i] = std::unique_ptr(new(cache_align) u8[_streamInfo.blockSize]); + if (_streamInfo.format == BCSTM_FORMAT_DSP_ADPCM) + { + _dspAdpcmContexts[i].coefTable = _dspAdpcmInfo[i].coefs; + _dspAdpcmContexts[i].frameData[12] = _dspAdpcmInfo[i].context.last2; + _dspAdpcmContexts[i].frameData[13] = _dspAdpcmInfo[i].context.last1; + } + } + + _blockNumber = 0; + _sampleNumberInBlock = 0; + + if (_streamInfo.loop) + { + _loopBlockNumber = _streamInfo.loopStart / _streamInfo.blockSampleCount; + _loopBlockStartSample = _streamInfo.loopStart % _streamInfo.blockSampleCount; + + _loopEndBlockNumber = (_streamInfo.loopEnd - 1) / _streamInfo.blockSampleCount; + _loopEndBlockEndSample = (_streamInfo.loopEnd - 1) % _streamInfo.blockSampleCount; + } + + return true; +} + +void BcstmAudioStream::Close() +{ + _audioFile.Close(); + _adpcmBlocks[0].reset(); + _adpcmBlocks[1].reset(); +} + +void BcstmAudioStream::ReadSamples(s16* left, s16* right, u32 count) +{ + u32 remaining = count; + s16* curLeft = left; + s16* curRight = right; + do + { + if (_sampleNumberInBlock == 0) + FetchBlock(); + + u32 totalSamplesInBlock = GetTotalSamplesInCurrentBlock(); + u32 samplesToDecode = std::min(totalSamplesInBlock - _sampleNumberInBlock, remaining); + DecodeAdpcmSamples(_dspAdpcmContexts[0], curLeft, samplesToDecode); + if (_channels == 2) + DecodeAdpcmSamples(_dspAdpcmContexts[1], curRight, samplesToDecode); + curLeft += samplesToDecode; + curRight += samplesToDecode; + remaining -= samplesToDecode; + _sampleNumberInBlock += samplesToDecode; + if (_sampleNumberInBlock == totalSamplesInBlock) + { + _blockNumber++; + _sampleNumberInBlock = 0; + } + } while (remaining != 0); + + // when mono, copy the samples from the left to the right channel + if (_channels == 1) + memcpy(right, left, count * sizeof(s16)); +} + +u32 BcstmAudioStream::GetTotalSamplesInCurrentBlock() +{ + u32 totalSamplesInBlock = _blockNumber == _streamInfo.nrBlocks - 1 ? _streamInfo.lastBlockSampleCount : _streamInfo.blockSampleCount; + if (_streamInfo.loop && _blockNumber == _loopEndBlockNumber) + totalSamplesInBlock = _loopEndBlockEndSample + 1; + return totalSamplesInBlock; +} + +void BcstmAudioStream::FetchBlock() +{ + bool looped = false; + if (_blockNumber == _streamInfo.nrBlocks || (_streamInfo.loop && _blockNumber == _loopEndBlockNumber + 1)) + { + looped = true; + _blockNumber = _streamInfo.loop ? _loopBlockNumber : 0; + } + + // fetch block + u32 blockOffset = _dataOffset + _streamInfo.blockSize * (_blockNumber * _streamInfo.nrChannels); + if (_audioFile.Seek(blockOffset) != FR_OK) + return; + + for (u32 i = 0; i < _channels; i++) + { + u32 blockSize = _blockNumber == _streamInfo.nrBlocks - 1 + ? _streamInfo.lastBlockPaddedSize : _streamInfo.blockSize; + u32 bytesRead = 0; + if (_audioFile.Read(_adpcmBlocks[i].get(), blockSize, bytesRead) != FR_OK || + bytesRead != blockSize) + { + return; + } + _dspAdpcmContexts[i].SetData(_adpcmBlocks[i].get()); + } + + if (looped) + { + _sampleNumberInBlock = _loopBlockStartSample; + if (_streamInfo.format == BCSTM_FORMAT_DSP_ADPCM) + { + if (_blockNumber == 0) + { + for (u32 i = 0; i < _channels; i++) + { + _dspAdpcmContexts[i].frameData[12] = _dspAdpcmInfo[i].context.last2; + _dspAdpcmContexts[i].frameData[13] = _dspAdpcmInfo[i].context.last1; + } + } + else + { + u32 frameOffset = _loopBlockStartSample % 14u; + u32 frame = _loopBlockStartSample / 14u; + for (u32 i = 0; i < _channels; i++) + { + _dspAdpcmContexts[i].SetData(_adpcmBlocks[i].get() + frame * 8); + if (frameOffset != 0) + { + DecodeLoopAdpcmFrame( + _dspAdpcmContexts[i], + _dspAdpcmInfo[i].loopContext.predictorAndScale, + _dspAdpcmInfo[i].loopContext.last1, + _dspAdpcmInfo[i].loopContext.last2, + _loopBlockStartSample % 14u); + } + else + { + _dspAdpcmContexts[i].frameData[12] = _dspAdpcmInfo[i].loopContext.last2; + _dspAdpcmContexts[i].frameData[13] = _dspAdpcmInfo[i].loopContext.last1; + } + } + } + } + } +} + +extern "C" void dspadpcm_decode(DspAdpcmContext* context); + +void BcstmAudioStream::DecodeAdpcmSamples(DspAdpcmContext& context, s16* dst, u32 count) +{ + do + { + if (context.frameIdx == 0) + dspadpcm_decode(&context); + u32 toCopy = std::min(14 - context.frameIdx, count); + memcpy(dst, &context.frameData[context.frameIdx], toCopy * sizeof(s16)); + context.frameIdx += toCopy; + if (context.frameIdx == 14) + context.frameIdx = 0; + count -= toCopy; + dst += toCopy; + } while (count != 0); +} + +// ITCM_CODE void BcstmAudioStream::DecodeAdpcmFrame(DspAdpcmContext& context) +// { +// const u8* data = context.data; +// u32 scaleCoef = *data++; +// u32 scale = (scaleCoef & 0xF) + 11; +// u32 coefIdx = scaleCoef >> 4; +// s16 coef1 = context.coefTable[coefIdx * 2]; +// s16 coef2 = context.coefTable[coefIdx * 2 + 1]; +// s16 last2 = context.frameData[12]; +// s16 last1 = context.frameData[13]; + +// for (u32 j = 0; j < 7; j++) +// { +// u32 dataValue = *data++; +// int high = (int)(dataValue << 24) >> 28; +// int val = ((high << scale) + (coef1 * last1 + coef2 * last2) + 1024) >> 11; +// int sample = std::clamp(val, -0x8000, 0x7FFF); +// context.frameData[j * 2] = sample; +// last2 = last1; +// last1 = sample; + +// int low = (int)(dataValue << 28) >> 28; +// val = ((low << scale) + (coef1 * last1 + coef2 * last2) + 1024) >> 11; +// sample = std::clamp(val, -0x8000, 0x7FFF); +// context.frameData[j * 2 + 1] = sample; +// last2 = last1; +// last1 = sample; +// } + +// context.frameIdx = 0; +// context.data = data; +// } + +ITCM_CODE void BcstmAudioStream::DecodeLoopAdpcmFrame( + DspAdpcmContext& context, u32 scaleCoef, s16 last1, s16 last2, u32 sampleOffset) +{ + const u8* data = context.data; + data += 2 + (sampleOffset >> 1); + u32 scale = (scaleCoef & 0xF) + 11; + u32 coefIdx = scaleCoef >> 4; + s16 coef1 = context.coefTable[coefIdx * 2]; + s16 coef2 = context.coefTable[coefIdx * 2 + 1]; + if ((int)sampleOffset - 2 >= 0) + context.frameData[sampleOffset - 2] = last2; + if ((int)sampleOffset - 1 >= 0) + context.frameData[sampleOffset - 1] = last1; + + for (u32 j = sampleOffset; j < 14; j += 2) + { + u32 dataValue = *data++; + if ((j & 1) == 0) + { + int high = (int)(dataValue << 24) >> 28; + int val = ((high << scale) + (coef1 * last1 + coef2 * last2) + 1024) >> 11; + int sample = std::clamp(val, -0x8000, 0x7FFF); + context.frameData[j] = sample; + last2 = last1; + last1 = sample; + } + else + { + j--; + } + + int low = (int)(dataValue << 28) >> 28; + int val = ((low << scale) + (coef1 * last1 + coef2 * last2) + 1024) >> 11; + int sample = std::clamp(val, -0x8000, 0x7FFF); + context.frameData[j + 1] = sample; + last2 = last1; + last1 = sample; + } + + context.frameIdx = sampleOffset; + context.data = data; +} diff --git a/arm9/source/bgm/BcstmAudioStream.h b/arm9/source/bgm/BcstmAudioStream.h new file mode 100644 index 0000000..08129b6 --- /dev/null +++ b/arm9/source/bgm/BcstmAudioStream.h @@ -0,0 +1,66 @@ +#pragma once +#include "common.h" +#include +#include "fat/File.h" +#include "IAudioStream.h" +#include "bcstm.h" + +struct DspAdpcmContext +{ + const s16* coefTable; + const u8* data; + s16 frameData[14]; + u8 frameIdx; + + void SetData(const u8* data) + { + this->data = data; + frameIdx = 0; + } +}; + +class alignas(32) BcstmAudioStream : public IAudioStream +{ +public: + /// @brief Opens the given file. Call this function before + /// using ReadSamples. + /// @param filePath The path of the file to open. + /// @return True if the file was successfully opened, or false otherwise. + bool Open(const TCHAR* filePath); + + /// @brief Opens the given file. Call this function before + /// using ReadSamples. + /// @param filePath The path of the file to open. + /// @return True if the file was successfully opened, or false otherwise. + bool Open(const FastFileRef& fastFileRef); + + void Close() override; + void ReadSamples(s16* left, s16* right, u32 count) override; + + u32 GetSampleRate() const override + { + return _streamInfo.sampleRate; + } + +private: + File _audioFile alignas(32); + DWORD _clusterTable[1024] alignas(32); + bcstm_info_stream_t _streamInfo; + bcstm_dspadpcm_t _dspAdpcmInfo[2]; + u32 _dataOffset; + std::unique_ptr _adpcmBlocks[2]; + u32 _channels; + u32 _blockNumber; + u32 _sampleNumberInBlock; + u32 _loopBlockNumber; + u32 _loopEndBlockNumber; + u32 _loopBlockStartSample; + u32 _loopEndBlockEndSample; + DspAdpcmContext _dspAdpcmContexts[2]; + + bool TryLoadBcstm(); + u32 GetTotalSamplesInCurrentBlock(); + void FetchBlock(); + void DecodeAdpcmSamples(DspAdpcmContext& context, s16* dst, u32 count); + void DecodeLoopAdpcmFrame(DspAdpcmContext& context, u32 scaleCoef, s16 last1, s16 last2, u32 sampleOffset); +}; diff --git a/arm9/source/bgm/BgmService.cpp b/arm9/source/bgm/BgmService.cpp new file mode 100644 index 0000000..54e86af --- /dev/null +++ b/arm9/source/bgm/BgmService.cpp @@ -0,0 +1,44 @@ +#include "common.h" +#include "core/mini-printf.h" +#include "Pcm16FileAudioStream.h" +#include "BcstmAudioStream.h" +#include "romBrowser/SdFolder.h" +#include "romBrowser/SdFolderFactory.h" +#include "romBrowser/FileType/NullFileTypeProvider.h" +#include "BgmService.h" + +bool BgmService::StartBgm(const TCHAR* filePath) +{ + auto stream = std::make_unique(); + if (!stream->Open(filePath)) + return false; + + return _audioStreamPlayer->StartPlayback(std::move(stream)); +} + +void BgmService::StartBgmFromConfig() +{ + TCHAR pathBuffer[128]; + mini_snprintf(pathBuffer, sizeof(pathBuffer), "/_pico/themes/%s/bgm", _appSettingsService.GetAppSettings().theme.GetString()); + NullFileTypeProvider fileTypeProvider; + auto bgmFolder = SdFolderFactory(&fileTypeProvider).CreateFromPath(pathBuffer); + if (!bgmFolder || bgmFolder->GetFileCount() == 0) + { + StopBgm(); + return; + } + u32 bgmToPlay = _randomGenerator.NextU32(bgmFolder->GetFileCount()); + auto stream = std::make_unique(); + if (!stream->Open(bgmFolder->GetFiles()[bgmToPlay]->GetFastFileRef())) + { + StopBgm(); + return; + } + + _audioStreamPlayer->StartPlayback(std::move(stream)); +} + +void BgmService::StopBgm() +{ + _audioStreamPlayer->StopPlayback(); +} diff --git a/arm9/source/bgm/BgmService.h b/arm9/source/bgm/BgmService.h new file mode 100644 index 0000000..d39390f --- /dev/null +++ b/arm9/source/bgm/BgmService.h @@ -0,0 +1,28 @@ +#pragma once +#include +#include "IAudioStreamPlayer.h" +#include "services/settings/IAppSettingsService.h" +#include "rng/RandomGenerator.h" +#include "IBgmService.h" + +/// @brief Class implementing a background music service. +class BgmService : public IBgmService +{ +public: + constexpr BgmService( + std::unique_ptr audioStreamPlayer, + IAppSettingsService& appSettingsService, + RandomGenerator& randomGenerator) + : _audioStreamPlayer(std::move(audioStreamPlayer)) + , _appSettingsService(appSettingsService) + , _randomGenerator(randomGenerator) { } + + bool StartBgm(const TCHAR* filePath) override; + void StartBgmFromConfig() override; + void StopBgm() override; + +private: + std::unique_ptr _audioStreamPlayer; + IAppSettingsService& _appSettingsService; + RandomGenerator& _randomGenerator; +}; diff --git a/arm9/source/bgm/IAudioStream.h b/arm9/source/bgm/IAudioStream.h new file mode 100644 index 0000000..6dd1d2c --- /dev/null +++ b/arm9/source/bgm/IAudioStream.h @@ -0,0 +1,24 @@ +#pragma once + +/// @brief Interface for an audio stream. +class IAudioStream +{ +public: + virtual ~IAudioStream() = 0; + + /// @brief Returns the sample rate of the audio stream in Hertz. + /// @return The sample rate in Hertz. + virtual u32 GetSampleRate() const = 0; + + /// @brief Reads the given number of samples from the audio stream + /// to the given buffers for the left and right channel. + /// @param left The left channel buffer. + /// @param right The right channel buffer. + /// @param count The number of samples to read. + virtual void ReadSamples(s16* left, s16* right, u32 count) = 0; + + /// @brief Closes the audio stream. + virtual void Close() = 0; +}; + +inline IAudioStream::~IAudioStream() { } diff --git a/arm9/source/bgm/IAudioStreamPlayer.h b/arm9/source/bgm/IAudioStreamPlayer.h new file mode 100644 index 0000000..eaf518e --- /dev/null +++ b/arm9/source/bgm/IAudioStreamPlayer.h @@ -0,0 +1,20 @@ +#pragma once +#include +#include "IAudioStream.h" + +/// @brief Interface for playback of audio streams. +class IAudioStreamPlayer +{ +public: + virtual ~IAudioStreamPlayer() = 0; + + /// @brief Starts playback of the given audio stream. + /// @param audioStream The stream to play. + /// @return True if playback was successfully started, or false otherwise. + virtual bool StartPlayback(std::unique_ptr audioStream) = 0; + + /// @brief Stops playback of the currently playing audio stream. + virtual void StopPlayback() = 0; +}; + +inline IAudioStreamPlayer::~IAudioStreamPlayer() { } diff --git a/arm9/source/bgm/IBgmService.h b/arm9/source/bgm/IBgmService.h new file mode 100644 index 0000000..b04e0c8 --- /dev/null +++ b/arm9/source/bgm/IBgmService.h @@ -0,0 +1,22 @@ +#pragma once +#include "fat/ff.h" + +/// @brief Interface for a background music service. +class IBgmService +{ +public: + virtual ~IBgmService() = 0; + + /// @brief Starts playback of the given file. + /// @param filePath The file to play. + /// @return True if playback was successfully started, or false otherwise. + virtual bool StartBgm(const TCHAR* filePath) = 0; + + /// @brief Starts playback of the background music according to the app config. + virtual void StartBgmFromConfig() = 0; + + /// @brief If currently playing, stops playback. + virtual void StopBgm() = 0; +}; + +inline IBgmService::~IBgmService() { } diff --git a/arm9/source/bgm/Pcm16FileAudioStream.cpp b/arm9/source/bgm/Pcm16FileAudioStream.cpp new file mode 100644 index 0000000..9b77764 --- /dev/null +++ b/arm9/source/bgm/Pcm16FileAudioStream.cpp @@ -0,0 +1,26 @@ +#include "common.h" +#include +#include "Pcm16FileAudioStream.h" + +bool Pcm16FileAudioStream::Open(const TCHAR* filePath) +{ + if (_audioFile.Open(filePath, FA_OPEN_EXISTING | FA_READ) != FR_OK) + return false; + + if (_audioFile.CreateClusterTable(_clusterTable, sizeof(_clusterTable)) != FR_OK) + return false; + + return true; +} + +void Pcm16FileAudioStream::Close() +{ + _audioFile.Close(); +} + +void Pcm16FileAudioStream::ReadSamples(s16 *left, s16 *right, u32 count) +{ + u32 bytesRead = 0; + _audioFile.Read(left, count * sizeof(s16), bytesRead); + memcpy(right, left, count * sizeof(s16)); +} diff --git a/arm9/source/bgm/Pcm16FileAudioStream.h b/arm9/source/bgm/Pcm16FileAudioStream.h new file mode 100644 index 0000000..ff955d0 --- /dev/null +++ b/arm9/source/bgm/Pcm16FileAudioStream.h @@ -0,0 +1,33 @@ +#pragma once +#include "common.h" +#include "fat/File.h" +#include "IAudioStream.h" + +/// @brief Raw mono 16 bit pcm audio file stream. +/// Assumes a sample rate of 32728 Hz. +class alignas(32) Pcm16FileAudioStream : public IAudioStream +{ +public: + /// @brief Opens the given file. Call this function before + /// using ReadSamples. + /// @param filePath The path of the file to open. + /// @return True if the file was successfully opened, or false otherwise. + bool Open(const TCHAR* filePath); + + void Close() override; + void ReadSamples(s16* left, s16* right, u32 count) override; + + u32 GetSampleRate() const override + { + return 32728; + } + + ~Pcm16FileAudioStream() + { + Close(); + } + +private: + File _audioFile alignas(32); + DWORD _clusterTable[1024] alignas(32); +}; diff --git a/arm9/source/bgm/bcstm.h b/arm9/source/bgm/bcstm.h new file mode 100644 index 0000000..d2324be --- /dev/null +++ b/arm9/source/bgm/bcstm.h @@ -0,0 +1,102 @@ +#pragma once +#include "common.h" + +#define BCSTM_FORMAT_DSP_ADPCM 2 + +struct bcstm_ref_t +{ + u16 typeId; + u16 padding; + u32 offset; +}; + +struct bcstm_sized_ref_t +{ + u16 typeId; + u16 padding; + u32 offset; + u32 size; +}; + +struct bcstm_ref_table_t +{ + u32 count; + bcstm_ref_t references[1]; +}; + +struct bcstm_header_t +{ + u32 signature; + u16 endianness; + u16 headerSize; + u32 version; + u32 fileSize; + u16 nrBlocks; + u16 reserved; + bcstm_sized_ref_t infoBlockRef; + bcstm_sized_ref_t seekBlockRef; + bcstm_sized_ref_t dataBlockRef; +}; + +struct bcstm_info_stream_t +{ + u8 format; + u8 loop; + u8 nrChannels; + u8 padding; + u32 sampleRate; + u32 loopStart; + u32 loopEnd; + u32 nrBlocks; + u32 blockSize; + u32 blockSampleCount; + u32 lastBlockSize; + u32 lastBlockSampleCount; + u32 lastBlockPaddedSize; + u32 seekEntrySize; + u32 seekInterval; + bcstm_ref_t dataRef; +}; + +struct bcstm_info_channel_t +{ + bcstm_ref_t codecInfoRef; +}; + +struct bcstm_dspadpcm_context_t +{ + u8 predictorAndScale; + u8 reserved; + s16 last1; + s16 last2; +}; + +struct alignas(4) bcstm_dspadpcm_t +{ + s16 coefs[16]; + bcstm_dspadpcm_context_t context; + bcstm_dspadpcm_context_t loopContext; +}; + +struct bcstm_info_t +{ + u32 signature; + u32 sectionSize; + bcstm_ref_t streamInfoRef; + bcstm_ref_t trackInfoRef; + bcstm_ref_t channelInfoRef; +}; + +struct bcstm_seek_t +{ + u32 signature; + u32 sectionSize; + u8 seekData[1]; +}; + +struct bcstm_data_t +{ + u32 signature; + u32 sectionSize; + u8 data[1]; +}; diff --git a/arm9/source/bgm/dspAdpcm.s b/arm9/source/bgm/dspAdpcm.s new file mode 100644 index 0000000..37ccbe0 --- /dev/null +++ b/arm9/source/bgm/dspAdpcm.s @@ -0,0 +1,55 @@ +.section ".itcm", "ax" +.arm + +// r0 = context pointer +.global dspadpcm_decode +.type dspadpcm_decode, %function +dspadpcm_decode: + stmfd sp!, {r4, r5, r6, r7, r8, lr} + + mov lr, #1024 + ldr r5,= #0x7FFFFFFF + + ldmia r0!, {r1, r2} // context->coefTable, context->data + strb lr, [r0, #(36 - 8)] // context->frameIdx = 0 + add r2, r2, #8 + str r2, [r0, #(4 - 8)] // context->data += 8 + ldrb r3, [r2, #-8]! + ldr r7, [r0, #(32 - 8)] // r7 = last1_last2 + + and r8, r3, #0xF0 + ldr r6, [r1, r8, lsr #2] // r6 = coef2_coef1 + + and r1, r3, #0xF + add r1, r1, #11 // r1 = scale + + mov r4, #7 +sample_loop: + ldrsb r3, [r2, #1]! + smlatb r8, r6, r7, lr // r8 = coef2 * last2 + 1024 + smlabt r8, r6, r7, r8 // r8 += coef1 * last1 + mov r3, r3, ror #4 + add r8, r8, r3, lsl r1// r8 += nibble << scale + + mov r12, r8, lsl #5 + cmp r8, r12, asr #5 + eorne r12, r5, r8, asr #31 + + smlatt r7, r6, r7, lr + smlabt r7, r6, r12, r7 + mov r3, r3, asr #28 + add r7, r7, r3, lsl r1 + + mov r8, r7, lsl #5 + cmp r7, r8, asr #5 + eorne r8, r5, r7, asr #31 + + bic r8, r8, r5, lsr #15 + orr r7, r8, r12, lsr #16 + + str r7, [r0], #4 + + subs r4, r4, #1 + bne sample_loop + + ldmfd sp!, {r4, r5, r6, r7, r8, pc} \ No newline at end of file diff --git a/arm9/source/common.h b/arm9/source/common.h new file mode 100644 index 0000000..02c59ab --- /dev/null +++ b/arm9/source/common.h @@ -0,0 +1,31 @@ +#pragma once +#include +#include "fat/ff.h" + +#ifdef __cplusplus + +#include "core/math/fixed.h" +#include "core/math/SinTable.h" +#include "globalHeap.h" +#include "services/process/ProcessManager.h" +#include "VBlank.h" +#include "logger/ILogger.h" +#include "core/TickCounter.h" +#include "rng/RandomGenerator.h" + +extern ProcessManager gProcessManager; +extern ILogger* gLogger; +extern RandomGenerator* gRandomGenerator; + +#define MAX_COMPILED_LOG_LEVEL LogLevel::All + +#define LOG_FATAL(...) if (LogLevel::Fatal < MAX_COMPILED_LOG_LEVEL) gLogger->Log(LogLevel::Fatal, __VA_ARGS__) +#define LOG_ERROR(...) if (LogLevel::Error < MAX_COMPILED_LOG_LEVEL) gLogger->Log(LogLevel::Error, __VA_ARGS__) +#define LOG_WARNING(...) if (LogLevel::Warning < MAX_COMPILED_LOG_LEVEL) gLogger->Log(LogLevel::Warning, __VA_ARGS__) +#define LOG_INFO(...) if (LogLevel::Info < MAX_COMPILED_LOG_LEVEL) gLogger->Log(LogLevel::Info, __VA_ARGS__) +#define LOG_DEBUG(...) if (LogLevel::Debug < MAX_COMPILED_LOG_LEVEL) gLogger->Log(LogLevel::Debug, __VA_ARGS__) +#define LOG_TRACE(...) if (LogLevel::Trace < MAX_COMPILED_LOG_LEVEL) gLogger->Log(LogLevel::Trace, __VA_ARGS__) + +#endif + +extern FATFS gFatFs; diff --git a/arm9/source/core/BitVector.h b/arm9/source/core/BitVector.h new file mode 100644 index 0000000..0189da6 --- /dev/null +++ b/arm9/source/core/BitVector.h @@ -0,0 +1,53 @@ +#pragma once +#include + +template +class BitVector +{ +public: + BitVector() + { + Clear(); + } + + u32 Get(u32 idx) const + { + return (_flags[idx >> 5] >> (idx & 0x1F)) & 1; + } + + void Set(u32 idx, u32 value) + { + u32 f = _flags[idx >> 5]; + if (value) + f |= 1 << (idx & 0x1F); + else + f &= ~(1 << (idx & 0x1F)); + _flags[idx >> 5] = f; + } + + void Clear() + { + _flags.fill(0); + } + + [[gnu::noinline]] + constexpr int FindFirstZero() + { + u32 globalBitNr = 0; + for (const u32 bits : _flags) + { + if (bits == ~0u) + { + globalBitNr += 32; + continue; + } + u32 invBits = ~bits; + globalBitNr += 31 - __builtin_clz(-invBits & invBits); + return globalBitNr >= Length ? -1 : globalBitNr; + } + return -1; + } + +private: + std::array _flags; +}; \ No newline at end of file diff --git a/arm9/source/core/Environment.cpp b/arm9/source/core/Environment.cpp new file mode 100644 index 0000000..e53a1bb --- /dev/null +++ b/arm9/source/core/Environment.cpp @@ -0,0 +1,64 @@ +#include +#include "picoAgbAdapter.h" +#include "Environment.h" + +u32 Environment::_flags; + +static bool detectIsNitroEmulator() +{ + u32 agbMemoryAddress = *(vu32*)0x027FFF7C; + if (agbMemoryAddress < 0x08000000 || agbMemoryAddress >= 0x0A000000) + return false; + u32 monitorRomLoadAddress = *(vu32*)0x027FFF68; + if (monitorRomLoadAddress < 0x02000000 || monitorRomLoadAddress >= 0x02800000) + return false; + return true; +} + +static bool detectNocashPrintSuppport() +{ + vu8* identifier = (vu8*)0x04FFFA00; + // melon ds only seems to implement this for 8 bit reads + u32 nocashIdentifier = identifier[0] | (identifier[1] << 8) | (identifier[2] << 16) | (identifier[3] << 24); + return nocashIdentifier == 0x67246F6E // no$g + || nocashIdentifier == 0x6F6C656D; // melo +} + +static bool detectPicoAgbAdapter() +{ + REG_EXMEMCNT &= ~0xFF; + return PICO_AGB_IDENTIFIER == PICO_AGB_IDENTIFIER_VALUE; +} + +void Environment::Initialize() +{ + _flags = ENVIRONMENT_FLAGS_NONE; + if (isDSiMode()) + { + _flags |= ENVIRONMENT_FLAGS_DSI_MODE; + } + else + { + if (detectIsNitroEmulator()) + { + _flags |= ENVIRONMENT_FLAGS_IS_NITRO_EMULATOR; + _flags |= ENVIRONMENT_FLAGS_JTAG_SEMIHOSTING; + + REG_EXMEMCNT &= ~0xFF; + + u32 agbMemoryAddress = *(vu32*)0x027FFF7C; + if (*(vu32*)(agbMemoryAddress + 0x100) == 0x44495349) //ISID + _flags |= ENVIRONMENT_FLAGS_AGB_SEMIHOSTING; + } + else + { + if (detectPicoAgbAdapter()) + _flags |= ENVIRONMENT_FLAGS_PICO_AGB_ADAPTER; + } + } + if (!(_flags & ENVIRONMENT_FLAGS_IS_NITRO_EMULATOR)) + { + if (detectNocashPrintSuppport()) + _flags |= ENVIRONMENT_FLAGS_NOCASH_PRINT; + } +} \ No newline at end of file diff --git a/arm9/source/core/Environment.h b/arm9/source/core/Environment.h new file mode 100644 index 0000000..0a04919 --- /dev/null +++ b/arm9/source/core/Environment.h @@ -0,0 +1,32 @@ +#pragma once + +class Environment +{ + enum EnvironmentFlags : u32 + { + ENVIRONMENT_FLAGS_NONE = 0, + + ENVIRONMENT_FLAGS_DSI_MODE = (1 << 0), + ENVIRONMENT_FLAGS_NOCASH_PRINT = (1 << 1), + ENVIRONMENT_FLAGS_IS_NITRO_EMULATOR = (1 << 2), + ENVIRONMENT_FLAGS_JTAG_SEMIHOSTING = (1 << 3), + ENVIRONMENT_FLAGS_AGB_SEMIHOSTING = (1 << 4), + ENVIRONMENT_FLAGS_DLDI = (1 << 5), + ENVIRONMENT_FLAGS_ARGV = (1 << 6), + ENVIRONMENT_FLAGS_PICO_AGB_ADAPTER = (1 << 7) + }; + + static u32 _flags; + +public: + static void Initialize(); + + static inline bool IsDsiMode() { return _flags & ENVIRONMENT_FLAGS_DSI_MODE; } + static inline bool SupportsNocashPrint() { return _flags & ENVIRONMENT_FLAGS_NOCASH_PRINT; } + static inline bool IsIsNitroEmulator() { return _flags & ENVIRONMENT_FLAGS_IS_NITRO_EMULATOR; } + static inline bool SupportsJtagSemihosting() { return _flags & ENVIRONMENT_FLAGS_JTAG_SEMIHOSTING; } + static inline bool SupportsAgbSemihosting() { return _flags & ENVIRONMENT_FLAGS_AGB_SEMIHOSTING; } + static inline bool SupportsDldi() { return _flags & ENVIRONMENT_FLAGS_DLDI; } + static inline bool SupportsArgv() { return _flags & ENVIRONMENT_FLAGS_ARGV; } + static inline bool HasPicoAgbAdapter() { return _flags & ENVIRONMENT_FLAGS_PICO_AGB_ADAPTER; } +}; \ No newline at end of file diff --git a/arm9/source/core/LinkedList.h b/arm9/source/core/LinkedList.h new file mode 100644 index 0000000..af46181 --- /dev/null +++ b/arm9/source/core/LinkedList.h @@ -0,0 +1,189 @@ +#pragma once +#include "LinkedListLink.h" + +/// @brief Class implementing a linked list with the link inside the list items. +/// @tparam T The type of list items. +/// @tparam member The link inside the items type. +template +class LinkedList +{ +public: + /// @brief Linked list iterator. + class Iterator + { + LinkedListLink* _itemLink; + + public: + /// @brief Creates an iterator pointing to the given item. + /// @param itemLink The link of the item this iterator points to. + explicit Iterator(LinkedListLink* itemLink) + : _itemLink(itemLink) { } + + /// @brief Checks whether this iterator is inequal to rhs. + /// @param rhs The iterator to compare to. + /// @return True if the iterators are inequal, or false otherwise. + constexpr bool operator!=(Iterator rhs) const + { + return _itemLink != rhs._itemLink; + } + + /// @brief Gets the list item this iterator points to. + /// @return A reference to the list item. + constexpr T& operator*() const + { + return *LinkToItem(_itemLink); + } + + /// @brief Advances this iterator to the next list item. + void operator++() + { + _itemLink = _itemLink->next; + } + }; + + /// @brief Inserts the given item at the head of the list. + /// @param item The item to insert. + void InsertHead(T* item) + { + LinkedListLink* link = &(item->*member); + link->next = _head; + link->prev = nullptr; + if (_head) + { + _head->prev = link; + } + _head = link; + if (!_count++) + { + _tail = link; + } + } + + /// @brief Inserts the given item at the tail of the list. + /// @param item The item to insert. + void InsertTail(T* item) + { + LinkedListLink* link = &(item->*member); + link->next = nullptr; + link->prev = _tail; + if (_tail) + { + _tail->next = link; + } + _tail = link; + if (!_count++) + { + _head = link; + } + } + + /// @brief Inserts the given item after the other item. + /// @param item The item to insert. + /// @param after The item after which will be inserted. + void InsertAfter(T* item, T* after) + { + LinkedListLink* itemLink = &(item->*member); + LinkedListLink* afterLink = &(after->*member); + itemLink->next = afterLink->next; + itemLink->prev = afterLink; + if (afterLink->next) + { + afterLink->next->prev = itemLink; + } + _count++; + } + + /// @brief Inserts the given item before the other item. + /// @param item The item to insert. + /// @param after The item before which will be inserted. + void InsertBefore(T* item, T* before) + { + LinkedListLink* itemLink = &(item->*member); + LinkedListLink* beforeLink = &(before->*member); + itemLink->next = beforeLink; + itemLink->prev = beforeLink->prev; + if (beforeLink->prev) + { + beforeLink->prev->next = itemLink; + } + _count++; + } + + /// @brief Removes the given item from the list. + /// @param item The item to remove. + void Remove(T* item) + { + LinkedListLink* itemLink = &(item->*member); + if (!itemLink->prev) + { + _head = itemLink->next; + } + else + { + itemLink->prev->next = itemLink->next; + } + + if (!itemLink->next) + { + _tail = itemLink->prev; + } + else + { + itemLink->next->prev = itemLink->prev; + } + itemLink->prev = nullptr; + itemLink->next = nullptr; + _count--; + } + + /// @brief Gets the head of the list. + /// @return The head of the list, or null if the list is empty. + constexpr T* GetHead() const { return _head ? LinkToItem(_head) : nullptr; } + + /// @brief Gets the tail of the list. + /// @return The tail of the list, or null if the list is empty. + constexpr T* GetTail() const { return _tail ? LinkToItem(_tail) : nullptr; } + + /// @brief Gets the item that comes after the given item. + /// @param item The item to get the next for. + /// @return The next item, or null if item is the last item in the list. + constexpr T* GetNext(T* item) const + { + LinkedListLink* itemLink = &(item->*member); + return itemLink->next ? LinkToItem(itemLink->next) : nullptr; + } + + /// @brief Gets the item that comes before the given item. + /// @param item The item to get the previous for. + /// @return The previous item, or null if item is the first item in the list. + constexpr T* GetPrevious(T* item) const + { + LinkedListLink* itemLink = &(item->*member); + return itemLink->prev ? LinkToItem(itemLink->prev) : nullptr; + } + + /// @brief Gets the amount of items in the list. + /// @return The amount of items in the list. + constexpr u32 GetCount() const { return _count; } + + /// @brief Gets an iterator to the start of the list. + /// @return An iterator to start of the list. + constexpr Iterator begin() const { return Iterator(_head); } + + /// @brief Gets an iterator to the end of the list. + /// @return An iterator to end of the list. + constexpr Iterator end() const { return Iterator(nullptr); } + +private: + LinkedListLink* _head = nullptr; + LinkedListLink* _tail = nullptr; + u32 _count = 0; + + /// @brief Converts a list link pointer to an item pointer. + /// @param link The list link pointer. + /// @return The item pointer. + static constexpr T* LinkToItem(LinkedListLink* link) + { + return (T*)(((u8*)link)-((size_t)&(((T*)nullptr)->*member))); + } +}; \ No newline at end of file diff --git a/arm9/source/core/LinkedListLink.h b/arm9/source/core/LinkedListLink.h new file mode 100644 index 0000000..b1afb73 --- /dev/null +++ b/arm9/source/core/LinkedListLink.h @@ -0,0 +1,11 @@ +#pragma once + +/// @brief Link for a linked list. +struct LinkedListLink +{ + /// @brief Pointer to the next list link in the list. + LinkedListLink* next = nullptr; + + /// @brief Pointer to the previous list link in the list. + LinkedListLink* prev = nullptr; +}; diff --git a/arm9/source/core/SharedPtr.h b/arm9/source/core/SharedPtr.h new file mode 100644 index 0000000..839839e --- /dev/null +++ b/arm9/source/core/SharedPtr.h @@ -0,0 +1,163 @@ +#pragma once + +static inline u32 arm_getCpsr() +{ + u32 cpsr; + asm volatile("mrs %0, cpsr" : "=r" (cpsr)); + return cpsr; +} + +static inline void arm_setCpsrControl(u32 cpsrControl) +{ + asm volatile("msr cpsr_c, %0" :: "r" (cpsrControl) : "cc"); +} + +static inline u32 arm_disableIrqs(void) +{ + u32 oldCpsr = arm_getCpsr(); + arm_setCpsrControl(oldCpsr | 0x80); + return oldCpsr; +} + +static inline void arm_restoreIrqs(u32 oldCpsr) +{ + arm_setCpsrControl(oldCpsr); +} + +template +class SharedPtr +{ + T* _pointer; + vu32* _refCount; + +public: + SharedPtr() + : _pointer(nullptr), _refCount(nullptr) { (void)sizeof(T); } + + explicit SharedPtr(T* pointer) + : _pointer(pointer), _refCount(pointer ? new u32(1) : nullptr) { (void)sizeof(T); } + + SharedPtr(const SharedPtr& other) + { + u32 irq = arm_disableIrqs(); + _pointer = other._pointer; + _refCount = other._refCount; + if (_pointer) + { + (*_refCount)++; + } + arm_restoreIrqs(irq); + } + + SharedPtr(SharedPtr&& other) + : _pointer(other._pointer), _refCount(other._refCount) + { + other._pointer = nullptr; + other._refCount = nullptr; + } + + ~SharedPtr() + { + Reset(); + } + + [[gnu::noinline]] + SharedPtr& operator=(const SharedPtr& other) + { + u32 irq = arm_disableIrqs(); + T* pointer = _pointer; + if (pointer) + { + vu32* refCount = _refCount; + u32 newValue = *refCount - 1; + *refCount = newValue; + _pointer = other._pointer; + _refCount = other._refCount; + if (_pointer) + { + (*_refCount)++; + } + arm_restoreIrqs(irq); + if (newValue == 0) + { + delete pointer; + delete refCount; + } + } + else + { + _pointer = other._pointer; + _refCount = other._refCount; + if (_pointer) + { + (*_refCount)++; + } + arm_restoreIrqs(irq); + } + return *this; + } + + [[gnu::noinline]] + SharedPtr& operator=(SharedPtr&& other) + { + u32 irq = arm_disableIrqs(); + T* pointer = _pointer; + if (pointer) + { + vu32* refCount = _refCount; + u32 newValue = *refCount - 1; + *refCount = newValue; + _pointer = other._pointer; + _refCount = other._refCount; + other._pointer = nullptr; + other._refCount = nullptr; + arm_restoreIrqs(irq); + if (newValue == 0) + { + delete pointer; + delete refCount; + } + } + else + { + _pointer = other._pointer; + _refCount = other._refCount; + other._pointer = nullptr; + other._refCount = nullptr; + arm_restoreIrqs(irq); + } + return *this; + } + + [[gnu::noinline]] + void Reset() + { + u32 irq = arm_disableIrqs(); + T* pointer = _pointer; + if (pointer) + { + vu32* refCount = _refCount; + u32 newValue = *refCount - 1; + *refCount = newValue; + _pointer = nullptr; + _refCount = nullptr; + arm_restoreIrqs(irq); + if (newValue == 0) + { + delete pointer; + delete refCount; + } + } + else + { + arm_restoreIrqs(irq); + } + } + + constexpr T& operator*() const { return *_pointer; } + constexpr T* operator->() const { return _pointer; } + + constexpr T* GetPointer() const { return _pointer; } + constexpr u32 GetRefCount() const { return _refCount ? *_refCount : 0; } + constexpr bool IsValid() const { return _pointer; } +}; diff --git a/arm9/source/core/String.h b/arm9/source/core/String.h new file mode 100644 index 0000000..7130034 --- /dev/null +++ b/arm9/source/core/String.h @@ -0,0 +1,60 @@ +#pragma once +#include +#include "StringUtil.h" + +template +class String +{ + CharType _buffer[MaxLength + 1]; + +public: + String() + { + _buffer[0] = 0; + } + + template>> + String(const T* const & str) + { + StringUtil::Copy(_buffer, str, MaxLength + 1); + } + + explicit String(const char* str) + { + StringUtil::Copy(_buffer, str, MaxLength + 1); + } + + template + constexpr String(const CharType(&str)[N]) + { + if (N * sizeof(CharType) <= sizeof(_buffer)) + memcpy(_buffer, str, N * sizeof(CharType)); + else + { + memcpy(_buffer, str, MaxLength * sizeof(CharType)); + _buffer[MaxLength] = 0; + } + } + + template>> + void operator=(const T* const & str) + { + StringUtil::Copy(_buffer, str, MaxLength + 1); + } + + template + constexpr void operator=(const CharType(&str)[N]) + { + if (N * sizeof(CharType) <= sizeof(_buffer)) + memcpy(_buffer, str, N * sizeof(CharType)); + else + { + memcpy(_buffer, str, MaxLength * sizeof(CharType)); + _buffer[MaxLength] = 0; + } + } + + constexpr operator const CharType*() const { return _buffer; } + + constexpr const CharType* GetString() const { return _buffer; } +}; \ No newline at end of file diff --git a/arm9/source/core/StringUtil.cpp b/arm9/source/core/StringUtil.cpp new file mode 100644 index 0000000..f3b4c7a --- /dev/null +++ b/arm9/source/core/StringUtil.cpp @@ -0,0 +1,104 @@ +#include "common.h" +#include +#include "StringUtil.h" + +u32 StringUtil::Copy(char* dst, const char* src, u32 dstLength) +{ + if (!dst || dstLength == 0) + { + return 0; + } + + u32 i = 0; + if (src) + { + for (; i < dstLength - 1; i++) + { + char c = src[i]; + dst[i] = c; + if (c == 0) + { + return i; + } + } + } + + dst[i] = 0; + return i; +} + +u32 StringUtil::Copy(char16_t* dst, const char16_t* src, u32 dstLength) +{ + if (!dst || dstLength == 0) + { + return 0; + } + + u32 i = 0; + if (src) + { + for (; i < dstLength - 1; i++) + { + char16_t c = src[i]; + dst[i] = c; + if (c == 0) + { + return i; + } + } + } + + dst[i] = 0; + return i; +} + +u32 StringUtil::Copy(char16_t* dst, const char* src, u32 dstLength) +{ + if (!dst || dstLength == 0) + { + return 0; + } + + u32 i = 0; + if (src) + { + for (; i < dstLength - 1; i++) + { + char c0 = *src++; + + // decode UTF-8 + if ((c0 & 0x80) == 0) + { + // 1 byte + dst[i] = c0 & 0x7F; + } + else if ((c0 & 0xE0) == 0xC0) + { + // 2 bytes + char c1 = *src++; + dst[i] = ((c0 & 0x1F) << 6) | (c1 & 0x3F); + } + else if ((c0 & 0xF0) == 0xE0) + { + // 3 bytes + char c1 = *src++; + char c2 = *src++; + dst[i] = ((c0 & 0x0F) << 12) | ((c1 & 0x3F) << 6) | (c2 & 0x3F); + } + else + { + // 4 bytes; not supported + src += 3; + dst[i] = '?'; // substitute with question mark + } + + if (c0 == 0) + { + return i; + } + } + } + + dst[i] = 0; + return i; +} diff --git a/arm9/source/core/StringUtil.h b/arm9/source/core/StringUtil.h new file mode 100644 index 0000000..b09a2e0 --- /dev/null +++ b/arm9/source/core/StringUtil.h @@ -0,0 +1,32 @@ +#pragma once + +class StringUtil +{ +public: + /// @brief Copies the null terminated string in src to the + /// dst buffer of size dstSize. If dstSize > 0 the dst + /// buffer is guarenteed to be null terminated, even + /// if src is truncated to fit in dst. + /// @param dst The destination buffer. + /// @param src The source string (null terminated). + /// @param dstLength The length of the destination buffer in characters. + static u32 Copy(char* dst, const char* src, u32 dstLength); + + /// @brief Copies the null terminated string in src to the + /// dst buffer of size dstSize. If dstSize > 0 the dst + /// buffer is guarenteed to be null terminated, even + /// if src is truncated to fit in dst. + /// @param dst The destination buffer. + /// @param src The source string (null terminated). + /// @param dstLength The length of the destination buffer in char16_t characters. + static u32 Copy(char16_t* dst, const char16_t* src, u32 dstLength); + + /// @brief Copies the null terminated string in src to the + /// dst buffer of size dstSize. If dstSize > 0 the dst + /// buffer is guarenteed to be null terminated, even + /// if src is truncated to fit in dst. + /// @param dst The destination buffer. + /// @param src The source string (null terminated). + /// @param dstLength The length of the destination buffer in char16_t characters. + static u32 Copy(char16_t* dst, const char* src, u32 dstLength); +}; diff --git a/arm9/source/core/di.h b/arm9/source/core/di.h new file mode 100644 index 0000000..1b82dfd --- /dev/null +++ b/arm9/source/core/di.h @@ -0,0 +1,3362 @@ +// +// Copyright (c) 2012-2020 Kris Jusiak (kris at jusiak dot net) +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once +#if (__cplusplus < 201305L && _MSC_VER < 1900) +#error "[Boost::ext].DI requires C++14 support (Clang-3.4+, GCC-5.1+, MSVC-2015+)" +#else +#define BOOST_DI_VERSION 1'2'0 +#define BOOST_DI_NAMESPACE_BEGIN \ + namespace boost { \ + inline namespace ext { \ + namespace di { \ + inline namespace v1_2_0 { +#define BOOST_DI_NAMESPACE_END \ + } \ + } \ + } \ + } +#if !defined(BOOST_DI_CFG_DIAGNOSTICS_LEVEL) +#define BOOST_DI_CFG_DIAGNOSTICS_LEVEL 1 +#endif +#if defined(BOOST_DI_CFG_FWD) +BOOST_DI_CFG_FWD +#endif +#define __BOOST_DI_COMPILER(arg, ...) __BOOST_DI_COMPILER_IMPL(arg, __VA_ARGS__) +#define __BOOST_DI_COMPILER_IMPL(arg, ...) arg##__VA_ARGS__ +#if defined(__clang__) +#define __CLANG__ __BOOST_DI_COMPILER(__clang_major__, __clang_minor__) +#define __BOOST_DI_UNUSED __attribute__((unused)) +#define __BOOST_DI_DEPRECATED(...) [[deprecated(__VA_ARGS__)]] +#define __BOOST_DI_TYPE_WKND(T) +#define __BOOST_DI_ACCESS_WKND private +#define __BOOST_DI_VARIABLE_TEMPLATE_INIT_WKND \ + {} +#elif defined(__GNUC__) +#define __GCC__ +#define __BOOST_DI_UNUSED __attribute__((unused)) +#define __BOOST_DI_DEPRECATED(...) [[deprecated(__VA_ARGS__)]] +#define __BOOST_DI_TYPE_WKND(T) +#define __BOOST_DI_ACCESS_WKND private +#define __BOOST_DI_VARIABLE_TEMPLATE_INIT_WKND \ + {} +#elif defined(_MSC_VER) +#define __MSVC__ +#if !defined(__has_include) +#define __has_include(...) 0 +#endif +#define __BOOST_DI_UNUSED +#define __BOOST_DI_DEPRECATED(...) __declspec(deprecated(__VA_ARGS__)) +#define __BOOST_DI_TYPE_WKND(T) (T &&) +#define __BOOST_DI_ACCESS_WKND public +#define __BOOST_DI_VARIABLE_TEMPLATE_INIT_WKND +#endif +#if !defined(__has_builtin) +#define __has_builtin(...) 0 +#endif +#if !defined(__has_extension) +#define __has_extension(...) 0 +#endif +#if defined(__CLANG__) +#if (!BOOST_DI_CFG_DIAGNOSTICS_LEVEL) +#pragma clang diagnostic error "-Wdeprecated-declarations" +#else +#pragma clang diagnostic warning "-Wdeprecated-declarations" +#endif +#pragma clang diagnostic push +#pragma clang diagnostic error "-Wundefined-inline" +#pragma clang diagnostic error "-Wundefined-internal" +#pragma clang diagnostic ignored "-Wmissing-field-initializers" +#elif defined(__GCC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic error "-Wdeprecated-declarations" +#if (__GNUC__ < 6) +#pragma GCC diagnostic error "-Werror" +#endif +#elif defined(__MSVC__) +#pragma warning(disable : 4503) +#pragma warning(disable : 4822) +#pragma warning(disable : 4505) +#endif +#if defined(_LIBCPP_VERSION) +#define NAMESPACE_STD_BEGIN _LIBCPP_BEGIN_NAMESPACE_STD { +#else +#define NAMESPACE_STD_BEGIN namespace std { +#endif +#if defined(_LIBCPP_VERSION) +#define NAMESPACE_STD_END _LIBCPP_END_NAMESPACE_STD +#else +#define NAMESPACE_STD_END } +#endif +#if __has_include(<__config>) +#include <__config> +#endif +#if __has_include() +#include +#else +NAMESPACE_STD_BEGIN +template +class shared_ptr; +template +class weak_ptr; +template +class unique_ptr; +NAMESPACE_STD_END +#endif +#if __has_include() +#include +#else +NAMESPACE_STD_BEGIN +template +class vector; +NAMESPACE_STD_END +#endif +#if __has_include() +#include +#else +NAMESPACE_STD_BEGIN +template +class set; +NAMESPACE_STD_END +#endif +#if __has_include() +#include +#else +NAMESPACE_STD_BEGIN +template +class initializer_list; +NAMESPACE_STD_END +#endif +#if __has_include() +#include +#else +NAMESPACE_STD_BEGIN +template +class tuple; +NAMESPACE_STD_END +#endif +#if __has_include() +#include +#else +NAMESPACE_STD_BEGIN +template +class move_iterator; +NAMESPACE_STD_END +#endif +#if __has_include() +#include +#else +NAMESPACE_STD_BEGIN +template +struct char_traits; +NAMESPACE_STD_END +#endif +// clang-format off +#if __has_include() +// clang-format on +#include +#else +namespace boost { +template +class shared_ptr; +} +#endif +BOOST_DI_NAMESPACE_BEGIN +struct _ { + _(...) {} +}; +namespace aux { +using swallow = int[]; +template +using owner = T; +template +struct valid { + using type = int; +}; +template +using valid_t = typename valid::type; +template +struct type {}; +struct none_type {}; +template +struct non_type {}; +template +struct always { + static constexpr auto value = true; +}; +template +struct never { + static constexpr auto value = false; +}; +template +struct identity { + using type = T; +}; +template +struct type_list { + using type = type_list; +}; +template +struct bool_list { + using type = bool_list; +}; +template +struct pair { + using type = pair; + using first = T1; + using second = T2; +}; +template +struct inherit : Ts... { + using type = inherit; +}; +template +struct join { + using type = type_list<>; +}; +template +struct join { + using type = T; +}; +template +struct join, type_list, Ts...> : join, Ts...> {}; +template +struct join, type_list, type_list, type_list, type_list, type_list, + type_list, type_list, type_list, type_list, type_list, type_list, + type_list, type_list, type_list, type_list, type_list, Us...> + : join, + Us...> {}; +template +using join_t = typename join::type; +template +struct index_sequence { + using type = index_sequence; +}; +#if __has_builtin(__make_integer_seq) +template +struct integer_sequence { + using type = index_sequence; +}; +template +struct make_index_sequence_impl { + using type = typename __make_integer_seq::type; +}; +#else +template +struct build_index_sequence; +template +struct build_index_sequence, index_sequence> { + using type = index_sequence; +}; +template +struct make_index_sequence_impl { + using type = typename build_index_sequence::type, + typename make_index_sequence_impl::type>::type; +}; +template <> +struct make_index_sequence_impl<0> : index_sequence<> {}; +template <> +struct make_index_sequence_impl<1> : index_sequence<0> {}; +template <> +struct make_index_sequence_impl<2> : index_sequence<0, 1> {}; +template <> +struct make_index_sequence_impl<3> : index_sequence<0, 1, 2> {}; +template <> +struct make_index_sequence_impl<4> : index_sequence<0, 1, 2, 3> {}; +template <> +struct make_index_sequence_impl<5> : index_sequence<0, 1, 2, 3, 4> {}; +template <> +struct make_index_sequence_impl<6> : index_sequence<0, 1, 2, 3, 4, 5> {}; +template <> +struct make_index_sequence_impl<7> : index_sequence<0, 1, 2, 3, 4, 5, 6> {}; +template <> +struct make_index_sequence_impl<8> : index_sequence<0, 1, 2, 3, 4, 5, 6, 7> {}; +template <> +struct make_index_sequence_impl<9> : index_sequence<0, 1, 2, 3, 4, 5, 6, 7, 8> {}; +template <> +struct make_index_sequence_impl<10> : index_sequence<0, 1, 2, 3, 4, 5, 6, 7, 8, 9> {}; +#endif +template +using make_index_sequence = typename make_index_sequence_impl::type; +} +namespace placeholders { +__BOOST_DI_UNUSED static const struct arg { } _{}; } +template +struct named {}; +struct no_name { + constexpr auto operator()() const noexcept { return ""; } +}; +template +struct ctor_traits; +template +struct self {}; +struct ignore_policies {}; +namespace core { +template +struct any_type_fwd; +template +struct any_type_ref_fwd; +template +struct any_type_1st_fwd; +template +struct any_type_1st_ref_fwd; +struct dependency_base {}; +struct injector_base {}; +template +struct dependency__ : T { + using T::create; + using T::is_referable; + using T::try_create; +}; +template +struct injector__ : T { + using T::cfg; + using T::create_impl; + using T::create_successful_impl; +#if defined(__MSVC__) + template + using is_creatable = typename T::template is_creatable; + template + using try_create = typename T::template try_create; +#else + using T::is_creatable; + using T::try_create; +#endif +}; +template +struct array; +struct deduced {}; +struct none {}; +template +class dependency; +} +namespace scopes { +class deduce; +class instance; +class singleton; +class unique; +} +#define __BOOST_DI_REQUIRES(...) typename ::boost::ext::di::v1_2_0::aux::enable_if<__VA_ARGS__, int>::type +#define __BOOST_DI_REQUIRES_MSG(...) typename ::boost::ext::di::v1_2_0::aux::concept_check<__VA_ARGS__>::type +namespace aux { +template +T&& declval(); +template +struct integral_constant { + using type = integral_constant; + static constexpr T value = V; +}; +using true_type = integral_constant; +using false_type = integral_constant; +template +struct conditional { + using type = T; +}; +template +struct conditional { + using type = F; +}; +template +using conditional_t = typename conditional::type; +template +struct enable_if {}; +template +struct enable_if { + using type = T; +}; +template +using enable_if_t = typename enable_if::type; +template +struct concept_check { + static_assert(T::value, "constraint not satisfied"); +}; +template <> +struct concept_check { + using type = int; +}; +template +struct remove_reference { + using type = T; +}; +template +struct remove_reference { + using type = T; +}; +template +struct remove_reference { + using type = T; +}; +template +using remove_reference_t = typename remove_reference::type; +template +struct remove_pointer { + using type = T; +}; +template +struct remove_pointer { + using type = T; +}; +template +using remove_pointer_t = typename remove_pointer::type; +template +struct remove_smart_ptr { + using type = T; +}; +template +struct remove_smart_ptr> { + using type = T; +}; +template +struct remove_smart_ptr> { + using type = T; +}; +template +struct remove_smart_ptr> { + using type = T; +}; +template +struct remove_smart_ptr> { + using type = T; +}; +template +using remove_smart_ptr_t = typename remove_smart_ptr::type; +template +struct remove_qualifiers { + using type = T; +}; +template +struct remove_qualifiers { + using type = T; +}; +template +struct remove_qualifiers { + using type = T; +}; +template +struct remove_qualifiers { + using type = T; +}; +template +struct remove_qualifiers { + using type = T; +}; +template +struct remove_qualifiers { + using type = T; +}; +template +struct remove_qualifiers { + using type = T; +}; +template +struct remove_qualifiers { + using type = T; +}; +template +struct remove_qualifiers { + using type = T; +}; +template +struct remove_qualifiers { + using type = T; +}; +template +using remove_qualifiers_t = typename remove_qualifiers::type; +template +struct remove_extent { + using type = T; +}; +template +struct remove_extent { + using type = T; +}; +template +using remove_extent_t = typename remove_extent::type; +template +struct deref_type { + using type = T; +}; +template +struct deref_type> { + using type = remove_qualifiers_t::type>; +}; +template +struct deref_type> { + using type = remove_qualifiers_t::type>; +}; +template +struct deref_type> { + using type = remove_qualifiers_t::type>; +}; +template +struct deref_type> { + using type = remove_qualifiers_t::type>; +}; +template +struct deref_type> { + using type = core::array::type>>; +}; +template +struct deref_type> { + using type = core::array::type>>; +}; +template +struct deref_type> { + using type = core::array::type>>; +}; +template +using decay_t = typename deref_type>::type; +template +struct is_same : false_type {}; +template +struct is_same : true_type {}; +template +struct is_base_of : integral_constant {}; +template +struct is_class : integral_constant {}; +template +struct is_abstract : integral_constant {}; +template +struct is_polymorphic : integral_constant {}; +template +struct is_final : integral_constant {}; +template +using is_valid_expr = true_type; +#if __has_extension(is_constructible) && !((__clang_major__ == 3) && (__clang_minor__ == 5)) +template +using is_constructible = integral_constant; +#else +template +decltype(void(T(declval()...)), true_type{}) test_is_constructible(int); +template +false_type test_is_constructible(...); +template +struct is_constructible : decltype(test_is_constructible(0)) {}; +#endif +template +using is_constructible_t = typename is_constructible::type; +template +decltype(void(T{declval()...}), true_type{}) test_is_braces_constructible(int); +template +false_type test_is_braces_constructible(...); +template +using is_braces_constructible = decltype(test_is_braces_constructible(0)); +template +using is_braces_constructible_t = typename is_braces_constructible::type; +#if defined(__MSVC__) +template +struct is_copy_constructible : integral_constant {}; +template +struct is_default_constructible : integral_constant {}; +#else +template +using is_copy_constructible = is_constructible; +template +using is_default_constructible = is_constructible; +#endif +#if defined(__CLANG__) || defined(__MSVC__) +template +struct is_convertible : integral_constant {}; +#else +struct test_is_convertible__ { + template + static void test(T); +}; +template (declval()))> +true_type test_is_convertible(int); +template +false_type test_is_convertible(...); +template +using is_convertible = decltype(test_is_convertible(0)); +#endif +template > +using is_narrowed = integral_constant::value && !is_class::value && !is_same::value>; +template +struct is_array : false_type {}; +template +struct is_array : true_type {}; +template +true_type is_complete_impl(int); +template +false_type is_complete_impl(...); +template +struct is_complete : decltype(is_complete_impl(0)) {}; +template +is_base_of is_a_impl(int); +template +false_type is_a_impl(...); +template +struct is_a : decltype(is_a_impl(0)) {}; +template +struct is_unique_impl; +template +struct not_unique : false_type { + using type = not_unique; +}; +template <> +struct not_unique<> : true_type { + using type = not_unique; +}; +template +struct is_unique_impl : not_unique<> {}; +template +struct is_unique_impl + : conditional_t, T1>::value, not_unique, is_unique_impl>, Ts...>> {}; +template +using is_unique = is_unique_impl; +template +struct unique; +template +struct unique, T, Ts...> : conditional_t, inherit...>>::value, + unique, Ts...>, unique, Ts...>> {}; +template +struct unique> : type_list {}; +template +using unique_t = typename unique, Ts...>::type; +false_type has_shared_ptr__(...); +#if !defined(BOOST_DI_DISABLE_SHARED_PTR_DEDUCTION) +template +auto has_shared_ptr__(T &&) -> is_valid_expr{})>; +#endif +template +decltype(::boost::ext::di::v1_2_0::aux::declval().operator()(::boost::ext::di::v1_2_0::aux::declval()...), + ::boost::ext::di::v1_2_0::aux::true_type()) +is_invocable_impl(int); +template +::boost::ext::di::v1_2_0::aux::false_type is_invocable_impl(...); +template +struct is_invocable : decltype(is_invocable_impl(0)) {}; +struct callable_base_impl { + void operator()(...) {} +}; +template +struct callable_base : callable_base_impl, + aux::conditional_t::value && !aux::is_final::value, T, aux::none_type> {}; +template +aux::false_type is_callable_impl(T*, aux::non_type* = 0); +aux::true_type is_callable_impl(...); +template +struct is_callable : decltype(is_callable_impl((callable_base*)0)) {}; +template +struct is_empty_expr : false_type {}; +template +#if defined(__MSVC__) +struct is_empty_expr()())>> : integral_constant { +}; +#else +struct is_empty_expr()), decltype(declval()())>> : true_type { +}; +#endif +template +struct function_traits; +template +struct function_traits { + using result_type = R; + using args = type_list; +}; +template +struct function_traits { + using result_type = R; + using args = type_list; +}; +template +struct function_traits { + using result_type = R; + using args = type_list; +}; +template +struct function_traits { + using result_type = R; + using args = type_list; +}; +template +using function_traits_t = typename function_traits::args; +} +namespace core { +template ::type> +struct bindings_impl; +template +struct bindings_impl { + using type = typename T::deps; +}; +template +struct bindings_impl { + using type = aux::type_list; +}; +#if defined(__MSVC__) +template +struct bindings : aux::join_t::type...> {}; +template +using bindings_t = typename bindings::type; +#else +template +using bindings_t = aux::join_t::type...>; +#endif +} +namespace concepts { +template +struct type_ { + template + struct named { + struct is_bound_more_than_once : aux::false_type {}; + }; + struct is_bound_more_than_once : aux::false_type {}; + struct is_neither_a_dependency_nor_an_injector : aux::false_type {}; + struct has_disallowed_qualifiers : aux::false_type {}; + struct is_abstract : +#if (BOOST_DI_CFG_DIAGNOSTICS_LEVEL >= 2) + // clang-format off + decltype( + T{} + ), +// clang-format on +#endif + aux::false_type { + }; + template + struct is_not_related_to : aux::false_type {}; +}; +template +struct any_of : aux::false_type {}; +template +struct is_supported + : aux::is_same::value...>, + aux::bool_list<(aux::is_constructible::value && + (aux::is_a::value || + aux::is_a::value || aux::is_empty_expr::value))...>> { +}; +template +struct get_not_supported; +template +struct get_not_supported { + using type = T; +}; +template +struct get_not_supported + : aux::conditional::value || aux::is_a::value, + typename get_not_supported::type, T> {}; +template +struct is_unique; +template +struct unique_dependency : aux::type {}; +template +struct unique_dependency::value)> + : aux::pair, typename T::priority> {}; +template +struct is_unique> : aux::is_unique::type...> {}; +template +struct get_is_unique_error_impl : aux::true_type {}; +template +struct get_is_unique_error_impl, TPriority>>> { + using type = typename type_::template named::is_bound_more_than_once; +}; +template +struct get_is_unique_error_impl, TPriority>>> { + using type = typename type_::is_bound_more_than_once; +}; +template +struct get_is_unique_error_impl> { + using type = typename type_::is_bound_more_than_once; +}; +template +struct get_is_unique_error; +template +struct get_is_unique_error> + : get_is_unique_error_impl::type...>::type> {}; +template +using boundable_bindings = + aux::conditional_t::value, typename get_is_unique_error>::type, + typename type_::type>::is_neither_a_dependency_nor_an_injector>; +template +struct get_any_of_error : aux::conditional::value...>, + aux::bool_list::value...>>::value, + aux::true_type, any_of> {}; +template +struct is_related { + static constexpr auto value = true; +}; +template +struct is_related { + static constexpr auto value = + aux::is_base_of::value || (aux::is_convertible::value && !aux::is_narrowed::value); +}; +template +struct is_abstract { + static constexpr auto value = false; +}; +template +struct is_abstract { + static constexpr auto value = aux::is_abstract::value; +}; +auto boundable_impl(any_of<> &&) -> aux::true_type; +template +auto boundable_impl(any_of &&) + -> aux::conditional_t>::value, decltype(boundable_impl(aux::declval>())), + typename type_::has_disallowed_qualifiers>; +template +using boundable_impl__ = aux::conditional_t< + is_related::value && aux::is_complete::value, I, T>::value, + aux::conditional_t::value, T>::value, typename type_::is_abstract, aux::true_type>, + typename type_::template is_not_related_to>; +template +auto boundable_impl(I&&, T &&) -> aux::conditional_t>::value || !aux::is_complete::value, + boundable_impl__, typename type_::has_disallowed_qualifiers>; +template +auto boundable_impl(I&&, T&&, aux::valid<> &&) + -> aux::conditional_t::value && aux::is_complete::value, I, T>::value, aux::true_type, + typename type_::template is_not_related_to>; +template +auto boundable_impl(I* [], T &&) -> aux::conditional_t>::value, boundable_impl__, + typename type_::has_disallowed_qualifiers>; +template +auto boundable_impl(I[], T &&) -> aux::conditional_t>::value, boundable_impl__, + typename type_::has_disallowed_qualifiers>; +template +auto boundable_impl(aux::type_list &&) -> boundable_bindings; +template +auto boundable_impl(concepts::any_of&&, T &&) -> + typename get_any_of_error(), aux::declval()))...>::type; +template +auto boundable_impl(aux::type &&) -> typename get_is_unique_error_impl::type>::type; +aux::true_type boundable_impl(...); +template +struct boundable__ { + using type = decltype(boundable_impl(aux::declval()...)); +}; +template +using boundable = typename boundable__::type; +} +namespace type_traits { +struct stack {}; +struct heap {}; +template +struct memory_traits { + using type = stack; +}; +template +struct memory_traits { + using type = heap; +}; +template +struct memory_traits { + using type = typename memory_traits::type; +}; +template +struct memory_traits> { + using type = heap; +}; +template +struct memory_traits> { + using type = heap; +}; +template +struct memory_traits> { + using type = heap; +}; +template +struct memory_traits> { + using type = heap; +}; +template +struct memory_traits::value)> { + using type = heap; +}; +} +namespace concepts { +template +struct scope { + struct is_referable {}; + struct try_create {}; + struct create {}; + template + struct requires_ : aux::false_type {}; +}; +template +struct scope__ { + template + struct scope { + template + using is_referable = aux::true_type; + template + T try_create(const TProvider&); + template + T create(const TProvider&); + }; +}; +template +struct config__ { + template + struct scope_traits { + using type = scope__; + }; + template + struct memory_traits { + using type = type_traits::heap; + }; +}; +template +struct provider__ { + using config = config__; + template + aux::conditional_t::value, T, T*> try_get(const TMemory& = {}) const; + template + T* get(const TMemory& = {}) const { + return nullptr; + } + config& cfg() const; +}; +template +typename scope::template requires_::is_referable, typename scope<_, _>::try_create, + typename scope<_, _>::create> +scopable_impl(...); +template +auto scopable_impl(T &&) + -> aux::is_valid_expr::template is_referable<_, config__<_>>, + decltype(T::template scope<_, _>::template try_create<_, _>(provider__<_>{})), + decltype(aux::declval>().template create<_, _>(provider__<_>{}))>; +template +struct scopable__ { + using type = decltype(scopable_impl(aux::declval())); +}; +template +using scopable = typename scopable__::type; +} +namespace core { +template > +struct pool; +template +using pool_t = pool>; +template +struct pool> : TArgs... { + template + explicit pool(Ts... args) noexcept : Ts(static_cast(args))... {} + template + pool(const aux::type_list&, TPool p) noexcept : pool(static_cast(p)...) { + (void)p; + } + template + pool& operator=(T&& other) noexcept { + (void)aux::swallow{0, (static_cast(*this).operator=(static_cast(other)), 0)...}; + return *this; + } +}; +} +#if !defined(BOOST_DI_CFG_CTOR_LIMIT_SIZE) +#define BOOST_DI_CFG_CTOR_LIMIT_SIZE 10 +#endif +namespace type_traits { +template +struct is_injectable : ::boost::ext::di::v1_2_0::aux::false_type {}; +template +struct is_injectable> + : ::boost::ext::di::v1_2_0::aux::true_type {}; +struct direct {}; +struct uniform {}; +template +using get = T; +template