37 Commits

Author SHA1 Message Date
Gericom
b087565651 Update changelog 2026-04-18 19:12:03 +02:00
Gericom
281a781c13 Fix BlocksDS link in readme. Fixes #41 2026-04-18 13:28:29 +02:00
Gericom
5569c005a5 Add define for cheat up button position 2026-04-18 13:23:33 +02:00
Gericom
9f9f0143a2 Add ability to move to and from the up button in the cheats panel with the dpad, fix oopsie with cheat category name text, update Usage.md 2026-04-18 13:15:25 +02:00
Gericom
97762b14d3 Add touch input support, add fast scrolling support for coverflow display mode, fix use after free bug in banner list mode 2026-04-18 12:20:57 +02:00
Gericom
21a8790ebc Add new shared pointer and make use of it 2026-04-06 12:08:00 +02:00
Gericom
bec797ffe7 Add ability to set the position of the top screen cover image in custom themes 2026-04-04 10:01:15 +02:00
Gericom
127de36b1c Update changelog 2026-03-29 17:03:39 +02:00
Gericom
5442c02341 Add changelog 2026-03-29 16:20:27 +02:00
Gericom
cf9ce63db5 Add various extra customization options for custom themes. Fixes #40 2026-03-29 14:48:12 +02:00
Gericom
53727e5fdd Avoid having a single frame where the icon was not displayed on the top screen after selecting a different rom 2026-03-29 12:21:20 +02:00
Gericom
9ca3e38668 Enable marquee for file names on the top screen. Fixes #22 2026-03-29 11:56:13 +02:00
Gericom
3f780fdd69 Improve error handling for parsing banners. Fixes #18 2026-03-29 11:47:08 +02:00
Gericom
7c06abf224 Hide files/dirs with hidden attribute and files/dirs starting with a period. Fixes #13, fixes #23 2026-03-29 09:55:45 +02:00
Gericom
2c142caa98 Enable debug information 2026-03-29 09:41:51 +02:00
Gericom
b7d7f9f352 Change cheat implementation to show cheats in database order, fix some bugs
- AdvancedPaletteManager incorrectly handled negative y positions
- FocusManager still had a pointer to a view that was destroyed in the cheats panel. After changing focus, memory got corrupted.
2026-03-15 13:28:59 +01:00
Gericom
601fd6371e Do not attempt to draw NdsFileIcon when the vram address is not yet set 2026-03-15 10:22:33 +01:00
Gericom
a4ecea6802 Fix some bugs in the cheats panel 2026-03-14 10:55:10 +01:00
Gericom
6c34d9324d Add cheat documentation, enable input repeat for L and R, show cheat category name 2026-03-08 13:03:26 +01:00
Gericom
43b1bf7afa Fix vram corruption when using the cheat panel in vertical grid mode
The graphics of an animated nds icon are now not all uploaded to vram at the same time.
There are now 1024 bytes available for each icon (previously 4096).
Double buffering is used to upload the new icon frame every time it changes.
2026-03-08 11:32:48 +01:00
Gericom
12ebd482d4 Add option to disable all cheats by pressing X in the cheat panel 2026-03-07 14:51:58 +01:00
Gericom
4d9318b0b9 Fix label marquee speed, add cheat description label 2026-03-01 17:12:56 +01:00
Gericom
10431c4615 Remember selected index when returning from cheat category 2026-03-01 16:24:05 +01:00
Gericom
e2e42115e7 Add marquee for long cheat names 2026-03-01 16:18:06 +01:00
Gericom
a9425eea7c Add cheats not found label 2026-03-01 13:43:31 +01:00
Gericom
7f35d524ae Fix a few bugs related to the cheat panel 2026-02-28 17:32:40 +01:00
Gericom
126b898ac4 Run CI on all branches 2026-02-28 17:10:57 +01:00
Gericom
f54a379ff2 Further work on support for cheats
Cheats can now be enabled/disabled and games can be launched with cheats
2026-02-28 17:00:02 +01:00
Gericom
dddee0bb94 Initial work on implementing support for cheats 2026-02-22 20:28:35 +01:00
Gericom
f73c8b0547 Merge pull request #21 from oakwoodwolf/develop
Update Usage.md
2026-02-08 16:35:52 +01:00
Roy Flaherty
a102068433 Update Usage.md 2026-02-03 21:42:24 +00:00
Gericom
d76d46ea73 Added support for Pico Loader API v2. This makes it possible to return to Pico Launcher from homebrew. 2026-01-10 17:08:06 +01:00
Gericom
e4c2fafa74 Updated to latest blocksds (Fixes #14) 2026-01-04 11:00:33 +01:00
Gericom
e2a8a540e9 Merge pull request #12 from lifehackerhansol/workflow-release
workflow: add release pipeline
2025-12-04 13:41:05 +01:00
lifehackerhansol
17d8e3b4f8 workflow: add release pipeline
- Minor change in push pipeline to remove spaces from artifact name
- Create release pipeline with the same steps that will upload a zipped
  package to every published release automatically
2025-12-03 21:09:03 -08:00
Gericom
9863dbf631 Merge pull request #10 from lifehackerhansol/workflow-remove-dotnet-env
workflow: remove unnecessary dotnet environment variables
2025-11-29 14:41:52 +01:00
lifehackerhansol
c77bf774d4 workflow: remove unnecessary dotnet environment variables
- This is not needed in pico-launcher.
2025-11-29 04:43:35 -08:00
201 changed files with 5809 additions and 1340 deletions

View File

@@ -2,7 +2,6 @@ name: Build Pico Launcher
on: on:
push: push:
branches: ["develop"]
paths-ignore: paths-ignore:
- 'README.md' - 'README.md'
pull_request: pull_request:
@@ -14,12 +13,8 @@ on:
jobs: jobs:
pico_launcher: pico_launcher:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: skylyrac/blocksds:slim-v1.13.1 container: skylyrac/blocksds:slim-v1.16.0
name: Build Pico Launcher name: Build Pico Launcher
env:
DOTNET_CLI_TELEMETRY_OPTOUT: 1
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: 1
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -34,4 +29,4 @@ jobs:
path: | path: |
_pico/ _pico/
LAUNCHER.nds LAUNCHER.nds
name: Pico Launcher name: Pico_Launcher

39
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
name: Build Pico Launcher release
on:
release:
types: [published]
jobs:
pico_launcher:
runs-on: ubuntu-latest
container: skylyrac/blocksds:slim-v1.16.0
name: Build Pico Launcher
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
submodules: true
- name: Install zip
run:
apt-get update && apt-get -y install zip
- 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
- name: Package for release
run: |
mkdir Pico_Launcher
cp -r _pico/ LAUNCHER.nds Pico_Launcher
cd Pico_Launcher && zip -r $PWD.zip *
- name: Release
uses: softprops/action-gh-release@v2
with:
files: |
Pico_Launcher.zip

37
CHANGELOG.md Normal file
View File

@@ -0,0 +1,37 @@
# Changelog
## [Unreleased]
## [v1.3.0] - 18 Apr 2026
### Added
- Ability to set the position of the top screen cover image in custom themes
- Support for fast scrolling with the L and R buttons in coverflow display mode
- Support for touch input
### Fixed
- Use after free bug with the texture load request in Label3DView. This occurred for example when spamming B in banner list mode.
## [v1.2.0] - 29 Mar 2026
### Added
- Support for cheats with Pico Loader API v3
- Hide files/dirs with hidden attribute, or with a name starting with a period
- New customization options for custom themes
- Position of elements on the top screen
- Text colors
- Blend colors
### Changed
- File name on the top screen now uses marquee when too long
### Fixed
- Improve error handling for banners to better detect if a rom has a valid banner
## [v1.1.0] - 11 Jan 2026
### Added
- Support for Pico Loader API v2. This makes it possible to return to Pico Launcher from supported homebrew applications.
## [v1.0.0] - 25 Nov 2025
- Initial release

View File

@@ -105,17 +105,17 @@ INCLUDEFLAGS := $(foreach path,$(INCLUDEDIRS),-I$(path)) \
LIBDIRSFLAGS := $(foreach path,$(LIBDIRS),-L$(path)/lib) LIBDIRSFLAGS := $(foreach path,$(LIBDIRS),-L$(path)/lib)
ASFLAGS += -x assembler-with-cpp $(DEFINES) $(INCLUDEFLAGS) \ ASFLAGS += -x assembler-with-cpp $(DEFINES) $(INCLUDEFLAGS) \
$(ARCH) -ffunction-sections -fdata-sections \ $(ARCH) -g -ffunction-sections -fdata-sections \
-specs=$(SPECS) -specs=$(SPECS)
CFLAGS += -std=gnu17 $(WARNFLAGS) $(DEFINES) $(INCLUDEFLAGS) \ CFLAGS += -std=gnu17 $(WARNFLAGS) $(DEFINES) $(INCLUDEFLAGS) \
$(ARCH) -O2 -ffunction-sections -fdata-sections \ $(ARCH) -g -O2 -ffunction-sections -fdata-sections \
-fno-devirtualize-speculatively \ -fno-devirtualize-speculatively \
-Werror=return-type \ -Werror=return-type \
-specs=$(SPECS) -specs=$(SPECS)
CXXFLAGS += -std=gnu++23 $(WARNFLAGS) $(DEFINES) $(INCLUDEFLAGS) \ CXXFLAGS += -std=gnu++23 $(WARNFLAGS) $(DEFINES) $(INCLUDEFLAGS) \
$(ARCH) -O2 -ffunction-sections -fdata-sections \ $(ARCH) -g -O2 -ffunction-sections -fdata-sections \
-fno-exceptions -fno-rtti \ -fno-exceptions -fno-rtti \
-fno-devirtualize-speculatively \ -fno-devirtualize-speculatively \
-Werror=return-type \ -Werror=return-type \

View File

@@ -15,6 +15,7 @@ This repository contains Pico Launcher, which is a front-end for [Pico Loader](h
- [Covers](docs/Covers.md) - [Covers](docs/Covers.md)
- [Material Design 3 and custom themes](docs/Themes.md) - [Material Design 3 and custom themes](docs/Themes.md)
- Support for background music (see [Themes](docs/Themes.md)) - Support for background music (see [Themes](docs/Themes.md))
- Support for cheats (See [Cheats](docs/Cheats.md))
General usage documentation can be found here: [Usage](docs/Usage.md). General usage documentation can be found here: [Usage](docs/Usage.md).
@@ -22,7 +23,7 @@ General usage documentation can be found here: [Usage](docs/Usage.md).
We recommend using WSL (Windows Subsystem for Linux), or MSYS2 to compile this repository. 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. The steps provided will assume you already have one of those environments set up.
1. Install [BlocksDS](https://blocksds.skylyrac.net/docs/setup/options/) 1. Install [BlocksDS](https://blocksds.skylyrac.net/docs/setup/)
## Compiling ## Compiling

View File

@@ -8,5 +8,125 @@
"g": 217, "g": 217,
"b": 255 "b": 255
}, },
"darkTheme": false "darkTheme": false,
"topIcon": {
"position": {
"x": 24,
"y": 132
},
"blendColor": {
"r": 200,
"g": 200,
"b": 200
}
},
"topBannerTextLine0": {
"position": {
"x": 70,
"y": 126
},
"width": 176,
"textColor": {
"r": 30,
"g": 30,
"b": 30
},
"blendColor": {
"r": 200,
"g": 200,
"b": 200
}
},
"topBannerTextLine1": {
"position": {
"x": 70,
"y": 141
},
"width": 176,
"textColor": {
"r": 30,
"g": 30,
"b": 30
},
"blendColor": {
"r": 200,
"g": 200,
"b": 200
}
},
"topBannerTextLine2": {
"position": {
"x": 70,
"y": 155
},
"width": 176,
"textColor": {
"r": 30,
"g": 30,
"b": 30
},
"blendColor": {
"r": 200,
"g": 200,
"b": 200
}
},
"topFileNameText": {
"position": {
"x": 18,
"y": 170
},
"width": 220,
"textColor": {
"r": 30,
"g": 30,
"b": 30
},
"blendColor": {
"r": 200,
"g": 200,
"b": 200
}
},
"topCover": {
"position": {
"x": 75,
"y": 18
}
},
"gridIcon": {
"blendColor": {
"r": 200,
"g": 200,
"b": 200
}
},
"bannerListIcon": {
"blendColor": {
"r": 200,
"g": 200,
"b": 200
}
},
"bannerListTextLine0": {
"textColor": {
"r": 30,
"g": 30,
"b": 30
}
},
"bannerListTextLine1": {
"textColor": {
"r": 30,
"g": 30,
"b": 30
}
},
"bannerListTextLine2": {
"textColor": {
"r": 30,
"g": 30,
"b": 30
}
}
} }

View File

@@ -30,6 +30,7 @@
#include "ExitMode.h" #include "ExitMode.h"
#include "Arm7State.h" #include "Arm7State.h"
#include "mmc/tmio.h" #include "mmc/tmio.h"
#include "touchScreen.h"
static NocashOutputStream sNocashOutputStream; static NocashOutputStream sNocashOutputStream;
static PlainLogger sPlainLogger = PlainLogger(LogLevel::All, std::unique_ptr<IOutputStream>(&sNocashOutputStream)); static PlainLogger sPlainLogger = PlainLogger(LogLevel::All, std::unique_ptr<IOutputStream>(&sNocashOutputStream));
@@ -42,19 +43,14 @@ static RtcIpcService sRtcIpcService;
ILogger* gLogger = &sThreadSafeLogger; ILogger* gLogger = &sThreadSafeLogger;
static rtos_event_t sVBlankEvent; static rtos_event_t sVCountEvent;
static ExitMode sExitMode; static ExitMode sExitMode;
static Arm7State sState; static Arm7State sState;
static volatile u8 sMcuIrqFlag = false; static volatile u8 sMcuIrqFlag = false;
static void vblankIrq(u32 irqMask)
{
rtos_signalEvent(&sVBlankEvent);
}
static void vcountIrq(u32 irqMask) static void vcountIrq(u32 irqMask)
{ {
SHARED_KEY_XY = REG_RCNT0_H; rtos_signalEvent(&sVCountEvent);
} }
static void mcuIrq(u32 irq2Mask) static void mcuIrq(u32 irq2Mask)
@@ -88,12 +84,13 @@ static void checkMcuIrq(void)
} }
} }
static void initializeVBlankIrq() static void initializeVCountIrq()
{ {
rtos_createEvent(&sVBlankEvent); rtos_createEvent(&sVCountEvent);
rtos_setIrqFunc(RTOS_IRQ_VBLANK, vblankIrq); gfx_setVCountMatchLine(96);
rtos_enableIrqMask(RTOS_IRQ_VBLANK); rtos_setIrqFunc(RTOS_IRQ_VCOUNT, vcountIrq);
gfx_setVBlankIrqEnabled(true); rtos_enableIrqMask(RTOS_IRQ_VCOUNT);
gfx_setVCountMatchIrqEnabled(true);
} }
static void clearSoundRegisters() static void clearSoundRegisters()
@@ -145,12 +142,7 @@ static void initializeArm7()
sSoundIpcService.Start(); sSoundIpcService.Start();
sRtcIpcService.Start(); sRtcIpcService.Start();
gfx_setVCountMatchLine(96); initializeVCountIrq();
rtos_setIrqFunc(RTOS_IRQ_VCOUNT, vcountIrq);
rtos_enableIrqMask(RTOS_IRQ_VCOUNT);
gfx_setVCountMatchIrqEnabled(true);
initializeVBlankIrq();
if (isDSiMode()) if (isDSiMode())
{ {
@@ -158,6 +150,8 @@ static void initializeArm7()
rtos_enableIrq2Mask(RTOS_IRQ2_MCU); rtos_enableIrq2Mask(RTOS_IRQ2_MCU);
} }
touch_init();
ipc_setArm7SyncBits(7); ipc_setArm7SyncBits(7);
} }
@@ -233,7 +227,16 @@ int main()
while (true) while (true)
{ {
rtos_waitEvent(&sVBlankEvent, true, true); rtos_waitEvent(&sVCountEvent, true, true);
u16 keys = REG_RCNT0_H | RCNT0_H_DATA_PEN;
touchPosition touchPos;
if (touch_update(touchPos))
{
keys &= ~RCNT0_H_DATA_PEN; // pen down
SHARED_TOUCH_X = touchPos.px;
SHARED_TOUCH_Y = touchPos.py;
}
SHARED_KEY_XY = keys;
updateArm7(); updateArm7();
} }

146
arm7/source/touchScreen.cpp Normal file
View File

@@ -0,0 +1,146 @@
#include "common.h"
#include <nds/input.h>
#include <nds/arm7/touch.h>
// See: https://github.com/blocksds/libnds/blob/master/source/arm7/input.c
// === Touchscreen filter configuration ===
// Replace Z1/Z2 values with X/Y noisiness measurements.
// #define TOUCH_DEBUG_NOISINESS
// The number of frames to debounce/hold pen presses for.
// Set to 0 to disable.
#define PEN_DOWN_DEBOUNCE 1
// The shift (1 << N) used for the IIR filter to average noisy samples across
// time. Set to 0 to disable.
#define TOUCH_MAX_NOISE_PEN_UP_IIR_SHIFT 5
// The maximum value of noisiness for pressing a pen down (measurement now valid).
#define TOUCH_MAX_NOISE_PEN_DOWN 38
// The minimum value of noisiness for lifting a pen up (measurement no longer valid).
#define TOUCH_MAX_NOISE_PEN_UP 50
// === Touchscreen filter ===
// IIR filter constants.
#define TOUCH_MAX_NOISE_PEN_UP_IIR_RATIO (1 << TOUCH_MAX_NOISE_PEN_UP_IIR_SHIFT)
#define TOUCH_MAX_NOISE_PEN_UP_IIR_MIN (TOUCH_MAX_NOISE_PEN_UP - TOUCH_MAX_NOISE_PEN_UP_IIR_RATIO)
typedef struct {
u16 value; // 1..4095, 0 if invalid
u16 noisiness; // 0..4095, ~15-16 = 1 pixel
} libnds_touchMeasurementFilterResult;
/**
* @brief Perform filtering on the raw touch samples provided to return one
* averaged sample and an estimate of how noisy it is, while skipping outliers.
*
* Internal. See touchFilter.c for more information.
*/
extern "C" libnds_touchMeasurementFilterResult libnds_touchMeasurementFilter(u16 values[5]);
void touch_init()
{
touchInit();
}
bool touch_update(touchPosition& touchPos)
{
#if PEN_DOWN_DEBOUNCE > 0
static touchPosition lastTouchPosition;
static bool lastPenDown = false;
static u8 penDownDebounce = 0;
#else
touchPosition lastTouchPosition;
bool lastPenDown = false;
#endif
bool penDown = touchPenDown();
if (penDown)
{
// Set penDown to false for later fallthroughs to noPenDown.
// It will be set to true if all the touch filtering ensures the readout is valid.
penDown = false;
// Measure new touch position.
touchRawArray data;
if (!touchReadData(&data))
goto noPenDown;
libnds_touchMeasurementFilterResult rawXresult = libnds_touchMeasurementFilter(data.rawX);
if (!rawXresult.value)
goto noPenDown;
libnds_touchMeasurementFilterResult rawYresult = libnds_touchMeasurementFilter(data.rawY);
if (!rawYresult.value)
goto noPenDown;
// Valid sample read.
u16 noisiness = rawXresult.noisiness > rawYresult.noisiness ? rawXresult.noisiness : rawYresult.noisiness;
if (noisiness <= (lastPenDown ? TOUCH_MAX_NOISE_PEN_UP : TOUCH_MAX_NOISE_PEN_DOWN))
{
lastTouchPosition.z1 = libnds_touchMeasurementFilter(data.z1).value;
lastTouchPosition.z2 = libnds_touchMeasurementFilter(data.z2).value;
#if TOUCH_MAX_NOISE_PEN_UP_IIR_SHIFT > 0
// Apply an IIR filter on noisy X/Y samples.
// Skip the IIR filter if the pen was just pressed.
int n = (noisiness - TOUCH_MAX_NOISE_PEN_UP_IIR_MIN);
if (noisiness <= 0 || !lastPenDown)
{
lastTouchPosition.rawx = rawXresult.value;
lastTouchPosition.rawy = rawYresult.value;
}
else if (noisiness <= TOUCH_MAX_NOISE_PEN_UP_IIR_RATIO)
{
lastTouchPosition.rawx =
((rawXresult.value * (TOUCH_MAX_NOISE_PEN_UP_IIR_RATIO - n))
+ (lastTouchPosition.rawx * n)) >> TOUCH_MAX_NOISE_PEN_UP_IIR_SHIFT;
lastTouchPosition.rawy =
((rawYresult.value * (TOUCH_MAX_NOISE_PEN_UP_IIR_RATIO - n))
+ (lastTouchPosition.rawy * n)) >> TOUCH_MAX_NOISE_PEN_UP_IIR_SHIFT;
}
#else
lastTouchPosition.rawx = rawXresult.value;
lastTouchPosition.rawy = rawYresult.value;
#endif
touchApplyCalibration(lastTouchPosition.rawx, lastTouchPosition.rawy, &lastTouchPosition.px, &lastTouchPosition.py);
penDown = true;
}
#ifdef TOUCH_DEBUG_NOISINESS
lastTouchPosition.z1 = rawXresult.noisiness;
lastTouchPosition.z2 = rawYresult.noisiness;
#endif
}
noPenDown:
#if PEN_DOWN_DEBOUNCE > 0
// Perform simple debouncing.
// Hold new presses for PEN_DOWN_DEBOUNCE frames.
if (!penDownDebounce)
{
if (lastPenDown != penDown)
{
lastPenDown = penDown;
if (penDown)
penDownDebounce = PEN_DOWN_DEBOUNCE;
}
}
else
{
penDownDebounce--;
}
#else
lastPenDown = penDown;
#endif
// Return the touch position and pen down.
if (lastPenDown)
{
touchPos = lastTouchPosition;
}
return lastPenDown;
}

View File

@@ -0,0 +1,5 @@
#pragma once
#include <nds/touch.h>
void touch_init();
bool touch_update(touchPosition& touchPos);

View File

@@ -0,0 +1,7 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4
-p!

BIN
arm9/gfx/cheatSelector.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

View File

@@ -0,0 +1,7 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4
-p!

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

View File

@@ -0,0 +1,7 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4
-p!

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

7
arm9/gfx/folderIcon.grit Normal file
View File

@@ -0,0 +1,7 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4
-p!

BIN
arm9/gfx/folderIcon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

7
arm9/gfx/upIcon.grit Normal file
View File

@@ -0,0 +1,7 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4
-p!

BIN
arm9/gfx/upIcon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

View File

@@ -20,6 +20,7 @@
#include "romBrowser/DisplayMode/RomBrowserDisplayModeFactory.h" #include "romBrowser/DisplayMode/RomBrowserDisplayModeFactory.h"
#include "romBrowser/Theme/Material/MaterialThemeFileIconFactory.h" #include "romBrowser/Theme/Material/MaterialThemeFileIconFactory.h"
#include "romBrowser/views/NdsGameDetailsBottomSheetView.h" #include "romBrowser/views/NdsGameDetailsBottomSheetView.h"
#include "romBrowser/views/cheats/CheatsBottomSheetView.h"
#include "romBrowser/views/DisplaySettingsBottomSheetView.h" #include "romBrowser/views/DisplaySettingsBottomSheetView.h"
#include "bgm/AudioStreamPlayer.h" #include "bgm/AudioStreamPlayer.h"
#include "bgm/BgmService.h" #include "bgm/BgmService.h"
@@ -42,9 +43,9 @@ App::App(IAppSettingsService& appSettingsService, IBgmService& bgmService)
, _subVramContext(nullptr, &_subObjVram, nullptr, nullptr) , _subVramContext(nullptr, &_subObjVram, nullptr, nullptr)
, _appSettingsService(appSettingsService) , _appSettingsService(appSettingsService)
, _bgmService(bgmService) , _bgmService(bgmService)
, _inputProvider(&_inputSource) , _inputProvider(&_keyInputSource, &_touchInputSource)
, _inputRepeater(&_inputProvider, , _inputRepeater(&_inputProvider,
InputKey::DpadLeft | InputKey::DpadRight | InputKey::DpadUp | InputKey::DpadDown, InputKey::DpadLeft | InputKey::DpadRight | InputKey::DpadUp | InputKey::DpadDown | InputKey::L | InputKey::R,
25, 8) 25, 8)
, _romBrowserController(&appSettingsService, &_ioTaskQueue, &_bgTaskQueue) , _romBrowserController(&appSettingsService, &_ioTaskQueue, &_bgTaskQueue)
, _displaySettingsBottomSheetViewModel(&_romBrowserController) , _displaySettingsBottomSheetViewModel(&_romBrowserController)
@@ -133,7 +134,7 @@ void App::Run()
StoreVramState(_vramStateBeforeMakeBottomScreenView); StoreVramState(_vramStateBeforeMakeBottomScreenView);
_romBrowserBottomScreenView = std::make_unique<RomBrowserBottomScreenView>( _romBrowserBottomScreenView = RomBrowserBottomScreenView::CreateShared(
&_romBrowserBottomScreenViewModel, &_romBrowserBottomScreenViewModel,
RomBrowserDisplayModeFactory().GetRomBrowserDisplayMode( RomBrowserDisplayModeFactory().GetRomBrowserDisplayMode(
_romBrowserController.GetRomBrowserDisplaySettings().layout), _romBrowserController.GetRomBrowserDisplaySettings().layout),
@@ -295,10 +296,15 @@ void App::HandleTrigger(RomBrowserStateTrigger trigger, RomBrowserState newState
void App::HandleShowGameInfoTrigger() void App::HandleShowGameInfoTrigger()
{ {
auto gameInfoDialog = std::make_unique<NdsGameDetailsBottomSheetView>( // auto gameInfoDialog = std::make_unique<NdsGameDetailsBottomSheetView>(
&_romBrowserController, &_theme->GetMaterialColorScheme(), _theme->GetFontRepository()); // &_romBrowserController, &_theme->GetMaterialColorScheme(), _theme->GetFontRepository());
gameInfoDialog->SetGraphics(_chipViewVram); // gameInfoDialog->SetGraphics(_chipViewVram);
_dialogPresenter.ShowDialog(std::move(gameInfoDialog)); // _dialogPresenter.ShowDialog(std::move(gameInfoDialog));
auto cheatsViewModel = SharedPtr<CheatsViewModel>::MakeShared(_romBrowserController.GetTriggerFileInfo(), &_romBrowserController);
auto cheatsDialog = CheatsBottomSheetView::CreateShared(
std::move(cheatsViewModel), &_theme->GetMaterialColorScheme(), _theme->GetFontRepository(), &_focusManager);
_dialogPresenter.ShowDialog(std::move(cheatsDialog));
} }
void App::HandleHideGameInfoTrigger() void App::HandleHideGameInfoTrigger()
@@ -310,7 +316,7 @@ void App::HandleHideGameInfoTrigger()
void App::HandleShowDisplaySettingsTrigger() void App::HandleShowDisplaySettingsTrigger()
{ {
auto displaySettingsDialog = std::make_unique<DisplaySettingsBottomSheetView>( auto displaySettingsDialog = DisplaySettingsBottomSheetView::CreateShared(
&_displaySettingsBottomSheetViewModel, &_theme->GetMaterialColorScheme(), _theme->GetFontRepository()); &_displaySettingsBottomSheetViewModel, &_theme->GetMaterialColorScheme(), _theme->GetFontRepository());
displaySettingsDialog->SetGraphics(_iconButtonViewVram); displaySettingsDialog->SetGraphics(_iconButtonViewVram);
_dialogPresenter.ShowDialog(std::move(displaySettingsDialog)); _dialogPresenter.ShowDialog(std::move(displaySettingsDialog));
@@ -331,11 +337,11 @@ void App::HandleNavigateTrigger()
void App::HandleFolderLoadDoneTrigger() void App::HandleFolderLoadDoneTrigger()
{ {
_romBrowserTopScreenView.reset(); _romBrowserTopScreenView.Reset();
RestoreVramState(_vramStateAfterMakeBottomScreenView); RestoreVramState(_vramStateAfterMakeBottomScreenView);
auto displayMode = RomBrowserDisplayModeFactory().GetRomBrowserDisplayMode( auto displayMode = RomBrowserDisplayModeFactory().GetRomBrowserDisplayMode(
_romBrowserController.GetRomBrowserDisplaySettings().layout); _romBrowserController.GetRomBrowserDisplaySettings().layout);
_romBrowserTopScreenView = std::make_unique<RomBrowserTopScreenView>( _romBrowserTopScreenView = RomBrowserTopScreenView::CreateShared(
_romBrowserController.GetRomBrowserViewModel(), _romBrowserController.GetRomBrowserViewModel(),
displayMode, displayMode,
_materialThemeFileIconFactory.get(), _materialThemeFileIconFactory.get(),
@@ -352,7 +358,7 @@ void App::HandleChangeDisplayModeTrigger(RomBrowserState newState)
RestoreVramState(_vramStateBeforeMakeBottomScreenView); RestoreVramState(_vramStateBeforeMakeBottomScreenView);
auto displayMode = RomBrowserDisplayModeFactory().GetRomBrowserDisplayMode( auto displayMode = RomBrowserDisplayModeFactory().GetRomBrowserDisplayMode(
_romBrowserController.GetRomBrowserDisplaySettings().layout); _romBrowserController.GetRomBrowserDisplaySettings().layout);
_romBrowserBottomScreenView = std::make_unique<RomBrowserBottomScreenView>( _romBrowserBottomScreenView = RomBrowserBottomScreenView::CreateShared(
&_romBrowserBottomScreenViewModel, &_romBrowserBottomScreenViewModel,
displayMode, displayMode,
_materialThemeFileIconFactory.get(), _materialThemeFileIconFactory.get(),
@@ -360,7 +366,7 @@ void App::HandleChangeDisplayModeTrigger(RomBrowserState newState)
&_vblankTextureLoader); &_vblankTextureLoader);
_romBrowserBottomScreenView->InitVram(_mainVramContext); _romBrowserBottomScreenView->InitVram(_mainVramContext);
StoreVramState(_vramStateAfterMakeBottomScreenView); StoreVramState(_vramStateAfterMakeBottomScreenView);
_romBrowserTopScreenView = std::make_unique<RomBrowserTopScreenView>( _romBrowserTopScreenView = RomBrowserTopScreenView::CreateShared(
_romBrowserController.GetRomBrowserViewModel(), _romBrowserController.GetRomBrowserViewModel(),
displayMode, displayMode,
_materialThemeFileIconFactory.get(), _materialThemeFileIconFactory.get(),
@@ -399,7 +405,7 @@ void App::Update()
bool isRomBrowserVisible = IsRomBrowserVisible(); bool isRomBrowserVisible = IsRomBrowserVisible();
if (isRomBrowserVisible && !_exit && curState != RomBrowserState::Launching) if (isRomBrowserVisible && !_exit && curState != RomBrowserState::Launching)
{ {
_focusManager.Update(_inputRepeater); HandleInput();
} }
if (_topBackground) if (_topBackground)
@@ -517,3 +523,49 @@ void App::RestoreVramState(const VramState& vramState)
_texturePaletteVram.SetState(vramState._texPlttVramState); _texturePaletteVram.SetState(vramState._texPlttVramState);
_subObjVram.SetState(vramState._subObjVramState); _subObjVram.SetState(vramState._subObjVramState);
} }
void App::HandleInput()
{
_focusManager.Update(_inputRepeater);
Point touchPoint;
if (_inputRepeater.Triggered(InputKey::Touch) &&
_inputRepeater.GetCurrentTouchPoint(touchPoint))
{
// pen down
if (_dialogPresenter.IsBottomSheetVisible())
{
_dialogPresenter.HandlePenDown(touchPoint, _focusManager);
}
else
{
_romBrowserBottomScreenView->HandlePenDown(touchPoint, _focusManager);
}
_lastTouchPoint = touchPoint;
}
else if (_inputRepeater.Released(InputKey::Touch))
{
// pen up
if (_dialogPresenter.IsBottomSheetVisible())
{
_dialogPresenter.HandlePenUp(_lastTouchPoint, _focusManager);
}
else
{
_romBrowserBottomScreenView->HandlePenUp(_lastTouchPoint, _focusManager);
}
}
else if (_inputRepeater.Current(InputKey::Touch)
&& _inputRepeater.GetCurrentTouchPoint(touchPoint))
{
// pen move
if (_dialogPresenter.IsBottomSheetVisible())
{
_dialogPresenter.HandlePenMove(touchPoint, _focusManager);
}
else
{
_romBrowserBottomScreenView->HandlePenMove(touchPoint, _focusManager);
}
_lastTouchPoint = touchPoint;
}
}

View File

@@ -12,6 +12,7 @@
#include "gui/DescendingStackVramManager.h" #include "gui/DescendingStackVramManager.h"
#include "material/scheme/scheme.h" #include "material/scheme/scheme.h"
#include "gui/input/PadInputSource.h" #include "gui/input/PadInputSource.h"
#include "gui/input/TouchInputSource.h"
#include "gui/input/SampledInputProvider.h" #include "gui/input/SampledInputProvider.h"
#include "gui/input/InputRepeater.h" #include "gui/input/InputRepeater.h"
#include "gui/VBlankTextureLoader.h" #include "gui/VBlankTextureLoader.h"
@@ -28,7 +29,6 @@
#include "romBrowser/RomBrowserController.h" #include "romBrowser/RomBrowserController.h"
#include "DialogPresenter.h" #include "DialogPresenter.h"
#include "themes/ITheme.h" #include "themes/ITheme.h"
#include "core/SharedPtr.h"
#include "animation/Animator.h" #include "animation/Animator.h"
class alignas(32) App : public IProcess class alignas(32) App : public IProcess
@@ -63,9 +63,9 @@ private:
Rgb6Palette _rgb6Palette; Rgb6Palette _rgb6Palette;
Animator<int> _fadeAnimator; Animator<int> _fadeAnimator;
TaskQueue<32, 32> _ioTaskQueue; TaskQueue<32, sizeof(TaskBase) + 32> _ioTaskQueue;
u32 _ioTaskThreadStack[2048 / 4]; u32 _ioTaskThreadStack[2048 / 4];
TaskQueue<32, 32> _bgTaskQueue; TaskQueue<32, sizeof(TaskBase) + 32> _bgTaskQueue;
u32 _bgTaskThreadStack[2048 / 4]; u32 _bgTaskThreadStack[2048 / 4];
std::unique_ptr<ITheme> _theme; std::unique_ptr<ITheme> _theme;
@@ -76,12 +76,13 @@ private:
IBgmService& _bgmService; IBgmService& _bgmService;
volatile bool _exit = false; volatile bool _exit = false;
PadInputSource _inputSource; PadInputSource _keyInputSource;
TouchInputSource _touchInputSource;
SampledInputProvider _inputProvider; SampledInputProvider _inputProvider;
InputRepeater _inputRepeater; InputRepeater _inputRepeater;
std::unique_ptr<RomBrowserBottomScreenView> _romBrowserBottomScreenView; SharedPtr<RomBrowserBottomScreenView> _romBrowserBottomScreenView;
std::unique_ptr<RomBrowserTopScreenView> _romBrowserTopScreenView; SharedPtr<RomBrowserTopScreenView> _romBrowserTopScreenView;
RomBrowserController _romBrowserController; RomBrowserController _romBrowserController;
@@ -104,10 +105,13 @@ private:
bool _vcountIrqStarted = false; bool _vcountIrqStarted = false;
Point _lastTouchPoint = Point(0, 0);
void InitVramMapping() const; void InitVramMapping() const;
void DisplaySplashScreen() const; void DisplaySplashScreen() const;
void LoadTheme(); void LoadTheme();
void VCountIrq(); void VCountIrq();
void HandleInput();
void HandleTrigger(RomBrowserStateTrigger trigger, RomBrowserState newState); void HandleTrigger(RomBrowserStateTrigger trigger, RomBrowserState newState);
void HandleShowGameInfoTrigger(); void HandleShowGameInfoTrigger();
void HandleHideGameInfoTrigger(); void HandleHideGameInfoTrigger();

View File

@@ -16,11 +16,13 @@ DialogPresenter::DialogPresenter(FocusManager* focusManager, StackVramManager* v
_baseVramState = _vramManager->GetState(); _baseVramState = _vramManager->GetState();
} }
void DialogPresenter::ShowDialog(std::unique_ptr<DialogView> dialog) void DialogPresenter::ShowDialog(SharedPtr<DialogView> dialog)
{ {
if (!_nextDialog) if (!_nextDialog)
{
_nextDialog = std::move(dialog); _nextDialog = std::move(dialog);
} }
}
void DialogPresenter::CloseDialog() void DialogPresenter::CloseDialog()
{ {
@@ -93,7 +95,7 @@ void DialogPresenter::Update()
else else
{ {
_newState = State::Idle; _newState = State::Idle;
_currentDialog.reset(); _currentDialog.Reset();
} }
break; break;
} }
@@ -153,3 +155,27 @@ void DialogPresenter::InitVram()
REG_BLDCNT = 0x3944; REG_BLDCNT = 0x3944;
REG_BLDALPHA = (16 << 8) | 0; REG_BLDALPHA = (16 << 8) | 0;
} }
void DialogPresenter::HandlePenDown(const Point& touchPoint, FocusManager& focusManager)
{
if (_curState == State::BottomSheetVisible && _currentDialog)
{
_currentDialog->HandlePenDown(touchPoint, focusManager);
}
}
void DialogPresenter::HandlePenMove(const Point& touchPoint, FocusManager& focusManager)
{
if (_curState == State::BottomSheetVisible && _currentDialog)
{
_currentDialog->HandlePenMove(touchPoint, focusManager);
}
}
void DialogPresenter::HandlePenUp(const Point& lastTouchPoint, FocusManager& focusManager)
{
if (_curState == State::BottomSheetVisible && _currentDialog)
{
_currentDialog->HandlePenUp(lastTouchPoint, focusManager);
}
}

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include "core/SharedPtr.h"
#include "animation/Animator.h" #include "animation/Animator.h"
#include "gui/views/DialogView.h" #include "gui/views/DialogView.h"
@@ -14,7 +15,7 @@ public:
/// @brief Requests to show the given dialog. /// @brief Requests to show the given dialog.
/// @param dialog The dialog to show. /// @param dialog The dialog to show.
void ShowDialog(std::unique_ptr<DialogView> dialog); void ShowDialog(SharedPtr<DialogView> dialog);
/// @brief Closes the current dialog. /// @brief Closes the current dialog.
void CloseDialog(); void CloseDialog();
@@ -41,6 +42,21 @@ public:
/// @brief Initializes vram that is needed for showing dialogs. /// @brief Initializes vram that is needed for showing dialogs.
void InitVram(); void InitVram();
/// @brief Handles a pen down event.
/// @param touchPoint The touch point.
/// @param focusManager The focus manager.
void HandlePenDown(const Point& touchPoint, FocusManager& focusManager);
/// @brief Handles a pen move event.
/// @param touchPoint The touch point.
/// @param focusManager The focus manager.
void HandlePenMove(const Point& touchPoint, FocusManager& focusManager);
/// @brief Handles a pen up event.
/// @param lastTouchPoint The last touch point.
/// @param focusManager The focus manager.
void HandlePenUp(const Point& lastTouchPoint, FocusManager& focusManager);
/// @brief Clears the focus that was stored when a dialog was opened. /// @brief Clears the focus that was stored when a dialog was opened.
void ClearOldFocus() void ClearOldFocus()
{ {
@@ -49,11 +65,16 @@ public:
/// @brief Gets the focus that was stored when a dialog was opened. /// @brief Gets the focus that was stored when a dialog was opened.
/// @return The view that was focused when the current dialog was opened. /// @return The view that was focused when the current dialog was opened.
constexpr View* GetOldFocus() const constexpr SharedPtr<View> GetOldFocus() const
{ {
return _oldFocus; return _oldFocus;
} }
bool IsBottomSheetVisible() const
{
return _curState != State::Idle;
}
private: private:
enum class State enum class State
{ {
@@ -65,10 +86,10 @@ private:
FocusManager* _focusManager; FocusManager* _focusManager;
StackVramManager* _vramManager; StackVramManager* _vramManager;
u32 _baseVramState; u32 _baseVramState;
std::unique_ptr<DialogView> _currentDialog; SharedPtr<DialogView> _currentDialog;
std::unique_ptr<DialogView> _nextDialog; SharedPtr<DialogView> _nextDialog;
bool _initVram = false; bool _initVram = false;
View* _oldFocus = nullptr; SharedPtr<View> _oldFocus = nullptr;
Animator<int> _scrimAnimator; Animator<int> _scrimAnimator;
Animator<int> _yAnimator; Animator<int> _yAnimator;
State _curState = State::Idle; State _curState = State::Idle;

View File

@@ -0,0 +1,190 @@
#pragma once
/// @brief Class representing a cheat or a cheat category.
class CheatEntry
{
public:
/// @brief Dummy empty constructor.
CheatEntry()
: _isCheatCategory(false), _flagsPointer(nullptr), _cheatData(nullptr), _cheatDataLength(0) { }
/// @brief Constructor for a cheat.
CheatEntry(const char* name, const char* description, u32* flagsPointer, const void* cheatData, u32 cheatDataLength)
: _name(name), _description(description), _isCheatCategory(false), _flagsPointer(flagsPointer)
, _cheatData(cheatData), _cheatDataLength(cheatDataLength) { }
/// @brief Constructor for a category.
CheatEntry(const char* name, const char* description, bool isMaxOneCheatActive, CheatEntry* subEntries, u32 numberOfSubEntries)
: _name(name), _description(description), _isCheatCategory(true), _isMaxOneCheatActive(isMaxOneCheatActive)
, _subEntries(subEntries), _numberOfSubEntries(numberOfSubEntries) { }
CheatEntry(const CheatEntry& other) = delete;
CheatEntry(CheatEntry&& other)
: _isCheatCategory(false), _flagsPointer(nullptr), _cheatData(nullptr), _cheatDataLength(0)
{
*this = std::move(other);
}
~CheatEntry()
{
if (_isCheatCategory && _subEntries != nullptr)
{
delete[] _subEntries;
}
}
CheatEntry& operator=(const CheatEntry& other) = delete;
CheatEntry& operator=(CheatEntry&& other)
{
if (_isCheatCategory && _subEntries != nullptr)
{
delete[] _subEntries;
_subEntries = nullptr;
}
_name = other._name;
other._name = nullptr;
_description = other._description;
other._description = nullptr;
_isCheatCategory = other.IsCheatCategory();
if (_isCheatCategory)
{
_isMaxOneCheatActive = other._isMaxOneCheatActive;
_subEntries = other._subEntries;
other._subEntries = nullptr;
_numberOfSubEntries = other._numberOfSubEntries;
other._numberOfSubEntries = 0;
}
else
{
_flagsPointer = other._flagsPointer;
other._flagsPointer = nullptr;
_cheatData = other._cheatData;
other._cheatData = nullptr;
_cheatDataLength = other._cheatDataLength;
other._cheatDataLength = 0;
}
return *this;
}
/// @brief Gets the name of this cheat entry.
/// @return A pointer to the name of this cheat entry.
const char* GetName() const
{
return _name;
}
/// @brief Gets the description of this cheat entry.
/// @return A pointer to the description of this cheat entry.
const char* GetDescription() const
{
return _description;
}
/// @brief When this entry is a cheat, gets a pointer to the cheat data.
/// @param cheatDataLength The length of the cheat data is returned in this reference.
/// @return A pointer to the cheat data.
/// This pointer is only valid for the lifetime of the \see GameCheats instance this cheat belongs to.
/// If this entry is not a cheat, \c nullptr is returned.
const void* GetCheatData(u32& cheatDataLength) const
{
if (_isCheatCategory)
{
cheatDataLength = 0;
return nullptr;
}
else
{
cheatDataLength = _cheatDataLength;
return _cheatData;
}
}
/// @brief Gets whether this entry is an active (enabled) cheat or not.
/// @return \c true when this entry is an active cheat, or \c false otherwise.
bool GetIsCheatActive() const
{
if (_isCheatCategory)
{
return false;
}
else
{
return ((*_flagsPointer >> 24) & 1) == 1;
}
}
/// @brief If this entry is a cheat, sets whether this cheat is active (enabled) or not.
/// @param isCheatActive \c true to enable this cheat, or \c false to disable this cheat.
void SetIsCheatActive(bool isCheatActive) const
{
if (!_isCheatCategory)
{
u32 flags = *_flagsPointer;
flags &= ~(1 << 24);
if (isCheatActive)
{
flags |= 1 << 24;
}
*_flagsPointer = flags;
}
}
/// @brief Indicates if this entry is a cheat category or not.
/// @return \c true when this entry is a cheat category, or \c false when this entry is a cheat.
bool IsCheatCategory() const
{
return _isCheatCategory;
}
/// @brief Indicates if this entry is a cheat category in which only one cheat is allowed to be on at a time or not.
/// @return \c true when this entry is a cheat category in which only one cheat is allowed
/// to be active at a time, or \c false otherwise.
bool GetIsMaxOneCheatActive() const
{
return _isCheatCategory && _isMaxOneCheatActive;
}
/// @brief Gets the sub-entries of this entry.
/// @param numberOfSubEntries The number of sub-entries is returned through this reference.
/// @return A pointer to an array of entries. This may be \c nullptr when \p numberOfSubEntries is 0.
const CheatEntry* GetSubEntries(u32& numberOfSubEntries) const
{
if (_isCheatCategory)
{
numberOfSubEntries = _numberOfSubEntries;
return _subEntries;
}
else
{
numberOfSubEntries = 0;
return nullptr;
}
}
private:
const char* _name = nullptr;
const char* _description = nullptr;
bool _isCheatCategory;
union
{
struct
{
// For cheat
u32* _flagsPointer;
const void* _cheatData;
u32 _cheatDataLength;
};
struct
{
// For category
bool _isMaxOneCheatActive;
CheatEntry* _subEntries;
u32 _numberOfSubEntries;
};
};
};

View File

@@ -0,0 +1,17 @@
#pragma once
#include "ICheatRepository.h"
/// @brief Class implementing an empty cheat repository.
class EmptyCheatRepository : public ICheatRepository
{
public:
std::unique_ptr<GameCheats> GetCheatsForGame(const FastFileRef& romFile) const override
{
return nullptr;
}
void UpdateEnabledCheatsForGame(const std::unique_ptr<GameCheats>& cheats) const override
{
// Do nothing
}
};

View File

@@ -0,0 +1,32 @@
#pragma once
#include "CheatEntry.h"
/// @brief Class holding the cheats for a game.
class GameCheats : public CheatEntry
{
public:
GameCheats(std::unique_ptr<u8[]> cheatData, u32 cheatDataLength, u32 fileOffset, const char* gameName,
CheatEntry* subEntries, u32 numberOfSubEntries)
: CheatEntry(gameName, "", false, subEntries, numberOfSubEntries)
, _cheatData(std::move(cheatData)), _cheatDataLength(cheatDataLength)
, _fileOffset(fileOffset) { }
/// @brief Gets a pointer to the cheat data.
/// @param cheatDataLength The length of the cheat data is returned in this reference.
/// @return A pointer to the cheat data. This pointer is only valid for the lifetime of this \see GameCheats instance.
u8* GetCheatData(u32& cheatDataLength) const
{
cheatDataLength = _cheatDataLength;
return _cheatData.get();
}
u32 GetFileOffset() const
{
return _fileOffset;
}
private:
std::unique_ptr<u8[]> _cheatData;
u32 _cheatDataLength;
u32 _fileOffset;
};

View File

@@ -0,0 +1,22 @@
#pragma once
#include "GameCheats.h"
#include "fat/FastFileRef.h"
/// @brief Interface for a repository providing access to cheats.
class ICheatRepository
{
public:
virtual ~ICheatRepository() { }
/// @brief Gets the available cheats for the given \p romFile.
/// @param romFile Reference to the rom file.
/// @return A unique pointer to the found cheats, or an empty unique pointer when no cheats were found.
virtual std::unique_ptr<GameCheats> GetCheatsForGame(const FastFileRef& romFile) const = 0;
/// @brief Writes the enable/disabled status of the given \p cheats.
/// @param cheats The cheats to update.
virtual void UpdateEnabledCheatsForGame(const std::unique_ptr<GameCheats>& cheats) const = 0;
protected:
ICheatRepository() { }
};

View File

@@ -0,0 +1,80 @@
#include "common.h"
#include <string.h>
#include "PicoLoaderCheatDataFactory.h"
pload_cheats_t* PicoLoaderCheatDataFactory::CreateCheatData(const std::unique_ptr<GameCheats>& gameCheats) const
{
pload_cheats_t* cheatData = nullptr;
if (gameCheats)
{
u32 totalNumberOfCheats = 0;
u32 requiredSize = GetCheatEntryRequiredSize(gameCheats.get(), totalNumberOfCheats);
if (totalNumberOfCheats != 0)
{
requiredSize += sizeof(u32) * 2;
cheatData = (pload_cheats_t*)new u8[requiredSize];
cheatData->length = requiredSize;
cheatData->numberOfCheats = totalNumberOfCheats;
u8* buffer = (u8*)&cheatData->firstCheat;
GetCheatEntryData(gameCheats.get(), buffer);
}
}
return cheatData;
}
u32 PicoLoaderCheatDataFactory::GetCheatEntryRequiredSize(const CheatEntry* cheatEntry, u32& totalNumberOfCheats) const
{
u32 requiredSize = 0;
if (cheatEntry->IsCheatCategory())
{
u32 numberOfSubEntries = 0;
auto subEntries = cheatEntry->GetSubEntries(numberOfSubEntries);
for (u32 i = 0; i < numberOfSubEntries; i++)
{
requiredSize += GetCheatEntryRequiredSize(&subEntries[i], totalNumberOfCheats);
}
}
else
{
if (cheatEntry->GetIsCheatActive())
{
u32 cheatDataLength = 0;
cheatEntry->GetCheatData(cheatDataLength);
requiredSize += sizeof(u32) + ((cheatDataLength + 7) & ~7);
totalNumberOfCheats++;
}
}
return requiredSize;
}
void PicoLoaderCheatDataFactory::GetCheatEntryData(const CheatEntry* cheatEntry, u8*& buffer) const
{
if (cheatEntry->IsCheatCategory())
{
u32 numberOfSubEntries = 0;
auto subEntries = cheatEntry->GetSubEntries(numberOfSubEntries);
for (u32 i = 0; i < numberOfSubEntries; i++)
{
GetCheatEntryData(&subEntries[i], buffer);
}
}
else
{
if (cheatEntry->GetIsCheatActive())
{
u32 cheatDataLength = 0;
auto cheatData = cheatEntry->GetCheatData(cheatDataLength);
u32 paddedCheatDataLength = (cheatDataLength + 7) & ~7;
*(u32*)buffer = paddedCheatDataLength;
buffer += sizeof(u32);
memcpy(buffer, cheatData, cheatDataLength);
if (cheatDataLength != paddedCheatDataLength)
{
memset(buffer + cheatDataLength, 0, paddedCheatDataLength - cheatDataLength);
}
buffer += paddedCheatDataLength;
}
}
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include <memory>
#include "GameCheats.h"
#include "picoLoader7.h"
/// @brief Factory for creating Pico Loader compatible cheat data.
class PicoLoaderCheatDataFactory
{
public:
/// @brief Converts the given \p gameCheats to Pico Loader format. Only the enabled cheats will be included.
/// @param gameCheats The cheats to convert.
/// @return Pointer to the created cheat data.
pload_cheats_t* CreateCheatData(const std::unique_ptr<GameCheats>& gameCheats) const;
private:
u32 GetCheatEntryRequiredSize(const CheatEntry* cheatEntry, u32& totalNumberOfCheats) const;
void GetCheatEntryData(const CheatEntry* cheatEntry, u8*& buffer) const;
};

View File

@@ -0,0 +1,12 @@
#pragma once
/// @brief Struct representing an index entry of usrcheat.dat.
struct usr_cheat_index_entry_t
{
u32 gameCode;
u32 headerCrc32;
u32 offset;
u32 padding;
};
static_assert(sizeof(usr_cheat_index_entry_t) == 16);

View File

@@ -0,0 +1,225 @@
#include "common.h"
#include <algorithm>
#include <string.h>
#include "UsrCheatRepository.h"
#define CRCPOLY 0xEDB88320
static u32 crc32(const void* buffer, u32 length)
{
u32 crc = ~0u;
const u8* p = (u8*)buffer;
while (length--)
{
crc ^= *p++;
for (int i = 0; i < 8; i++)
{
crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY : 0);
}
}
return crc;
}
std::unique_ptr<GameCheats> UsrCheatRepository::GetCheatsForGame(const FastFileRef& romFile) const
{
auto file = std::make_unique<File>();
file->Open(romFile, FA_READ);
auto headerBuffer = std::make_unique_for_overwrite<u8[]>(512);
if (!file->ReadExact(headerBuffer.get(), 512))
{
LOG_ERROR("Could not read rom header\n");
return nullptr;
}
file->Close();
u32 gameCode = *(u32*)(headerBuffer.get() + 0xC);
u32 crc = crc32(headerBuffer.get(), 512);
headerBuffer.reset();
return GetCheatsForGame(gameCode, crc);
}
void UsrCheatRepository::UpdateEnabledCheatsForGame(const std::unique_ptr<GameCheats>& cheats) const
{
u32 cheatDataLength = 0;
auto cheatData = cheats->GetCheatData(cheatDataLength);
if (_usrCheatFile->Seek(cheats->GetFileOffset()) != FR_OK)
{
LOG_ERROR("Failed to seek to cheat data\n");
return;
}
u32 bytesWritten = 0;
if (_usrCheatFile->Write(cheatData, cheatDataLength, bytesWritten) != FR_OK ||
bytesWritten != cheatDataLength)
{
LOG_ERROR("Failed to write cheat data\n");
return;
}
if (_usrCheatFile->Sync() != FR_OK)
{
LOG_ERROR("Failed to flush cheat data\n");
}
}
std::unique_ptr<GameCheats> UsrCheatRepository::GetCheatsForGame(u32 gameCode, u32 headerCrc32) const
{
auto index = FindIndex(gameCode, headerCrc32);
if (index == nullptr)
{
LOG_DEBUG("Cheats not found for %c%c%c%c - 0x%X\n",
gameCode & 0xFF, (gameCode >> 8) & 0xFF, (gameCode >> 16) & 0xFF, gameCode >> 24,
headerCrc32);
return nullptr;
}
const u32 cheatDataLength = index->padding; // padding was set to the size in UsrCheatRepositoryFactory
auto cheatData = std::make_unique_for_overwrite<u8[]>(cheatDataLength);
if (_usrCheatFile->Seek(index->offset) != FR_OK)
{
LOG_ERROR("Failed to seek to cheat data\n");
return nullptr;
}
if (!_usrCheatFile->ReadExact(cheatData.get(), cheatDataLength))
{
LOG_ERROR("Failed to read cheat data\n");
return nullptr;
}
u8* ptr = cheatData.get();
// game name
const char* gameName = (const char*)ptr;
ptr += strlen(gameName) + 1;
// padding
ptr = (u8*)(((u32)ptr + 3) & ~3); // 32-bit align
// flags
u32 flags = *(u32*)ptr;
u32 totalNumberOfItems = flags & 0x0FFFFFFF;
// u32 gameActive = flags >> 28;
ptr += 4;
// master codes
ptr += 8 * 4;
auto entries = new CheatEntry[totalNumberOfItems];
u32 entryCount = 0;
while (ptr < cheatData.get() + cheatDataLength)
{
u32 itemFlags = *(u32*)ptr;
bool isCategory = ((itemFlags >> 28) & 1) == 1;
if (isCategory)
{
entries[entryCount++] = ParseCategory(ptr);
}
else
{
entries[entryCount++] = ParseCheat(ptr);
}
}
auto actualEntries = new CheatEntry[entryCount];
std::move(entries, entries + entryCount, actualEntries);
delete[] entries;
return std::make_unique<GameCheats>(
std::move(cheatData), cheatDataLength, index->offset, gameName, actualEntries, entryCount);
}
const usr_cheat_index_entry_t* UsrCheatRepository::FindIndex(u32 gameCode, u32 headerCrc32) const
{
if (_numberOfIndices != 0)
{
const auto index = std::lower_bound(_sortedIndices.get(), _sortedIndices.get() + _numberOfIndices, gameCode,
[headerCrc32] (const usr_cheat_index_entry_t& entry, u32 value)
{
if (entry.gameCode != value)
{
return entry.gameCode < value;
}
return entry.headerCrc32 < headerCrc32;
});
if (index != _sortedIndices.get() + _numberOfIndices &&
index->gameCode == gameCode &&
index->headerCrc32 == headerCrc32)
{
return index;
}
}
return nullptr;
}
CheatEntry UsrCheatRepository::ParseCategory(u8*& ptr) const
{
// flags
u32 itemFlags = *(u32*)ptr;
ptr += 4;
u32 numberOfItems = itemFlags & 0x00FFFFFF;
bool isMaxOneCheatActive = ((itemFlags >> 24) & 1) == 1;
// item name
const char* itemName = (const char*)ptr;
ptr += strlen(itemName) + 1;
// item description
const char* itemDescription = (const char*)ptr;
ptr += strlen(itemDescription) + 1;
// padding
ptr = (u8*)(((u32)ptr + 3) & ~3); // 32-bit align
auto entries = new CheatEntry[numberOfItems];
for (u32 i = 0; i < numberOfItems; i++)
{
u32 itemFlags = *(u32*)ptr;
bool isCategory = ((itemFlags >> 28) & 1) == 1;
if (isCategory)
{
entries[i] = ParseCategory(ptr);
}
else
{
entries[i] = ParseCheat(ptr);
}
}
return CheatEntry(itemName, itemDescription, isMaxOneCheatActive, entries, numberOfItems);
}
CheatEntry UsrCheatRepository::ParseCheat(u8*& ptr) const
{
// flags
u32* flagsPtr = (u32*)ptr;
ptr += 4;
// item name
const char* itemName = (const char*)ptr;
ptr += strlen(itemName) + 1;
// item description
const char* itemDescription = (const char*)ptr;
ptr += strlen(itemDescription) + 1;
// padding
ptr = (u8*)(((u32)ptr + 3) & ~3); // 32-bit align
// number of code words
u32 numberOfCodeWords = *(u32*)ptr;
ptr += 4;
const void* cheatData = ptr;
// code
ptr += numberOfCodeWords * 4;
return CheatEntry(itemName, itemDescription, flagsPtr, cheatData, numberOfCodeWords * 4);
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include <memory>
#include "fat/File.h"
#include "ICheatRepository.h"
#include "UsrCheatDat.h"
/// @brief Class implementing a cheat repository for usrcheat.dat.
class UsrCheatRepository : public ICheatRepository
{
public:
UsrCheatRepository(std::unique_ptr<File> usrCheatDatFile,
std::unique_ptr<usr_cheat_index_entry_t[]> sortedIndices, u32 numberOfIndices)
: _usrCheatFile(std::move(usrCheatDatFile))
, _sortedIndices(std::move(sortedIndices)), _numberOfIndices(numberOfIndices) { }
std::unique_ptr<GameCheats> GetCheatsForGame(const FastFileRef& romFile) const override;
void UpdateEnabledCheatsForGame(const std::unique_ptr<GameCheats>& cheats) const override;
private:
std::unique_ptr<File> _usrCheatFile;
std::unique_ptr<usr_cheat_index_entry_t[]> _sortedIndices;
u32 _numberOfIndices;
std::unique_ptr<GameCheats> GetCheatsForGame(u32 gameCode, u32 headerCrc32) const;
const usr_cheat_index_entry_t* FindIndex(u32 gameCode, u32 headerCrc32) const;
CheatEntry ParseCategory(u8*& ptr) const;
CheatEntry ParseCheat(u8*& ptr) const;
};

View File

@@ -0,0 +1,87 @@
#include "common.h"
#include <algorithm>
#include <string.h>
#include "fat/File.h"
#include "UsrCheatDat.h"
#include "UsrCheatRepositoryFactory.h"
std::unique_ptr<UsrCheatRepository> UsrCheatRepositoryFactory::FromUsrCheatDat(const TCHAR* usrCheatDatPath)
{
auto file = std::make_unique<File>();
if (file->Open(usrCheatDatPath, FA_READ | FA_WRITE) != FR_OK)
{
LOG_ERROR("Failed to open %s\n", usrCheatDatPath);
return nullptr;
}
char header[12];
if (!file->ReadExact(header, sizeof(header)))
{
LOG_ERROR("Failed to read usr cheat header\n");
return nullptr;
}
if (memcmp(header, "R4 CheatCode", sizeof(header)))
{
LOG_ERROR("Invalid usr cheat header\n");
return nullptr;
}
// Read index
if (file->Seek(0x100) != FR_OK)
{
LOG_ERROR("Failed to seek to usr cheat index\n");
return nullptr;
}
usr_cheat_index_entry_t firstEntry;
if (!file->ReadExact(&firstEntry, sizeof(firstEntry)))
{
LOG_ERROR("Failed to read first index\n");
return nullptr;
}
u32 numberOfIndices = 0;
if (firstEntry.offset != 0)
{
numberOfIndices = (firstEntry.offset - 0x100 - sizeof(usr_cheat_index_entry_t)) / sizeof(usr_cheat_index_entry_t);
}
if (file->Seek(0x100) != FR_OK)
{
LOG_ERROR("Failed to seek to usr cheat index\n");
return nullptr;
}
auto indices = std::make_unique_for_overwrite<usr_cheat_index_entry_t[]>(numberOfIndices);
if (!file->ReadExact(indices.get(), numberOfIndices * sizeof(usr_cheat_index_entry_t)))
{
LOG_ERROR("Failed to read first index\n");
return nullptr;
}
// Set padding field to the size of the cheat data
if (numberOfIndices > 0)
{
for (u32 i = 0; i < numberOfIndices - 1; i++)
{
indices[i].padding = indices[i + 1].offset - indices[i].offset;
}
indices[numberOfIndices - 1].padding = file->GetSize() - indices[numberOfIndices - 1].offset;
}
// sort by gameCode, then by headerCrc32
std::sort(indices.get(), indices.get() + numberOfIndices,
[] (const usr_cheat_index_entry_t& a, const usr_cheat_index_entry_t& b)
{
if (a.gameCode != b.gameCode)
{
return a.gameCode < b.gameCode;
}
return a.headerCrc32 < b.headerCrc32;
});
return std::make_unique<UsrCheatRepository>(std::move(file), std::move(indices), numberOfIndices);
}

View File

@@ -0,0 +1,13 @@
#pragma once
#include <memory>
#include "UsrCheatRepository.h"
/// @brief Factory for constructing a \see UsrCheatRepository for a usrcheat.dat file.
class UsrCheatRepositoryFactory
{
public:
/// @brief Constructs a \see UsrCheatRepository for the given \p usrCheatDatPath.
/// @param usrCheatDatPath The path to the usrcheat.dat file.
/// @return A unique pointer to the constructed repository, or an empty unique pointer when construction failed.
std::unique_ptr<UsrCheatRepository> FromUsrCheatDat(const TCHAR* usrCheatDatPath);
};

View File

@@ -0,0 +1,35 @@
#include "common.h"
#include <libtwl/rtos/rtosIrq.h>
#include "AtomicSharedPtr.h"
void AtomicSharedPtrBase::Reset(void* newObject, RefCount* newRefCount, bool increaseNewRefCount)
{
u32 irq = rtos_disableIrqs(); // 1
auto refCount = _refCount;
_object = newObject;
_refCount = newRefCount;
if (increaseNewRefCount && _refCount)
{
_refCount->refCount++;
}
if (refCount && --refCount->refCount == 0) [[gnu::unlikely]]
{
refCount->weakRefCount++; // ensure the ref count is not destructed elsewhere
rtos_restoreIrqs(irq); // 1
refCount->DestructObject();
irq = rtos_disableIrqs(); // 2
if (--refCount->weakRefCount == 0) [[gnu::unlikely]]
{
rtos_restoreIrqs(irq); // 2
delete refCount;
}
else
{
rtos_restoreIrqs(irq); // 2
}
}
else
{
rtos_restoreIrqs(irq); // 1
}
}

View File

@@ -0,0 +1,134 @@
#pragma once
#include <type_traits>
#include <libtwl/rtos/rtosIrq.h>
#include "SharedPtr.h"
class AtomicSharedPtrBase
{
public:
void Reset()
{
Reset(nullptr, nullptr, false);
}
protected:
void* volatile _object;
RefCount* volatile _refCount;
AtomicSharedPtrBase()
: _object(nullptr), _refCount(nullptr) { }
AtomicSharedPtrBase(void* object, RefCount* refCount)
: _object(object), _refCount(refCount) { }
~AtomicSharedPtrBase()
{
Reset(nullptr, nullptr, false);
}
void Reset(void* newObject, RefCount* newRefCount, bool increaseNewRefCount);
};
template <class T>
class AtomicSharedPtr : public AtomicSharedPtrBase
{
public:
AtomicSharedPtr() { }
AtomicSharedPtr(std::nullptr_t) { }
AtomicSharedPtr(const SharedPtr<T>& sharedPtr)
: AtomicSharedPtrBase(sharedPtr._object, sharedPtr._refCount)
{
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->refCount);
}
}
template <class Y> requires std::assignable_from<T*&, Y*>
AtomicSharedPtr(const SharedPtr<Y>& sharedPtr)
: AtomicSharedPtrBase(static_cast<T*>(sharedPtr.GetPointer()), sharedPtr._refCount)
{
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->refCount);
}
}
template <class Y>
explicit AtomicSharedPtr(const SharedPtr<Y>& sharedPtr)
: AtomicSharedPtrBase(static_cast<T*>(sharedPtr.GetPointer()), sharedPtr._refCount)
{
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->refCount);
}
}
AtomicSharedPtr(SharedPtr<T>&& sharedPtr)
: AtomicSharedPtrBase(sharedPtr._object, sharedPtr._refCount)
{
sharedPtr._object = nullptr;
sharedPtr._refCount = nullptr;
}
template <class Y> requires std::assignable_from<T*&, Y*>
AtomicSharedPtr(SharedPtr<Y>&& sharedPtr)
: AtomicSharedPtrBase(static_cast<T*>(sharedPtr.GetPointer()), sharedPtr._refCount)
{
sharedPtr._object = nullptr;
sharedPtr._refCount = nullptr;
}
template <class Y>
explicit AtomicSharedPtr(SharedPtr<Y>&& sharedPtr)
: AtomicSharedPtrBase(static_cast<T*>(sharedPtr.GetPointer()), sharedPtr._refCount)
{
sharedPtr._object = nullptr;
sharedPtr._refCount = nullptr;
}
AtomicSharedPtr& operator=(const SharedPtr<T>& sharedPtr)
{
Reset(sharedPtr._object, sharedPtr._refCount, true);
return *this;
}
template <class Y> requires std::assignable_from<T*&, Y*>
AtomicSharedPtr<T>& operator=(const SharedPtr<Y>& sharedPtr)
{
Reset(static_cast<T*>(sharedPtr.GetPointer()), sharedPtr._refCount, true);
return *this;
}
AtomicSharedPtr& operator=(SharedPtr<T>&& sharedPtr)
{
Reset(sharedPtr._object, sharedPtr._refCount, false);
sharedPtr._object = nullptr;
sharedPtr._refCount = nullptr;
return *this;
}
template <class Y> requires std::assignable_from<T*&, Y*>
AtomicSharedPtr<T>& operator=(SharedPtr<Y>&& sharedPtr)
{
Reset(static_cast<T*>(sharedPtr.GetPointer()), sharedPtr._refCount, false);
sharedPtr._object = nullptr;
sharedPtr._refCount = nullptr;
return *this;
}
SharedPtr<T> Lock() const
{
u32 irq = rtos_disableIrqs();
auto object = static_cast<T*>(_object);
auto refCount = _refCount;
if (refCount)
{
refCount->refCount++;
}
rtos_restoreIrqs(irq);
return SharedPtr<T>(object, refCount);
}
};

View File

@@ -0,0 +1,42 @@
#pragma once
#include "SharedPtr.h"
#include "WeakPtr.h"
class EnableSharedFromThisBase
{
template <class Y>
friend class SharedPtr;
template <class Y>
friend class EnableSharedFromThis;
private:
EnableSharedFromThisBase() = default;
};
template <class T>
class EnableSharedFromThis : public EnableSharedFromThisBase
{
template <class Y>
friend class SharedPtr;
public:
SharedPtr<T> SharedFromThis()
{
return __sharedFromThisWeakPtr.Lock();
}
WeakPtr<T> WeakFromThis()
{
return __sharedFromThisWeakPtr;
}
private:
WeakPtr<T> __sharedFromThisWeakPtr;
template <class Y>
void __SetSharedFromThisWeakPtr(const SharedPtr<Y>& sharedPtr)
{
__sharedFromThisWeakPtr = WeakPtr<Y>(sharedPtr.GetPointer(), sharedPtr._refCount);
}
};

View File

@@ -0,0 +1,58 @@
#pragma once
#include <array>
extern "C" void shared_ptr_increase_ref_count(vu32& refCount);
class RefCount
{
public:
vu32 refCount;
vu32 weakRefCount;
virtual ~RefCount() = default;
virtual void DestructObject() = 0;
protected:
explicit RefCount()
: refCount(1), weakRefCount(0) { }
};
template <class T>
class StandaloneRefCount : public RefCount
{
public:
explicit StandaloneRefCount(T* object)
: _object(object) { }
void DestructObject() final
{
delete _object;
}
private:
T* _object;
};
template <class T>
class alignas(T) MakeSharedRefCount : public RefCount
{
public:
explicit MakeSharedRefCount(auto&&... args)
{
new (_object.data()) T(std::forward<decltype(args)>(args)...);
}
T* GetObject()
{
return reinterpret_cast<T*>(_object.data());
}
void DestructObject() final
{
reinterpret_cast<T*>(_object.data())->~T();
}
private:
std::array<u8, sizeof(T)> _object alignas(T);
};

View File

@@ -0,0 +1,31 @@
#include "common.h"
#include <libtwl/rtos/rtosIrq.h>
#include "SharedPtr.h"
void SharedPtrBase::ResetIntern()
{
auto refCount = _refCount;
_object = nullptr;
_refCount = nullptr;
u32 irq = rtos_disableIrqs(); // 1
if (--refCount->refCount == 0) [[gnu::unlikely]]
{
refCount->weakRefCount++; // ensure the ref count is not destructed elsewhere
rtos_restoreIrqs(irq); // 1
refCount->DestructObject();
irq = rtos_disableIrqs(); // 2
if (--refCount->weakRefCount == 0) [[gnu::unlikely]]
{
rtos_restoreIrqs(irq); // 2
delete refCount;
}
else
{
rtos_restoreIrqs(irq); // 2
}
}
else
{
rtos_restoreIrqs(irq); // 1
}
}

View File

@@ -1,163 +1,216 @@
#pragma once #pragma once
#include <type_traits>
#include "RefCount.h"
static inline u32 arm_getCpsr() class EnableSharedFromThisBase;
{
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 T> template <class T>
class SharedPtr class EnableSharedFromThis;
{
T* _pointer;
vu32* _refCount;
class SharedPtrBase
{
public: public:
SharedPtr() void Reset()
: _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(); if (_refCount != nullptr)
_pointer = other._pointer;
_refCount = other._refCount;
if (_pointer)
{ {
(*_refCount)++; ResetIntern();
} }
arm_restoreIrqs(irq);
} }
SharedPtr(SharedPtr&& other) protected:
: _pointer(other._pointer), _refCount(other._refCount) void* _object;
{ RefCount* _refCount;
other._pointer = nullptr;
other._refCount = nullptr;
}
~SharedPtr() SharedPtrBase()
: _object(nullptr), _refCount(nullptr) { }
SharedPtrBase(void* object, RefCount* refCount)
: _object(object), _refCount(refCount) { }
SharedPtrBase(const void* object, RefCount* refCount)
: _object((void*)object), _refCount(refCount) { }
~SharedPtrBase()
{ {
Reset(); Reset();
} }
[[gnu::noinline]] void ResetIntern();
};
template <class T>
class SharedPtr : public SharedPtrBase
{
template <class Y> friend class WeakPtr;
template <class Y> friend class SharedPtr;
template <class Y> friend class AtomicSharedPtr;
template <class Y> friend class EnableSharedFromThis;
public:
SharedPtr() { }
SharedPtr(std::nullptr_t) { }
explicit SharedPtr(T* object)
: SharedPtrBase(object, object == nullptr ? nullptr : new StandaloneRefCount<T>(object))
{
if (_object != nullptr)
{
if constexpr (std::is_convertible<T*, EnableSharedFromThisBase*>::value)
{
_refCount->weakRefCount = 1;
GetPointer()->__SetSharedFromThisWeakPtr(*this);
}
}
}
template <class Y> requires std::assignable_from<T*&, Y*>
explicit SharedPtr(Y* object)
: SharedPtrBase(static_cast<T*>(object), object == nullptr ? nullptr : new StandaloneRefCount<Y>(object))
{
if (object != nullptr)
{
if constexpr (std::is_convertible<Y*, EnableSharedFromThisBase*>::value)
{
_refCount->weakRefCount = 1;
GetPointer()->__SetSharedFromThisWeakPtr(*this);
}
}
}
SharedPtr(const SharedPtr& other)
: SharedPtrBase(other._object, other._refCount)
{
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->refCount);
}
}
template <class Y> requires std::assignable_from<T*&, Y*>
SharedPtr(const SharedPtr<Y>& other)
: SharedPtrBase(static_cast<T*>(other.GetPointer()), other._refCount)
{
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->refCount);
}
}
template <class Y>
explicit SharedPtr(const SharedPtr<Y>& other)
: SharedPtrBase(static_cast<T*>(other.GetPointer()), other._refCount)
{
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->refCount);
}
}
SharedPtr(SharedPtr&& other)
: SharedPtrBase(other._object, other._refCount)
{
other._object = nullptr;
other._refCount = nullptr;
}
template <class Y> requires std::assignable_from<T*&, Y*>
SharedPtr(SharedPtr<Y>&& other)
: SharedPtrBase(static_cast<T*>(other.GetPointer()), other._refCount)
{
other._object = nullptr;
other._refCount = nullptr;
}
template <class Y>
explicit SharedPtr(SharedPtr<Y>&& other)
: SharedPtrBase(static_cast<T*>(other.GetPointer()), other._refCount)
{
other._object = nullptr;
other._refCount = nullptr;
}
static SharedPtr<T> MakeShared(auto&&... args)
{
auto refCount = new MakeSharedRefCount<T>(std::forward<decltype(args)>(args)...);
return SharedPtr<T>(refCount->GetObject(), refCount, true);
}
SharedPtr& operator=(const SharedPtr& other) SharedPtr& operator=(const SharedPtr& other)
{ {
u32 irq = arm_disableIrqs(); Reset();
T* pointer = _pointer; _object = other._object;
if (pointer)
{
vu32* refCount = _refCount;
u32 newValue = *refCount - 1;
*refCount = newValue;
_pointer = other._pointer;
_refCount = other._refCount; _refCount = other._refCount;
if (_pointer) if (_refCount)
{ {
(*_refCount)++; shared_ptr_increase_ref_count(_refCount->refCount);
} }
arm_restoreIrqs(irq); return *this;
if (newValue == 0) }
{
delete pointer; template <class Y> requires std::assignable_from<T*&, Y*>
delete refCount; SharedPtr<T>& operator=(const SharedPtr<Y>& other)
} {
} Reset();
else _object = static_cast<T*>(other.GetPointer());
{ _refCount = other._refCount;
_pointer = other._pointer; if (_refCount)
_refCount = other._refCount; {
if (_pointer) shared_ptr_increase_ref_count(_refCount->refCount);
{
(*_refCount)++;
}
arm_restoreIrqs(irq);
} }
return *this; return *this;
} }
[[gnu::noinline]]
SharedPtr& operator=(SharedPtr&& other) SharedPtr& operator=(SharedPtr&& other)
{ {
u32 irq = arm_disableIrqs(); Reset();
T* pointer = _pointer; _object = other._object;
if (pointer)
{
vu32* refCount = _refCount;
u32 newValue = *refCount - 1;
*refCount = newValue;
_pointer = other._pointer;
_refCount = other._refCount; _refCount = other._refCount;
other._pointer = nullptr; other._object = nullptr;
other._refCount = 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; return *this;
} }
[[gnu::noinline]] template <class Y> requires std::assignable_from<T*&, Y*>
void Reset() SharedPtr<T>& operator=(SharedPtr<Y>&& other)
{ {
u32 irq = arm_disableIrqs(); Reset();
T* pointer = _pointer; _object = static_cast<T*>(other.GetPointer());
if (pointer) _refCount = other._refCount;
{ other._object = nullptr;
vu32* refCount = _refCount; other._refCount = nullptr;
u32 newValue = *refCount - 1; return *this;
*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; } T& operator*() const { return *static_cast<T*>(_object); }
constexpr T* operator->() const { return _pointer; } T* operator->() const { return static_cast<T*>(_object); }
T* GetPointer() const { return static_cast<T*>(_object); }
constexpr T* GetPointer() const { return _pointer; } bool IsValid() const { return _object != nullptr; }
constexpr u32 GetRefCount() const { return _refCount ? *_refCount : 0; } operator bool() const { return _object != nullptr; }
constexpr bool IsValid() const { return _pointer; }
private:
SharedPtr(T* object, RefCount* refCount)
: SharedPtrBase(object, refCount) { }
SharedPtr(T* object, RefCount* refCount, bool doSharedFromThis)
: SharedPtrBase(object, refCount)
{
if (doSharedFromThis)
{
if constexpr (std::is_convertible<T*, EnableSharedFromThisBase*>::value)
{
_refCount->weakRefCount = 1;
GetPointer()->__SetSharedFromThisWeakPtr(*this);
}
}
}
}; };
#define SHARED_ONLY(className) \
friend class MakeSharedRefCount<className>; \
public: \
static SharedPtr<className> CreateShared(auto&&... args) \
{ return SharedPtr<className>::MakeShared(std::forward<decltype(args)>(args)...); } \
private:

View File

@@ -0,0 +1,15 @@
.section .itcm
.arm
// r0 = &refCount
.global shared_ptr_increase_ref_count
.type shared_ptr_increase_ref_count, %function
shared_ptr_increase_ref_count:
mrs r2, cpsr
orr r1, r2, #0x80
msr cpsr_c, r1
ldr r12, [r0]
add r12, r12, #1
str r12, [r0]
msr cpsr_c, r2
bx lr

View File

@@ -0,0 +1,35 @@
#include "common.h"
#include <libtwl/rtos/rtosIrq.h>
#include "WeakPtr.h"
void WeakPtrBase::ResetIntern()
{
u32 irq = rtos_disableIrqs();
auto refCount = _refCount;
if (--refCount->weakRefCount == 0)
{
_refCount = nullptr;
rtos_restoreIrqs(irq);
delete refCount;
}
else
{
rtos_restoreIrqs(irq);
}
}
bool WeakPtrBase::LockIntern() const
{
u32 irq = rtos_disableIrqs();
if (_refCount->refCount != 0)
{
_refCount->refCount++;
rtos_restoreIrqs(irq);
return true;
}
else
{
rtos_restoreIrqs(irq);
return false;
}
}

158
arm9/source/core/WeakPtr.h Normal file
View File

@@ -0,0 +1,158 @@
#pragma once
#include <type_traits>
#include "RefCount.h"
#include "SharedPtr.h"
class WeakPtrBase
{
public:
~WeakPtrBase()
{
Reset();
}
void Reset()
{
if (_refCount)
{
Reset();
}
}
protected:
RefCount* _refCount;
WeakPtrBase()
: _refCount(nullptr) { }
explicit WeakPtrBase(RefCount* refCount)
: _refCount(refCount) { }
void ResetIntern();
bool LockIntern() const;
};
template <class T>
class WeakPtr : public WeakPtrBase
{
template <class Y> friend class WeakPtr;
template <class Y> friend class EnableSharedFromThis;
public:
WeakPtr() { }
WeakPtr(std::nullptr_t) { }
WeakPtr(const WeakPtr& other)
: WeakPtrBase(other._refCount), _object(other._object)
{
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->weakRefCount);
}
}
WeakPtr(WeakPtr&& other)
: WeakPtrBase(other._refCount), _object(other._object)
{
other._refCount = nullptr;
other._object = nullptr;
}
template <class Y> requires std::assignable_from<T*&, Y*>
WeakPtr(const SharedPtr<Y>& sharedPtr)
: WeakPtrBase(sharedPtr._refCount), _object(static_cast<T*>(sharedPtr.GetPointer()))
{
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->weakRefCount);
}
}
template <class Y>
explicit WeakPtr(const SharedPtr<Y>& sharedPtr)
: WeakPtrBase(sharedPtr._refCount), _object(static_cast<T*>(sharedPtr.GetPointer()))
{
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->weakRefCount);
}
}
WeakPtr& operator=(const WeakPtr& other)
{
Reset();
_object = other._object;
_refCount = other._refCount;
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->weakRefCount);
}
return *this;
}
template <class Y> requires std::assignable_from<T*&, Y*>
WeakPtr<T>& operator=(const WeakPtr<Y>& other)
{
Reset();
_object = static_cast<T*>(static_cast<Y*>(other._object));
_refCount = other._refCount;
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->weakRefCount);
}
return *this;
}
template <class Y> requires std::assignable_from<T*&, Y*>
WeakPtr<T>& operator=(const SharedPtr<Y>& sharedPtr)
{
Reset();
_object = static_cast<T*>(sharedPtr.GetPointer());
_refCount = sharedPtr._refCount;
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->weakRefCount);
}
return *this;
}
WeakPtr& operator=(WeakPtr&& other)
{
Reset();
_object = other._object;
_refCount = other._refCount;
other._object = nullptr;
other._refCount = nullptr;
return *this;
}
template <class Y> requires std::assignable_from<T*&, Y*>
WeakPtr<T>& operator=(WeakPtr<Y>&& other)
{
Reset();
_object = static_cast<T*>(static_cast<Y*>(other._object));
_refCount = other._refCount;
other._object = nullptr;
other._refCount = nullptr;
return *this;
}
SharedPtr<T> Lock() const
{
if (_refCount && LockIntern())
{
return SharedPtr<T>(_object, _refCount);
}
else
{
return SharedPtr<T>();
}
}
private:
T* _object;
WeakPtr(T* object, RefCount* refCount)
: WeakPtrBase(refCount), _object(object) { }
};

View File

@@ -75,3 +75,5 @@ public:
return Rgb(r + other.r, g + other.g, b + other.b); return Rgb(r + other.r, g + other.g, b + other.b);
} }
}; };
using Rgb8 = Rgb<8, 8, 8>;

View File

@@ -161,11 +161,27 @@ public:
return FromRawValue(this->_value >> rhs); return FromRawValue(this->_value >> rhs);
} }
constexpr fix16 abs() const constexpr fix16 Abs() const
{ {
return FromRawValue(std::abs(this->_value)); return FromRawValue(std::abs(this->_value));
} }
constexpr fix16 Clamp(fix16 min, fix16 max) const
{
if (this->_value < min._value)
{
return min;
}
else if (this->_value > max._value)
{
return max;
}
else
{
return *this;
}
}
template <u32 OtherFractionBits> template <u32 OtherFractionBits>
constexpr fix32<FractionBits + OtherFractionBits> LongMul(const fix16<OtherFractionBits>& other) const constexpr fix32<FractionBits + OtherFractionBits> LongMul(const fix16<OtherFractionBits>& other) const
{ {
@@ -265,21 +281,45 @@ public:
return this->_value < other._value; return this->_value < other._value;
} }
template <typename TLhs>
constexpr friend bool operator<(TLhs lhs, const fix32& rhs)
{
return fix32(lhs)._value < rhs._value;
}
constexpr bool operator<=(const fix32& other) const constexpr bool operator<=(const fix32& other) const
{ {
return this->_value <= other._value; return this->_value <= other._value;
} }
template <typename TLhs>
constexpr friend bool operator<=(TLhs lhs, const fix32& rhs)
{
return fix32(lhs)._value <= rhs._value;
}
constexpr bool operator>(const fix32& other) const constexpr bool operator>(const fix32& other) const
{ {
return this->_value > other._value; return this->_value > other._value;
} }
template <typename TLhs>
constexpr friend bool operator>(TLhs lhs, const fix32& rhs)
{
return fix32(lhs)._value > rhs._value;
}
constexpr bool operator>=(const fix32& other) const constexpr bool operator>=(const fix32& other) const
{ {
return this->_value >= other._value; return this->_value >= other._value;
} }
template <typename TLhs>
constexpr friend bool operator>=(TLhs lhs, const fix32& rhs)
{
return fix32(lhs)._value >= rhs._value;
}
constexpr fix32 operator-() const constexpr fix32 operator-() const
{ {
return FromRawValue(-this->_value); return FromRawValue(-this->_value);
@@ -337,6 +377,22 @@ public:
return FromRawValue(std::abs(this->_value)); return FromRawValue(std::abs(this->_value));
} }
constexpr fix32 Clamp(fix32 min, fix32 max) const
{
if (this->_value < min._value)
{
return min;
}
else if (this->_value > max._value)
{
return max;
}
else
{
return *this;
}
}
template <u32 OtherFractionBits> template <u32 OtherFractionBits>
constexpr fix64<FractionBits + OtherFractionBits> LongMul(const fix16<OtherFractionBits>& other) const constexpr fix64<FractionBits + OtherFractionBits> LongMul(const fix16<OtherFractionBits>& other) const
{ {
@@ -354,6 +410,11 @@ public:
return fix64<FractionBits>::FromRawValue((s64)this->_value * other); return fix64<FractionBits>::FromRawValue((s64)this->_value * other);
} }
constexpr fix64<FractionBits> LongMul(double other) const
{
return LongMul(fix32(other));
}
template <u32 OtherFractionBits> template <u32 OtherFractionBits>
constexpr fix32 operator*(const fix16<OtherFractionBits>& other) const constexpr fix32 operator*(const fix16<OtherFractionBits>& other) const
{ {
@@ -371,6 +432,11 @@ public:
return FromRawValue(this->_value * other); return FromRawValue(this->_value * other);
} }
constexpr fix32 operator*(double other) const
{
return fix32(LongMul(fix32(other)));
}
constexpr friend fix32 operator*(int lhs, const fix32& rhs) constexpr friend fix32 operator*(int lhs, const fix32& rhs)
{ {
return FromRawValue(lhs * rhs.GetRawValue()); return FromRawValue(lhs * rhs.GetRawValue());

View File

@@ -1,9 +1,20 @@
#include "common.h" #include "common.h"
#include "Task.h" #include "Task.h"
void TaskBase::Execute() void TaskBase::RequestCancel()
{ {
u32 irqs = rtos_disableIrqs(); u32 irqs = rtos_disableIrqs();
_cancelRequested = true;
if (_state == TaskState::NotStarted)
{
_state = TaskState::Canceled;
rtos_wakeupQueue(&_threadQueue);
}
rtos_restoreIrqs(irqs);
}
void TaskBase::Execute(u32 irqs)
{
if (_state == TaskState::NotStarted) if (_state == TaskState::NotStarted)
{ {
_state = TaskState::Running; _state = TaskState::Running;
@@ -12,8 +23,10 @@ void TaskBase::Execute()
SetFinalState(finalState); SetFinalState(finalState);
} }
else else
{
rtos_restoreIrqs(irqs); rtos_restoreIrqs(irqs);
} }
}
void TaskBase::SetFinalState(TaskState finalState) void TaskBase::SetFinalState(TaskState finalState)
{ {

View File

@@ -3,14 +3,17 @@
#include <memory> #include <memory>
#include <libtwl/rtos/rtosIrq.h> #include <libtwl/rtos/rtosIrq.h>
#include <libtwl/rtos/rtosThread.h> #include <libtwl/rtos/rtosThread.h>
#include "core/LinkedListLink.h"
#include "TaskResult.h" #include "TaskResult.h"
class TaskBase class TaskBase
{ {
public: public:
LinkedListLink link;
virtual ~TaskBase() { } virtual ~TaskBase() { }
void Execute(); void Execute(u32 irqs);
TaskState GetState() const { return _state; } TaskState GetState() const { return _state; }
@@ -24,7 +27,7 @@ public:
bool GetDestroyWhenComplete() const { return _destroyWhenComplete; } bool GetDestroyWhenComplete() const { return _destroyWhenComplete; }
void SetDestroyWhenComplete() { _destroyWhenComplete = true; } void SetDestroyWhenComplete() { _destroyWhenComplete = true; }
void RequestCancel() { _cancelRequested = true; } void RequestCancel();
bool IsCancelRequested() const { return _cancelRequested; } bool IsCancelRequested() const { return _cancelRequested; }
protected: protected:
@@ -65,7 +68,7 @@ template <>
class Task<void> : public TaskBase class Task<void> : public TaskBase
{ {
protected: protected:
virtual TaskResult<void> ExecuteFunc() const = 0; virtual TaskResult<void> ExecuteFunc() = 0;
private: private:
TaskState ExecuteDirect() override TaskState ExecuteDirect() override
@@ -79,11 +82,11 @@ template <class T, typename FuncType>
class FuncTask : public Task<T> class FuncTask : public Task<T>
{ {
public: public:
FuncTask(const FuncType& function) FuncTask(FuncType&& function)
: _function(function) { } : _function(std::move(function)) { }
private: private:
const FuncType _function; FuncType _function;
TaskResult<T> ExecuteFunc() const override { return _function((const volatile u8&)this->_cancelRequested); } TaskResult<T> ExecuteFunc() override { return _function((const volatile u8&)this->_cancelRequested); }
}; };

View File

@@ -1,23 +1,22 @@
#include "common.h" #include "common.h"
#include "TaskQueue.h" #include "TaskQueue.h"
void TaskQueueBase::ThreadMain(TaskBase** queue, u32 queueLength) void TaskQueueBase::ThreadMain()
{ {
while (true) while (true)
{ {
_idle = false; _idle = false;
u32 readPtr = _queueReadPtr; while (true)
while (readPtr != _queueWritePtr)
{ {
TaskBase* task = queue[readPtr]; u32 irqs = rtos_disableIrqs();
if (readPtr == queueLength - 1) auto task = _taskList.GetHead();
readPtr = 0;
else
readPtr++;
_queueReadPtr = readPtr;
if (!task) if (!task)
continue; {
task->Execute(); rtos_restoreIrqs(irqs);
break;
}
_taskList.Remove(task);
task->Execute(irqs);
if (task->GetDestroyWhenComplete()) if (task->GetDestroyWhenComplete())
{ {
// this will destroy the task // this will destroy the task

View File

@@ -4,6 +4,7 @@
#include <libtwl/rtos/rtosEvent.h> #include <libtwl/rtos/rtosEvent.h>
#include <libtwl/rtos/rtosThread.h> #include <libtwl/rtos/rtosThread.h>
#include "core/BitVector.h" #include "core/BitVector.h"
#include "core/LinkedList.h"
#include "Task.h" #include "Task.h"
class TaskQueueBase; class TaskQueueBase;
@@ -11,26 +12,14 @@ class TaskQueueBase;
class QueueTaskBase class QueueTaskBase
{ {
public: public:
// forbid copies
QueueTaskBase(const QueueTaskBase&) = delete;
QueueTaskBase& operator=(const QueueTaskBase&) = delete;
// move assignment
QueueTaskBase& operator=(QueueTaskBase&& other)
{
_taskQueue = other._taskQueue;
_task = other._task;
other._taskQueue = nullptr;
other._task = nullptr;
return *this;
}
~QueueTaskBase() ~QueueTaskBase()
{ {
// extra check here for optimizing out the dispose call after move assignment // extra check here for optimizing out the dispose call after move assignment
if (_task) if (_task)
{
Dispose(); Dispose();
} }
}
void Dispose(); void Dispose();
@@ -47,12 +36,10 @@ public:
protected: protected:
TaskBase* _task; TaskBase* _task;
TaskQueueBase* _taskQueue;
QueueTaskBase(TaskBase* task, TaskQueueBase* taskQueue) QueueTaskBase(TaskBase* task, TaskQueueBase* taskQueue)
: _task(task), _taskQueue(taskQueue) { } : _task(task), _taskQueue(taskQueue) { }
private:
TaskQueueBase* _taskQueue;
}; };
template <typename T> template <typename T>
@@ -62,6 +49,26 @@ public:
QueueTask() QueueTask()
: QueueTaskBase(nullptr, nullptr) { } : QueueTaskBase(nullptr, nullptr) { }
QueueTask(const QueueTask&) = delete;
QueueTask& operator=(const QueueTask&) = delete;
QueueTask(QueueTask&& other)
: QueueTaskBase(other._task, other._taskQueue)
{
other._task = nullptr;
other._taskQueue = nullptr;
}
QueueTask& operator=(QueueTask&& other)
{
Dispose();
_taskQueue = other._taskQueue;
_task = other._task;
other._taskQueue = nullptr;
other._task = nullptr;
return *this;
}
QueueTask(Task<T>* task, TaskQueueBase* taskQueue) QueueTask(Task<T>* task, TaskQueueBase* taskQueue)
: QueueTaskBase(task, taskQueue) { } : QueueTaskBase(task, taskQueue) { }
@@ -79,24 +86,23 @@ public:
template <typename FuncType> template <typename FuncType>
[[gnu::noinline]] [[gnu::noinline]]
auto Enqueue(const FuncType& function) -> QueueTask<decltype(TaskResultToResultType(function(*new vu8())))> auto Enqueue(FuncType&& function) -> QueueTask<decltype(TaskResultToResultType(function(*new vu8())))>
{ {
using TaskType = FuncTask<decltype(TaskResultToResultType(function(*new vu8()))), FuncType>; using TaskType = FuncTask<decltype(TaskResultToResultType(function(*new vu8()))), FuncType>;
// static_assert(sizeof(TaskType) <= MaxTaskSize, "Task is too big for this pool"); // static_assert(sizeof(TaskType) <= MaxTaskSize, "Task is too big for this pool");
void* slot = GetSlot(); void* slot = GetSlot();
auto task = new (slot) TaskType(function); auto task = new (slot) TaskType(std::move(function));
Enqueue(task); Enqueue(task);
return QueueTask(task, this); return QueueTask(task, this);
} }
protected: protected:
rtos_event_t _event; rtos_event_t _event;
vu32 _queueReadPtr = 0; LinkedList<TaskBase, &TaskBase::link> _taskList;
vu32 _queueWritePtr = 0;
volatile bool _endThreadWhenDone = false; volatile bool _endThreadWhenDone = false;
volatile bool _idle = true; volatile bool _idle = true;
void ThreadMain(TaskBase** queue, u32 queueLength); void ThreadMain();
TaskQueueBase() TaskQueueBase()
{ {
@@ -124,6 +130,10 @@ public:
u32 irqs = rtos_disableIrqs(); u32 irqs = rtos_disableIrqs();
if (task->IsCompleted()) if (task->IsCompleted())
{ {
if (task->link.prev != nullptr || task->link.next != nullptr)
{
_taskList.Remove(task);
}
task->~TaskBase(); task->~TaskBase();
u32 slot = ((u32)task - (u32)_taskPool) / ((MaxTaskSize + 3) & ~3); u32 slot = ((u32)task - (u32)_taskPool) / ((MaxTaskSize + 3) & ~3);
_poolOccupation.Set(slot, 0); _poolOccupation.Set(slot, 0);
@@ -159,13 +169,12 @@ public:
bool IsIdle() const bool IsIdle() const
{ {
return _queueReadPtr == _queueWritePtr && _idle; return _taskList.GetHead() == nullptr && _idle;
} }
private: private:
u32 _taskPool[QueueLength][(MaxTaskSize + 3) / 4]; u32 _taskPool[QueueLength][(MaxTaskSize + 3) / 4];
BitVector<QueueLength> _poolOccupation; BitVector<QueueLength> _poolOccupation;
TaskBase* _queue[QueueLength];
rtos_thread_t _thread; rtos_thread_t _thread;
bool _threadStarted = false; bool _threadStarted = false;
@@ -191,12 +200,11 @@ private:
[[gnu::noinline]] [[gnu::noinline]]
void Enqueue(TaskBase* task) override void Enqueue(TaskBase* task) override
{ {
u32 writePtr = _queueWritePtr; u32 irqs = rtos_disableIrqs();
_queue[writePtr] = task; {
if (writePtr == QueueLength - 1) _taskList.InsertTail(task);
_queueWritePtr = 0; }
else rtos_restoreIrqs(irqs);
_queueWritePtr = writePtr + 1;
rtos_signalEvent(&_event); rtos_signalEvent(&_event);
} }
@@ -207,6 +215,6 @@ private:
void ThreadMain() void ThreadMain()
{ {
TaskQueueBase::ThreadMain(&_queue[0], QueueLength); TaskQueueBase::ThreadMain();
} }
}; };

View File

@@ -60,6 +60,16 @@ extern "C" void* memalign(size_t alignment, size_t size)
return result; return result;
} }
extern "C" void* calloc(size_t num, size_t size)
{
void* result = malloc(num * size);
if (result)
{
memset(result, 0, num * size);
}
return result;
}
void* operator new(std::size_t blocksize) void* operator new(std::size_t blocksize)
{ {
return malloc(blocksize); return malloc(blocksize);

View File

@@ -89,14 +89,8 @@ u32 AdvancedPaletteManagerBase::TryMerge(PaletteRow* rows, const PaletteRow& new
u32 AdvancedPaletteManagerBase::AllocRowInternal(PaletteRow* rows, const IPalette& palette, int yStart, int yEnd) u32 AdvancedPaletteManagerBase::AllocRowInternal(PaletteRow* rows, const IPalette& palette, int yStart, int yEnd)
{ {
if (yStart < 0) yStart = std::clamp(yStart, 0, 192);
{ yEnd = std::clamp(yEnd, 0, 192);
yStart = 0;
}
if (yEnd > 192)
{
yEnd = 192;
}
u32 newIdx = _usedRows; u32 newIdx = _usedRows;
PaletteRow& newRow = rows[newIdx]; PaletteRow& newRow = rows[newIdx];

View File

@@ -84,6 +84,7 @@ public:
u32 AllocRow(const IPalette& palette, int yStart, int yEnd) override u32 AllocRow(const IPalette& palette, int yStart, int yEnd) override
{ {
yEnd++; // avoid back to back allocation
return AllocRowInternal(_rows[_curSet], palette, yStart, yEnd); return AllocRowInternal(_rows[_curSet], palette, yStart, yEnd);
} }

View File

@@ -3,7 +3,7 @@
#include "input/InputProvider.h" #include "input/InputProvider.h"
#include "FocusManager.h" #include "FocusManager.h"
void FocusManager::Focus(View* newFocus) void FocusManager::Focus(const SharedPtr<View>& newFocus)
{ {
if (!newFocus) if (!newFocus)
{ {
@@ -11,48 +11,71 @@ void FocusManager::Focus(View* newFocus)
return; return;
} }
if (_currentFocus) if (auto currentFocus = _currentFocus.Lock())
_currentFocus->SetFocused(false); {
currentFocus->SetFocused(false);
}
newFocus->SetFocused(true); newFocus->SetFocused(true);
_currentFocus = newFocus; _currentFocus = newFocus;
} }
void FocusManager::Unfocus() void FocusManager::Unfocus()
{ {
if (_currentFocus) if (auto currentFocus = _currentFocus.Lock())
_currentFocus->SetFocused(false); {
_currentFocus = nullptr; currentFocus->SetFocused(false);
}
_currentFocus.Reset();
} }
void FocusManager::Update(const InputProvider& inputProvider) void FocusManager::Update(const InputProvider& inputProvider)
{ {
if (!_currentFocus || !_currentFocus->GetParent()) auto currentFocus = _currentFocus.Lock();
if (!currentFocus || !currentFocus->GetParent())
return; // todo return; // todo
View* newFocus = nullptr; SharedPtr<View> newFocus;
if (inputProvider.Triggered(InputKey::DpadUp)) if (inputProvider.Triggered(InputKey::DpadUp))
newFocus = _currentFocus->GetParent()->MoveFocus(_currentFocus, FocusMoveDirection::Up, _currentFocus); {
newFocus = currentFocus->GetParent()->MoveFocus(currentFocus, FocusMoveDirection::Up, currentFocus.GetPointer());
}
else if (inputProvider.Triggered(InputKey::DpadDown)) else if (inputProvider.Triggered(InputKey::DpadDown))
newFocus = _currentFocus->GetParent()->MoveFocus(_currentFocus, FocusMoveDirection::Down, _currentFocus); {
newFocus = currentFocus->GetParent()->MoveFocus(currentFocus, FocusMoveDirection::Down, currentFocus.GetPointer());
}
else if (inputProvider.Triggered(InputKey::DpadLeft)) else if (inputProvider.Triggered(InputKey::DpadLeft))
newFocus = _currentFocus->GetParent()->MoveFocus(_currentFocus, FocusMoveDirection::Left, _currentFocus); {
newFocus = currentFocus->GetParent()->MoveFocus(currentFocus, FocusMoveDirection::Left, currentFocus.GetPointer());
}
else if (inputProvider.Triggered(InputKey::DpadRight)) else if (inputProvider.Triggered(InputKey::DpadRight))
newFocus = _currentFocus->GetParent()->MoveFocus(_currentFocus, FocusMoveDirection::Right, _currentFocus); {
newFocus = currentFocus->GetParent()->MoveFocus(currentFocus, FocusMoveDirection::Right, currentFocus.GetPointer());
}
else else
_currentFocus->HandleInput(inputProvider, *this); {
currentFocus->HandleInput(inputProvider, *this);
}
if (newFocus) if (newFocus)
{
Focus(newFocus); Focus(newFocus);
} }
}
bool FocusManager::IsFocusInside(const View* view) const bool FocusManager::IsFocusInside(const View* view) const
{ {
auto focusView = _currentFocus; if (auto currentFocus = _currentFocus.Lock())
{
auto focusView = currentFocus.GetPointer();
while (focusView) while (focusView)
{ {
if (view == focusView) if (view == focusView)
{
return true; return true;
}
focusView = focusView->GetParent(); focusView = focusView->GetParent();
} }
}
return false; return false;
} }

View File

@@ -1,4 +1,6 @@
#pragma once #pragma once
#include <core/SharedPtr.h>
#include <core/WeakPtr.h>
class View; class View;
class InputProvider; class InputProvider;
@@ -9,7 +11,7 @@ class FocusManager
public: public:
/// @brief Focuses the given view. /// @brief Focuses the given view.
/// @param newFocus The view to focus. /// @param newFocus The view to focus.
void Focus(View* newFocus); void Focus(const SharedPtr<View>& newFocus);
/// @brief Clears the current focus. /// @brief Clears the current focus.
void Unfocus(); void Unfocus();
@@ -20,7 +22,7 @@ public:
/// @brief Gets the currently focused view. /// @brief Gets the currently focused view.
/// @return A pointer to the view that is currently focused, or null if none. /// @return A pointer to the view that is currently focused, or null if none.
constexpr View* GetCurrentFocus() const { return _currentFocus; } constexpr SharedPtr<View> GetCurrentFocus() const { return _currentFocus.Lock(); }
/// @brief Checks whether the current focus lies inside the given view. /// @brief Checks whether the current focus lies inside the given view.
/// @param view The view to check for. /// @param view The view to check for.
@@ -28,5 +30,5 @@ public:
bool IsFocusInside(const View* view) const; bool IsFocusInside(const View* view) const;
private: private:
View* _currentFocus = nullptr; WeakPtr<View> _currentFocus;
}; };

View File

@@ -28,24 +28,28 @@ int nft2_findGlyphIdxForCharacter(const nft2_header_t* font, u16 character)
} }
static inline void renderGlyph(const nft2_header_t* font, const nft2_glyph_t* glyph, static inline void renderGlyph(const nft2_header_t* font, const nft2_glyph_t* glyph,
int xPos, int yPos, int width, int height, u8* dst, u32 stride, bool a5i3) int xPos, int yPos, const nft2_string_render_params_t* renderParams, u8* dst, u32 stride, bool a5i3)
{ {
int yOffset = glyph->spacingTop; int yOffset = glyph->spacingTop;
u32 xStart = xPos < 0 ? -xPos : 0; u32 xStart = xPos < 0 ? -xPos : 0;
u32 yStart = yPos + yOffset < 0 ? -(yPos + yOffset) : 0; u32 yStart = yPos + yOffset < 0 ? -(yPos + yOffset) : 0;
int xEnd = glyph->glyphWidth; int xEnd = glyph->glyphWidth;
if (xPos + xEnd > width) if (xPos + xEnd > (int)renderParams->width)
{
if (renderParams->onlyRenderWholeGlyphs)
{ {
// by returning we only render complete glyphs
return; return;
// old code for rendering partial glyphs }
// xEnd = width - xPos;
xEnd = renderParams->width - xPos;
} }
int yEnd = glyph->glyphHeight; int yEnd = glyph->glyphHeight;
if (yPos + yOffset + yEnd > height) if (yPos + yOffset + yEnd > (int)renderParams->height)
yEnd = height - (yPos + yOffset); // allow partial glyphs in the vertical direction {
yEnd = renderParams->height - (yPos + yOffset); // allow partial glyphs in the vertical direction
}
const u8* glyphData = &font->glyphDataPtr[glyph->dataOffset]; const u8* glyphData = &font->glyphDataPtr[glyph->dataOffset];
glyphData += yStart * ((glyph->glyphWidth + 1) >> 1); glyphData += yStart * ((glyph->glyphWidth + 1) >> 1);
@@ -97,15 +101,15 @@ static inline void renderGlyph(const nft2_header_t* font, const nft2_glyph_t* gl
} }
static ITCM_CODE void renderGlyphTiled(const nft2_header_t* font, const nft2_glyph_t* glyph, static ITCM_CODE void renderGlyphTiled(const nft2_header_t* font, const nft2_glyph_t* glyph,
int xPos, int yPos, int width, int height, u8* dst, u32 stride) int xPos, int yPos, const nft2_string_render_params_t* renderParams, u8* dst, u32 stride)
{ {
renderGlyph(font, glyph, xPos, yPos, width, height, dst, stride, false); renderGlyph(font, glyph, xPos, yPos, renderParams, dst, stride, false);
} }
static ITCM_CODE void renderGlyphA5I3(const nft2_header_t* font, const nft2_glyph_t* glyph, static ITCM_CODE void renderGlyphA5I3(const nft2_header_t* font, const nft2_glyph_t* glyph,
int xPos, int yPos, int width, int height, u8* dst, u32 stride) int xPos, int yPos, const nft2_string_render_params_t* renderParams, u8* dst, u32 stride)
{ {
renderGlyph(font, glyph, xPos, yPos, width, height, dst, stride, true); renderGlyph(font, glyph, xPos, yPos, renderParams, dst, stride, true);
} }
ITCM_CODE void nft2_renderString(const nft2_header_t* font, const char16_t* string, u8* dst, u32 stride, ITCM_CODE void nft2_renderString(const nft2_header_t* font, const char16_t* string, u8* dst, u32 stride,
@@ -134,18 +138,18 @@ ITCM_CODE void nft2_renderString(const nft2_header_t* font, const char16_t* stri
xPos += glyph->spacingLeft; xPos += glyph->spacingLeft;
if (a5i3) if (a5i3)
{ {
renderGlyphA5I3(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); renderGlyphA5I3(font, glyph, xPos, yPos, renderParams, dst, stride);
} }
else else
{ {
renderGlyphTiled(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); renderGlyphTiled(font, glyph, xPos, yPos, renderParams, dst, stride);
} }
xPos += glyph->glyphWidth; xPos += glyph->glyphWidth;
if (xPos > (int)textWidth) if (xPos > (int)textWidth)
textWidth = xPos; textWidth = xPos;
xPos += glyph->spacingRight; xPos += glyph->spacingRight;
} }
renderParams->textWidth = textWidth; renderParams->textWidth = textWidth - renderParams->x;
} }
ITCM_CODE void nft2_measureString(const nft2_header_t* font, const char16_t* string, u32& width, u32& height) ITCM_CODE void nft2_measureString(const nft2_header_t* font, const char16_t* string, u32& width, u32& height)
@@ -265,11 +269,11 @@ ITCM_CODE void nft2_renderStringEllipsis(const nft2_header_t* font, const char16
xPos += glyph->spacingLeft; xPos += glyph->spacingLeft;
if (a5i3) if (a5i3)
{ {
renderGlyphA5I3(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); renderGlyphA5I3(font, glyph, xPos, yPos, renderParams, dst, stride);
} }
else else
{ {
renderGlyphTiled(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); renderGlyphTiled(font, glyph, xPos, yPos, renderParams, dst, stride);
} }
xPos += glyph->glyphWidth; xPos += glyph->glyphWidth;
if (xPos > (int)textWidth) if (xPos > (int)textWidth)
@@ -287,11 +291,11 @@ ITCM_CODE void nft2_renderStringEllipsis(const nft2_header_t* font, const char16
xPos += glyph->spacingLeft; xPos += glyph->spacingLeft;
if (a5i3) if (a5i3)
{ {
renderGlyphA5I3(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); renderGlyphA5I3(font, glyph, xPos, yPos, renderParams, dst, stride);
} }
else else
{ {
renderGlyphTiled(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); renderGlyphTiled(font, glyph, xPos, yPos, renderParams, dst, stride);
} }
xPos += glyph->glyphWidth; xPos += glyph->glyphWidth;
if (xPos > (int)textWidth) if (xPos > (int)textWidth)
@@ -309,16 +313,16 @@ ITCM_CODE void nft2_renderStringEllipsis(const nft2_header_t* font, const char16
xPos += glyph->spacingLeft; xPos += glyph->spacingLeft;
if (a5i3) if (a5i3)
{ {
renderGlyphA5I3(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); renderGlyphA5I3(font, glyph, xPos, yPos, renderParams, dst, stride);
} }
else else
{ {
renderGlyphTiled(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); renderGlyphTiled(font, glyph, xPos, yPos, renderParams, dst, stride);
} }
xPos += glyph->glyphWidth; xPos += glyph->glyphWidth;
if (xPos > (int)textWidth) if (xPos > (int)textWidth)
textWidth = xPos; textWidth = xPos;
xPos += glyph->spacingRight; xPos += glyph->spacingRight;
} }
renderParams->textWidth = textWidth; renderParams->textWidth = textWidth - renderParams->x;
} }

View File

@@ -39,6 +39,7 @@ struct nft2_string_render_params_t
u32 textWidth; u32 textWidth;
// u32 textHeight; // u32 textHeight;
bool a5i3; bool a5i3;
bool onlyRenderWholeGlyphs = true;
}; };
/// @brief Prepares the ntf2 data of the given \p font for runtime use. /// @brief Prepares the ntf2 data of the given \p font for runtime use.

View File

@@ -2,10 +2,10 @@
#include "InputKey.h" #include "InputKey.h"
/// @brief Interface for a source of key input. /// @brief Interface for a source of key input.
class IInputSource class IKeyInputSource
{ {
public: public:
virtual ~IInputSource() { } virtual ~IKeyInputSource() { }
virtual InputKey Sample() const = 0; virtual InputKey Sample() const = 0;
}; };

View File

@@ -0,0 +1,14 @@
#pragma once
#include "core/math/Point.h"
/// @brief Interface for a source of touch input.
class ITouchInputSource
{
public:
virtual ~ITouchInputSource() { }
/// @brief Samples the touch input.
/// @param touchPosition When the pen is down, the current touch position.
/// @return \c true when the pen is down, or \c false otherwise.
virtual bool Sample(Point& touchPosition) const = 0;
};

View File

@@ -15,7 +15,9 @@ enum class InputKey : u16
L = 1 << 9, L = 1 << 9,
X = 1 << 10, X = 1 << 10,
Y = 1 << 11, Y = 1 << 11,
Debug = 1 << 13 Debug = 1 << 13,
Touch = 1 << 14,
Lid = 1 << 15
}; };
inline InputKey operator&(InputKey lhs, InputKey rhs) inline InputKey operator&(InputKey lhs, InputKey rhs)

View File

@@ -1,5 +1,5 @@
#pragma once #pragma once
#include "core/math/Point.h"
#include "InputKey.h" #include "InputKey.h"
class InputProvider class InputProvider
@@ -14,6 +14,23 @@ public:
/// @return \c true if any of the keys in the \p mask is being held, or \c false otherwise. /// @return \c true if any of the keys in the \p mask is being held, or \c false otherwise.
bool Current(InputKey mask) const { return static_cast<bool>(_currentKeys & mask); } bool Current(InputKey mask) const { return static_cast<bool>(_currentKeys & mask); }
/// @brief Returns the current touch point if the screen is being touched.
/// @param touchPoint The current touch point if the screen is being touched.
/// @return \c true if the screen is being touched, or \c false otherwise.
bool GetCurrentTouchPoint(Point& touchPoint)
{
if (Current(InputKey::Touch))
{
touchPoint = _currentTouchPoint;
return true;
}
else
{
touchPoint = Point(0, 0);
return false;
}
}
/// @brief Returns a bitmask of the keys that went from unpressed to pressed in the latest update. /// @brief Returns a bitmask of the keys that went from unpressed to pressed in the latest update.
/// @return A bitmask of the keys that went from unpressed to pressed in the latest update. /// @return A bitmask of the keys that went from unpressed to pressed in the latest update.
InputKey GetTriggeredKeys() const InputKey GetTriggeredKeys() const
@@ -46,13 +63,16 @@ public:
_currentKeys = InputKey::None; _currentKeys = InputKey::None;
_triggeredKeys = InputKey::None; _triggeredKeys = InputKey::None;
_releasedKeys = InputKey::None; _releasedKeys = InputKey::None;
_currentTouchPoint = Point(0, 0);
} }
protected: protected:
InputKey _currentKeys; InputKey _currentKeys;
InputKey _triggeredKeys; InputKey _triggeredKeys;
InputKey _releasedKeys; InputKey _releasedKeys;
Point _currentTouchPoint;
InputProvider() InputProvider()
: _currentKeys(InputKey::None), _triggeredKeys(InputKey::None), _releasedKeys(InputKey::None) { } : _currentKeys(InputKey::None), _triggeredKeys(InputKey::None), _releasedKeys(InputKey::None)
, _currentTouchPoint(0, 0) { }
}; };

View File

@@ -27,8 +27,10 @@ void InputRepeater::Update()
} }
} }
else else
{
_state = State::Idle; _state = State::Idle;
} }
}
else if (_state == State::NextRepeat) else if (_state == State::NextRepeat)
{ {
if (static_cast<bool>(curKeys & _repeatMask)) if (static_cast<bool>(curKeys & _repeatMask))
@@ -40,13 +42,24 @@ void InputRepeater::Update()
} }
} }
else else
{
_state = State::Idle; _state = State::Idle;
} }
}
InputKey lastRepKeys = _currentKeys & _repeatMask; InputKey lastRepKeys = _currentKeys & _repeatMask;
_currentKeys = curKeys | repKeys; _currentKeys = curKeys | repKeys;
_triggeredKeys = _inputProvider->GetTriggeredKeys() | repKeys; _triggeredKeys = _inputProvider->GetTriggeredKeys() | repKeys;
_releasedKeys = _inputProvider->GetReleasedKeys() | (lastRepKeys & (lastRepKeys ^ repKeys)); _releasedKeys = _inputProvider->GetReleasedKeys() | (lastRepKeys & (lastRepKeys ^ repKeys));
Point touchPoint;
if (_inputProvider->GetCurrentTouchPoint(touchPoint))
{
_currentTouchPoint = touchPoint;
}
else
{
_currentTouchPoint = Point(0, 0);
}
} }
void InputRepeater::Reset() void InputRepeater::Reset()

View File

@@ -7,7 +7,7 @@ class InputRepeater : public InputProvider
public: public:
InputRepeater(InputProvider* inputProvider, InputKey repeatMask, u16 firstRepeatDelay, u16 nextRepeatDelay) InputRepeater(InputProvider* inputProvider, InputKey repeatMask, u16 firstRepeatDelay, u16 nextRepeatDelay)
: _inputProvider(inputProvider), _state(State::Idle) : _inputProvider(inputProvider), _state(State::Idle)
, _frameCounter(0), _repeatMask(repeatMask) , _frameCounter(0), _repeatMask(repeatMask & ~InputKey::Touch)
, _firstRepeatDelayFrames(firstRepeatDelay) , _firstRepeatDelayFrames(firstRepeatDelay)
, _nextRepeatDelayFrames(nextRepeatDelay) { } , _nextRepeatDelayFrames(nextRepeatDelay) { }

View File

@@ -1,17 +1,18 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
#include <nds/input.h> #include <nds/input.h>
#include "IInputSource.h" #include "IKeyInputSource.h"
#include "sharedMemory.h" #include "sharedMemory.h"
/// @brief Input source from the physical DS buttons. /// @brief Input source from the physical DS buttons.
class PadInputSource : public IInputSource class PadInputSource : public IKeyInputSource
{ {
public: public:
InputKey Sample() const override InputKey Sample() const override
{ {
u16 arm9Mask = (~REG_KEYINPUT & 0x3FF); u16 arm9Mask = (~REG_KEYINPUT & 0x3FF);
u16 arm7Mask = (~SHARED_KEY_XY & 0xB); u16 arm7Mask = (~SHARED_KEY_XY & 0xCB);
arm7Mask = (arm7Mask & 0xB) | ((arm7Mask & 0xC0) >> 2);
return static_cast<InputKey>((arm9Mask | (arm7Mask << 10))); return static_cast<InputKey>((arm9Mask | (arm7Mask << 10)));
} }
}; };

View File

@@ -7,16 +7,44 @@ void SampledInputProvider::Update()
InputKey trig = InputKey::None; InputKey trig = InputKey::None;
InputKey rel = InputKey::None; InputKey rel = InputKey::None;
Point touchPoint(0, 0);
int touchPointCount = 0;
while (_inputBufferReadPtr != _inputBufferWritePtr) while (_inputBufferReadPtr != _inputBufferWritePtr)
{ {
InputKey nextKeys = _inputBuffer[_inputBufferReadPtr]; InputKey nextKeys = _keyInputBuffer[_inputBufferReadPtr];
trig |= (nextKeys ^ curKeys) & nextKeys; trig |= (nextKeys ^ curKeys) & nextKeys;
rel |= (nextKeys ^ curKeys) & curKeys; rel |= (nextKeys ^ curKeys) & curKeys;
curKeys = nextKeys; curKeys = nextKeys;
if ((nextKeys & InputKey::Touch) != InputKey::None)
{
touchPoint.x += _touchInputBuffer[_inputBufferReadPtr].x;
touchPoint.y += _touchInputBuffer[_inputBufferReadPtr].y;
touchPointCount++;
}
else
{
touchPoint = Point(0, 0);
touchPointCount = 0;
}
_inputBufferReadPtr = (_inputBufferReadPtr + 1) & 3; _inputBufferReadPtr = (_inputBufferReadPtr + 1) & 3;
} }
_triggeredKeys = trig; _triggeredKeys = trig;
_releasedKeys = rel; _releasedKeys = rel;
_currentKeys = curKeys; _currentKeys = curKeys;
if (touchPointCount == 0)
{
if ((_triggeredKeys & InputKey::Touch) != InputKey::None ||
(_currentKeys & InputKey::Touch) != InputKey::None)
{
_releasedKeys = _releasedKeys | InputKey::Touch;
}
_triggeredKeys = _triggeredKeys & ~InputKey::Touch;
_currentKeys = _currentKeys & ~InputKey::Touch;
}
else
{
_currentTouchPoint.x = touchPoint.x / touchPointCount;
_currentTouchPoint.y = touchPoint.y / touchPointCount;
}
} }

View File

@@ -1,20 +1,23 @@
#pragma once #pragma once
#include "InputProvider.h" #include "InputProvider.h"
#include "IInputSource.h" #include "IKeyInputSource.h"
#include "ITouchInputSource.h"
/// @brief Input provider providing input from an \see IInputSource. /// @brief Input provider providing input from an \see IKeyInputSource.
class SampledInputProvider : public InputProvider class SampledInputProvider : public InputProvider
{ {
public: public:
explicit SampledInputProvider(const IInputSource* inputSource) explicit SampledInputProvider(const IKeyInputSource* keyInputSource, const ITouchInputSource* touchInputSource)
: _inputSource(inputSource), _inputBufferReadPtr(0), _inputBufferWritePtr(0) { } : _keyInputSource(keyInputSource), _touchInputSource(touchInputSource)
, _inputBufferReadPtr(0), _inputBufferWritePtr(0) { }
void Update() override; void Update() override;
/// @brief Samples the input source. /// @brief Samples the input source.
void Sample() void Sample()
{ {
_inputBuffer[_inputBufferWritePtr] = _inputSource->Sample(); _keyInputBuffer[_inputBufferWritePtr] = _keyInputSource->Sample();
_touchInputSource->Sample(_touchInputBuffer[_inputBufferWritePtr]);
_inputBufferWritePtr = (_inputBufferWritePtr + 1) & 3; _inputBufferWritePtr = (_inputBufferWritePtr + 1) & 3;
} }
@@ -26,9 +29,11 @@ public:
} }
private: private:
const IInputSource* _inputSource; const IKeyInputSource* _keyInputSource;
const ITouchInputSource* _touchInputSource;
InputKey _inputBuffer[4]; InputKey _keyInputBuffer[4];
Point _touchInputBuffer[4];
u8 _inputBufferReadPtr; u8 _inputBufferReadPtr;
u8 _inputBufferWritePtr; u8 _inputBufferWritePtr;
}; };

View File

@@ -0,0 +1,24 @@
#pragma once
#include "common.h"
#include <nds/input.h>
#include "ITouchInputSource.h"
#include "sharedMemory.h"
/// @brief Input source from the physical DS touch screen.
class TouchInputSource : public ITouchInputSource
{
public:
bool Sample(Point& touchPosition) const override
{
if (SHARED_KEY_XY & (1 << 6))
{
touchPosition = Point(0, 0);
return false;
}
else
{
touchPosition = Point(SHARED_TOUCH_X, SHARED_TOUCH_Y);
return true;
}
}
};

View File

@@ -3,15 +3,17 @@
class Label2DView : public LabelView class Label2DView : public LabelView
{ {
public: SHARED_ONLY(Label2DView)
Label2DView(u32 width, u32 height, u32 maxStringLength, const nft2_header_t* font)
: LabelView(width, height, maxStringLength, font, false) { }
public:
void InitVram(const VramContext& vramContext) override; void InitVram(const VramContext& vramContext) override;
void Draw(GraphicsContext& graphicsContext) override; void Draw(GraphicsContext& graphicsContext) override;
void VBlank() override; void VBlank() override;
private: private:
Label2DView(u32 width, u32 height, u32 maxStringLength, const nft2_header_t* font)
: LabelView(width, height, maxStringLength, font, false) { }
void UpdateTileBuffer() override; void UpdateTileBuffer() override;
bool _tileBufferUpdated = false; bool _tileBufferUpdated = false;

View File

@@ -12,6 +12,11 @@ Label3DView::Label3DView(u32 width, u32 height, u32 maxStringLength, const nft2_
: LabelView(width, height, maxStringLength, font, true) : LabelView(width, height, maxStringLength, font, true)
, _vblankTextureLoader(vblankTextureLoader) { } , _vblankTextureLoader(vblankTextureLoader) { }
Label3DView::~Label3DView()
{
_vblankTextureLoader->CancelLoad(_textureLoadRequest);
}
void Label3DView::InitVram(const VramContext& vramContext) void Label3DView::InitVram(const VramContext& vramContext)
{ {
const auto texVramManager = vramContext.GetTexVramManager(); const auto texVramManager = vramContext.GetTexVramManager();

View File

@@ -4,14 +4,18 @@
class alignas(32) Label3DView : public LabelView class alignas(32) Label3DView : public LabelView
{ {
SHARED_ONLY(Label3DView)
public: public:
Label3DView(u32 width, u32 height, u32 maxStringLength, const nft2_header_t* font, ~Label3DView() override;
VBlankTextureLoader* vblankTextureLoader);
void InitVram(const VramContext& vramContext) override; void InitVram(const VramContext& vramContext) override;
void Draw(GraphicsContext& graphicsContext) override; void Draw(GraphicsContext& graphicsContext) override;
private: private:
Label3DView(u32 width, u32 height, u32 maxStringLength, const nft2_header_t* font,
VBlankTextureLoader* vblankTextureLoader);
void UpdateTileBuffer() override; void UpdateTileBuffer() override;
u32 _texVramOffset = 0; u32 _texVramOffset = 0;

View File

@@ -9,6 +9,10 @@
#include "gui/palette/GradientPalette.h" #include "gui/palette/GradientPalette.h"
#include "LabelView.h" #include "LabelView.h"
#define MARQUEE_START_FRAMES 60
#define MARQUEE_STEP_FRAMES 3
#define MARQUEE_END_FRAMES 90
LabelView::LabelView(u32 width, u32 height, u32 maxStringLength, const nft2_header_t* font, bool a5i3) LabelView::LabelView(u32 width, u32 height, u32 maxStringLength, const nft2_header_t* font, bool a5i3)
: _width(width), _height(height) : _width(width), _height(height)
, _maxStringLength(maxStringLength), _font(font) , _maxStringLength(maxStringLength), _font(font)
@@ -34,14 +38,30 @@ LabelView::LabelView(u32 width, u32 height, u32 maxStringLength, const nft2_head
SetText(u""); SetText(u"");
} }
void LabelView::Update()
{
if (_ellipsisStyleChanged)
{
RestartMarquee();
UpdateTileBuffer();
_ellipsisStyleChanged = false;
}
else if (_ellipsisStyle == EllipsisStyle::Marquee)
{
UpdateMarquee();
}
}
void LabelView::SetTextBuffer(const char* text) void LabelView::SetTextBuffer(const char* text)
{ {
StringUtil::Copy(_textBuffer.get(), text, _maxStringLength + 1); StringUtil::Copy(_textBuffer.get(), text, _maxStringLength + 1);
RestartMarquee();
} }
void LabelView::SetTextBuffer(const char16_t* text) void LabelView::SetTextBuffer(const char16_t* text)
{ {
StringUtil::Copy(_textBuffer.get(), text, _maxStringLength + 1); StringUtil::Copy(_textBuffer.get(), text, _maxStringLength + 1);
RestartMarquee();
} }
void LabelView::SetTextBuffer(const char16_t* text, u32 length) void LabelView::SetTextBuffer(const char16_t* text, u32 length)
@@ -50,6 +70,7 @@ void LabelView::SetTextBuffer(const char16_t* text, u32 length)
if (copyLength > 0) if (copyLength > 0)
memcpy(_textBuffer.get(), text, copyLength * 2); memcpy(_textBuffer.get(), text, copyLength * 2);
_textBuffer[copyLength] = 0; _textBuffer[copyLength] = 0;
RestartMarquee();
} }
void LabelView::UpdateTileBuffer() void LabelView::UpdateTileBuffer()
@@ -63,10 +84,19 @@ void LabelView::UpdateTileBuffer()
renderParams.width = _width; renderParams.width = _width;
renderParams.height = _height; renderParams.height = _height;
renderParams.a5i3 = _a5i3; renderParams.a5i3 = _a5i3;
if (_ellipsis) if (_ellipsisStyle == EllipsisStyle::Ellipsis)
{
nft2_renderStringEllipsis(_font, _textBuffer.get(), _tileBuffer.get(), _actualWidth, &renderParams, u" ... "); nft2_renderStringEllipsis(_font, _textBuffer.get(), _tileBuffer.get(), _actualWidth, &renderParams, u" ... ");
}
else else
{
if (_ellipsisStyle == EllipsisStyle::Marquee)
{
renderParams.x = -_marqueeOffset;
renderParams.onlyRenderWholeGlyphs = false;
}
nft2_renderString(_font, _textBuffer.get(), _tileBuffer.get(), _actualWidth, &renderParams); nft2_renderString(_font, _textBuffer.get(), _tileBuffer.get(), _actualWidth, &renderParams);
}
_newStringWidth = renderParams.textWidth; _newStringWidth = renderParams.textWidth;
} }
else else
@@ -119,3 +149,59 @@ QueueTask<void> LabelView::SetTextAsync(TaskQueueBase* taskQueue, const char16_t
SetTextBuffer(text, length); SetTextBuffer(text, length);
return UpdateTileBufferAsync(taskQueue); return UpdateTileBufferAsync(taskQueue);
} }
void LabelView::RestartMarquee()
{
_marqueeOffset = 0;
_marqueeCounter = MARQUEE_START_FRAMES;
_marqueeState = MarqueeState::StartWait;
}
void LabelView::UpdateMarquee()
{
UpdateTileBuffer();
if (_newStringWidth <= _width)
{
_marqueeOffset = 0;
}
else
{
switch (_marqueeState)
{
case MarqueeState::StartWait:
{
if (--_marqueeCounter <= 0)
{
_marqueeState = MarqueeState::Moving;
_marqueeOffset = 0;
_marqueeCounter = MARQUEE_STEP_FRAMES;
}
break;
}
case MarqueeState::Moving:
{
if (--_marqueeCounter == 0)
{
_marqueeCounter = MARQUEE_STEP_FRAMES;
_marqueeOffset++;
if (_newStringWidth - _marqueeOffset < _width)
{
_marqueeState = MarqueeState::EndWait;
_marqueeCounter = MARQUEE_END_FRAMES;
}
}
break;
}
case MarqueeState::EndWait:
{
if (--_marqueeCounter <= 0)
{
_marqueeState = MarqueeState::StartWait;
_marqueeCounter = MARQUEE_START_FRAMES;
_marqueeOffset = 0;
}
break;
}
}
}
}

View File

@@ -14,6 +14,15 @@ class MaterialGraphicsContext;
class LabelView : public View class LabelView : public View
{ {
public: public:
enum class EllipsisStyle
{
None,
Ellipsis,
Marquee
};
void Update() override;
void SetText(const char* text); void SetText(const char* text);
void SetText(const char16_t* text); void SetText(const char16_t* text);
void SetText(const char16_t* text, u32 length); void SetText(const char16_t* text, u32 length);
@@ -43,7 +52,14 @@ public:
return Rectangle(_position, _width, _height); return Rectangle(_position, _width, _height);
} }
void SetEllipsis(bool ellipsis) { _ellipsis = ellipsis; } void SetEllipsisStyle(EllipsisStyle ellipsisStyle)
{
if (_ellipsisStyle != ellipsisStyle)
{
_ellipsisStyle = ellipsisStyle;
_ellipsisStyleChanged = true;
}
}
protected: protected:
u32 _width; u32 _width;
@@ -61,14 +77,30 @@ protected:
Rgb<8, 8, 8> _backgroundColor; Rgb<8, 8, 8> _backgroundColor;
Rgb<8, 8, 8> _foregroundColor; Rgb<8, 8, 8> _foregroundColor;
int _paletteRow = -1; int _paletteRow = -1;
bool _ellipsis = false; EllipsisStyle _ellipsisStyle = EllipsisStyle::None;
bool _a5i3; bool _a5i3;
LabelView(u32 width, u32 height, u32 maxStringLength, const nft2_header_t* font, bool a5i3);
void SetTextBuffer(const char* text); void SetTextBuffer(const char* text);
void SetTextBuffer(const char16_t* text); void SetTextBuffer(const char16_t* text);
void SetTextBuffer(const char16_t* text, u32 length); void SetTextBuffer(const char16_t* text, u32 length);
virtual void UpdateTileBuffer(); virtual void UpdateTileBuffer();
QueueTask<void> UpdateTileBufferAsync(TaskQueueBase* taskQueue); QueueTask<void> UpdateTileBufferAsync(TaskQueueBase* taskQueue);
LabelView(u32 width, u32 height, u32 maxStringLength, const nft2_header_t* font, bool a5i3); private:
enum class MarqueeState
{
StartWait,
Moving,
EndWait
};
MarqueeState _marqueeState = MarqueeState::StartWait;
int _marqueeOffset = 0;
int _marqueeCounter = 0;
bool _ellipsisStyleChanged = false;
void RestartMarquee();
void UpdateMarquee();
}; };

View File

@@ -20,19 +20,15 @@ public:
/// @brief Creates and returns a view for this adapter. /// @brief Creates and returns a view for this adapter.
/// @return The created view. /// @return The created view.
virtual View* CreateView() const = 0; virtual SharedPtr<View> CreateView() const = 0;
/// @brief Destroys a \p view for this adapter that was previously created with CreateView.
/// @param view The view to destroy.
virtual void DestroyView(View* view) const = 0;
/// @brief Binds the given \p view to the item at the given \p index. /// @brief Binds the given \p view to the item at the given \p index.
/// @param view The view to bind. /// @param view The view to bind.
/// @param index The item index to bind to. /// @param index The item index to bind to.
virtual void BindView(View* view, int index) const = 0; virtual void BindView(SharedPtr<View> view, int index) const = 0;
/// @brief Releases a \p view that was previously bound with BindView, such that it can be reused. /// @brief Releases a \p view that was previously bound with BindView, such that it can be reused.
/// @param view The view to release. /// @param view The view to release.
/// @param index The item index that was bound to the view. /// @param index The item index that was bound to the view.
virtual void ReleaseView(View* view, int index) const = 0; virtual void ReleaseView(SharedPtr<View> view, int index) const = 0;
}; };

View File

@@ -20,27 +20,31 @@ RecyclerView::~RecyclerView()
{ {
if (_adapter) if (_adapter)
{ {
for (u32 i = 0; i < _viewPoolTotalCount; i++) for (u32 i = _viewPoolFreeCount; i < _viewPoolTotalCount; i++)
{ {
_adapter->DestroyView(_viewPool[i].view); _adapter->ReleaseView(_viewPool[i].view, _viewPool[i].itemIdx);
} }
} }
} }
void RecyclerView::SetAdapter(const RecyclerAdapter* adapter, int initialSelectedIndex) void RecyclerView::SetAdapter(SharedPtr<const RecyclerAdapter> adapter, int initialSelectedIndex)
{ {
if (_adapter) if (_adapter)
{ {
_selectedItem = nullptr; _selectedItem = nullptr;
for (u32 i = 0; i < _viewPoolTotalCount; i++) for (u32 i = _viewPoolFreeCount; i < _viewPoolTotalCount; i++)
{ {
_adapter->DestroyView(_viewPool[i].view); _adapter->ReleaseView(_viewPool[i].view, _viewPool[i].itemIdx);
} }
_viewPool.reset(); _viewPool.reset();
_viewPoolFreeCount = 0; _viewPoolFreeCount = 0;
_viewPoolTotalCount = 0; _viewPoolTotalCount = 0;
_xOffset = 0;
_yOffset = 0;
_curRangeStart = 0;
_curRangeLength = 0;
} }
_adapter = adapter; _adapter = std::move(adapter);
_adapter->GetViewSize(_itemWidth, _itemHeight); _adapter->GetViewSize(_itemWidth, _itemHeight);
_itemCount = _adapter->GetItemCount(); _itemCount = _adapter->GetItemCount();
if (_mode == Mode::HorizontalList || _mode == Mode::HorizontalGrid) if (_mode == Mode::HorizontalList || _mode == Mode::HorizontalGrid)
@@ -174,7 +178,7 @@ void RecyclerView::VBlank()
} }
} }
View* RecyclerView::MoveFocus(View* currentFocus, FocusMoveDirection direction, View* source) SharedPtr<View> RecyclerView::MoveFocus(const SharedPtr<View>& currentFocus, FocusMoveDirection direction, View* source)
{ {
if (_itemCount == 0) if (_itemCount == 0)
{ {
@@ -191,9 +195,9 @@ View* RecyclerView::MoveFocus(View* currentFocus, FocusMoveDirection direction,
} }
} }
View* RecyclerView::MoveFocusHorizontal(View* currentFocus, FocusMoveDirection direction, View* source) SharedPtr<View> RecyclerView::MoveFocusHorizontal(const SharedPtr<View>& currentFocus, FocusMoveDirection direction, View* source)
{ {
if (!_selectedItem || currentFocus != _selectedItem->view) if (!_selectedItem || currentFocus.GetPointer() != _selectedItem->view.GetPointer())
{ {
// incoming focus // incoming focus
if (direction != FocusMoveDirection::Down) if (direction != FocusMoveDirection::Down)
@@ -203,7 +207,7 @@ View* RecyclerView::MoveFocusHorizontal(View* currentFocus, FocusMoveDirection d
int idx = (-_xOffset + currentFocus->GetPosition().x - _xPadding + ((_xSpacing + _itemWidth) >> 1)) / (_xSpacing + _itemWidth) * _rows; int idx = (-_xOffset + currentFocus->GetPosition().x - _xPadding + ((_xSpacing + _itemWidth) >> 1)) / (_xSpacing + _itemWidth) * _rows;
SetSelectedItem(std::clamp(idx, 0, ((int)_itemCount - 1) / _rows * _rows)); SetSelectedItem(std::clamp(idx, 0, ((int)_itemCount - 1) / _rows * _rows));
return _selectedItem != nullptr ? _selectedItem->view : this; return _selectedItem != nullptr ? _selectedItem->view : SharedFromThis();
} }
int row = _selectedItem->itemIdx % _rows; int row = _selectedItem->itemIdx % _rows;
@@ -244,22 +248,28 @@ View* RecyclerView::MoveFocusHorizontal(View* currentFocus, FocusMoveDirection d
SetSelectedItem(std::clamp(idx, 0, (int)_itemCount - 1)); SetSelectedItem(std::clamp(idx, 0, (int)_itemCount - 1));
} }
return _selectedItem != nullptr ? _selectedItem->view : this; return _selectedItem != nullptr ? _selectedItem->view : SharedFromThis();
} }
View* RecyclerView::MoveFocusVertical(View* currentFocus, FocusMoveDirection direction, View* source) SharedPtr<View> RecyclerView::MoveFocusVertical(const SharedPtr<View>& currentFocus, FocusMoveDirection direction, View* source)
{ {
if (!_selectedItem || currentFocus != _selectedItem->view) if (!_selectedItem || currentFocus.GetPointer() != _selectedItem->view.GetPointer())
{ {
// incoming focus // incoming focus
if (direction != FocusMoveDirection::Right) if (direction == FocusMoveDirection::Right)
{ {
return nullptr;
}
int idx = (-_yOffset + currentFocus->GetPosition().y - _yPadding + ((_ySpacing + _itemHeight) >> 1)) / (_ySpacing + _itemHeight) * _columns; int idx = (-_yOffset + currentFocus->GetPosition().y - _yPadding + ((_ySpacing + _itemHeight) >> 1)) / (_ySpacing + _itemHeight) * _columns;
SetSelectedItem(std::clamp(idx, 0, ((int)_itemCount - 1) / _columns * _columns)); SetSelectedItem(std::clamp(idx, 0, ((int)_itemCount - 1) / _columns * _columns));
return _selectedItem != nullptr ? _selectedItem->view : this; return _selectedItem != nullptr ? _selectedItem->view : SharedFromThis();
}
else if (direction == FocusMoveDirection::Down)
{
int idx = (-_xOffset + currentFocus->GetPosition().x - _xPadding + ((_xSpacing + _itemWidth) >> 1)) / (_xSpacing + _itemWidth);
SetSelectedItem(std::clamp(idx, 0, _columns - 1));
return _selectedItem != nullptr ? _selectedItem->view : SharedFromThis();
}
return nullptr;
} }
int column = _selectedItem->itemIdx % _columns; int column = _selectedItem->itemIdx % _columns;
@@ -300,12 +310,12 @@ View* RecyclerView::MoveFocusVertical(View* currentFocus, FocusMoveDirection dir
SetSelectedItem(std::clamp(idx, 0, (int)_itemCount - 1)); SetSelectedItem(std::clamp(idx, 0, (int)_itemCount - 1));
} }
return _selectedItem != nullptr ? _selectedItem->view : this; return _selectedItem != nullptr ? _selectedItem->view : SharedFromThis();
} }
bool RecyclerView::HandleInput(const InputProvider& inputProvider, FocusManager& focusManager) bool RecyclerView::HandleInput(const InputProvider& inputProvider, FocusManager& focusManager)
{ {
if (inputProvider.Triggered(InputKey::L | InputKey::R)) if (_itemCount != 0 && inputProvider.Triggered(InputKey::L | InputKey::R))
{ {
int direction = inputProvider.Triggered(InputKey::L) ? 1 : -1; int direction = inputProvider.Triggered(InputKey::L) ? 1 : -1;
int selected = _selectedItem->itemIdx; int selected = _selectedItem->itemIdx;
@@ -337,6 +347,117 @@ bool RecyclerView::HandleInput(const InputProvider& inputProvider, FocusManager&
return View::HandleInput(inputProvider, focusManager); return View::HandleInput(inputProvider, focusManager);
} }
void RecyclerView::HandlePenDown(const Point& touchPoint, FocusManager& focusManager)
{
if (GetBounds().Contains(touchPoint))
{
_penDown = true;
_penDownPosition = touchPoint;
_hasScrollStarted = false;
_penDownScrollOffset = _scrollOffsetAnimator.GetValue();
for (u32 i = _viewPoolFreeCount; i < _viewPoolTotalCount; i++)
{
_viewPool[i].view->HandlePenDown(touchPoint, focusManager);
}
}
}
void RecyclerView::HandlePenMove(const Point& touchPoint, FocusManager& focusManager)
{
if (!_penDown)
{
return;
}
if (!_hasScrollStarted)
{
for (u32 i = _viewPoolFreeCount; i < _viewPoolTotalCount; i++)
{
_viewPool[i].view->HandlePenMove(touchPoint, focusManager);
if (focusManager.GetCurrentFocus().GetPointer() == _viewPool[i].view.GetPointer())
{
SetSelectedItem(_viewPool[i].itemIdx);
}
}
int dx = touchPoint.x - _penDownPosition.x;
int dy = touchPoint.y - _penDownPosition.y;
if (dx * dx + dy * dy > 7 * 7)
{
bool shouldScrollStart = (_mode == Mode::HorizontalGrid || _mode == Mode::HorizontalList)
? (std::abs(touchPoint.x - _penDownPosition.x) > std::abs(touchPoint.y - _penDownPosition.y))
: (std::abs(touchPoint.x - _penDownPosition.x) < std::abs(touchPoint.y - _penDownPosition.y));
if (shouldScrollStart)
{
_hasScrollStarted = true;
}
else
{
_penDown = false; //wrong direction drag, so cancel it
}
for (u32 i = _viewPoolFreeCount; i < _viewPoolTotalCount; i++)
{
_viewPool[i].view->HandlePenUp(Point(-1, -1), focusManager);
}
}
}
else
{
int newScrollOffset;
if (_mode == Mode::HorizontalGrid || _mode == Mode::HorizontalList)
{
newScrollOffset = _penDownScrollOffset + touchPoint.x - _penDownPosition.x;
if (-newScrollOffset < 0)
{
newScrollOffset = 0;
_penDownScrollOffset = 0;
_penDownPosition.x = touchPoint.x;
}
else if (newScrollOffset < GetMaxScrollOffset())
{
newScrollOffset = GetMaxScrollOffset();
_penDownScrollOffset = newScrollOffset;
_penDownPosition.x = touchPoint.x;
}
}
else
{
newScrollOffset = _penDownScrollOffset + touchPoint.y - _penDownPosition.y;
if (-newScrollOffset < 0)
{
newScrollOffset = 0;
_penDownScrollOffset = 0;
_penDownPosition.y = touchPoint.y;
}
else if (newScrollOffset < GetMaxScrollOffset())
{
newScrollOffset = GetMaxScrollOffset();
_penDownScrollOffset = newScrollOffset;
_penDownPosition.y = touchPoint.y;
}
}
SetScrollOffset(newScrollOffset, false);
}
}
void RecyclerView::HandlePenUp(const Point& lastTouchPoint, FocusManager& focusManager)
{
for (u32 i = _viewPoolFreeCount; i < _viewPoolTotalCount; i++)
{
_viewPool[i].view->HandlePenUp(lastTouchPoint, focusManager);
if (focusManager.GetCurrentFocus().GetPointer() == _viewPool[i].view.GetPointer())
{
SetSelectedItem(_viewPool[i].itemIdx);
}
}
_penDown = false;
}
Point RecyclerView::GetItemPosition(int itemIdx) Point RecyclerView::GetItemPosition(int itemIdx)
{ {
int x = 0; int x = 0;

View File

@@ -8,6 +8,8 @@
class RecyclerView : public RecyclerViewBase class RecyclerView : public RecyclerViewBase
{ {
SHARED_ONLY(RecyclerView)
public: public:
enum class Mode enum class Mode
{ {
@@ -21,10 +23,9 @@ public:
VerticalGrid VerticalGrid
}; };
RecyclerView(int x, int y, int width, int height, Mode mode);
~RecyclerView(); ~RecyclerView();
void SetAdapter(const RecyclerAdapter* adapter, int initialSelectedIndex = 0) override; void SetAdapter(SharedPtr<const RecyclerAdapter> adapter, int initialSelectedIndex = 0) override;
void InitVram(const VramContext& vramContext) override; void InitVram(const VramContext& vramContext) override;
void Update() override; void Update() override;
void Draw(GraphicsContext& graphicsContext) override; void Draw(GraphicsContext& graphicsContext) override;
@@ -35,16 +36,23 @@ public:
return Rectangle(_position, _width, _height); return Rectangle(_position, _width, _height);
} }
View* MoveFocus(View* currentFocus, FocusMoveDirection direction, View* source) override; SharedPtr<View> MoveFocus(const SharedPtr<View>& currentFocus, FocusMoveDirection direction, View* source) override;
bool HandleInput(const InputProvider& inputProvider, FocusManager& focusManager) override; bool HandleInput(const InputProvider& inputProvider, FocusManager& focusManager) override;
void HandlePenDown(const Point& touchPoint, FocusManager& focusManager) override;
void HandlePenMove(const Point& touchPoint, FocusManager& focusManager) override;
void HandlePenUp(const Point& lastTouchPoint, FocusManager& focusManager) override;
void Focus(FocusManager& focusManager) override void Focus(FocusManager& focusManager) override
{ {
if (_selectedItem) if (_selectedItem)
{
focusManager.Focus(_selectedItem->view); focusManager.Focus(_selectedItem->view);
}
else else
focusManager.Focus(this); {
focusManager.Focus(SharedFromThis());
}
} }
int GetSelectedItem() const override int GetSelectedItem() const override
@@ -69,7 +77,7 @@ public:
private: private:
struct ViewPoolEntry struct ViewPoolEntry
{ {
View* view; SharedPtr<View> view;
int itemIdx; int itemIdx;
}; };
@@ -94,6 +102,12 @@ private:
int _curRangeStart; int _curRangeStart;
int _curRangeLength; int _curRangeLength;
Animator<int> _scrollOffsetAnimator; Animator<int> _scrollOffsetAnimator;
bool _penDown = false;
Point _penDownPosition = Point(0, 0);
bool _hasScrollStarted = false;
int _penDownScrollOffset = 0;
RecyclerView(int x, int y, int width, int height, Mode mode);
void UpdatePosition(ViewPoolEntry& viewPoolEntry); void UpdatePosition(ViewPoolEntry& viewPoolEntry);
ViewPoolEntry* GetViewPoolEntryByItemIndex(int itemIdx); ViewPoolEntry* GetViewPoolEntryByItemIndex(int itemIdx);
@@ -107,6 +121,6 @@ private:
void EnsureVisible(int itemIdx, bool animate); void EnsureVisible(int itemIdx, bool animate);
Point GetItemPosition(int itemIdx); Point GetItemPosition(int itemIdx);
View* MoveFocusHorizontal(View* currentFocus, FocusMoveDirection direction, View* source); SharedPtr<View> MoveFocusHorizontal(const SharedPtr<View>& currentFocus, FocusMoveDirection direction, View* source);
View* MoveFocusVertical(View* currentFocus, FocusMoveDirection direction, View* source); SharedPtr<View> MoveFocusVertical(const SharedPtr<View>& currentFocus, FocusMoveDirection direction, View* source);
}; };

View File

@@ -2,16 +2,17 @@
#include "View.h" #include "View.h"
#include "RecyclerAdapter.h" #include "RecyclerAdapter.h"
#include "gui/FocusManager.h" #include "gui/FocusManager.h"
#include "core/SharedPtr.h"
/// @brief Abstract base class for a recycler view that displays a possibly large collection of items /// @brief Abstract base class for a recycler view that displays a possibly large collection of items
/// provided by an adapter in an efficient way. /// provided by an adapter in an efficient way.
class RecyclerViewBase : public View class RecyclerViewBase : public View
{ {
public: public:
virtual void SetAdapter(const RecyclerAdapter* adapter, int initialSelectedIndex = 0) = 0; virtual void SetAdapter(SharedPtr<const RecyclerAdapter> adapter, int initialSelectedIndex = 0) = 0;
virtual void Focus(FocusManager& focusManager) = 0; virtual void Focus(FocusManager& focusManager) = 0;
virtual int GetSelectedItem() const = 0; virtual int GetSelectedItem() const = 0;
protected: protected:
const RecyclerAdapter* _adapter = nullptr; SharedPtr<const RecyclerAdapter> _adapter;
}; };

View File

@@ -2,6 +2,8 @@
#include "core/LinkedListLink.h" #include "core/LinkedListLink.h"
#include "core/math/Point.h" #include "core/math/Point.h"
#include "core/math/Rectangle.h" #include "core/math/Rectangle.h"
#include "core/SharedPtr.h"
#include "core/EnableSharedFromThis.h"
#include "../FocusManager.h" #include "../FocusManager.h"
#include "../FocusMoveDirection.h" #include "../FocusMoveDirection.h"
@@ -10,7 +12,7 @@ class VramContext;
class InputProvider; class InputProvider;
/// @brief Base class for views. /// @brief Base class for views.
class View class View : public EnableSharedFromThis<View>
{ {
public: public:
/// @brief Link used for views that contain other views. /// @brief Link used for views that contain other views.
@@ -37,7 +39,7 @@ public:
/// @param direction The direction to move the focus in. /// @param direction The direction to move the focus in.
/// @param source The view that requested this view to move focus. /// @param source The view that requested this view to move focus.
/// @return The newly focused view, or null if the focus didn't change. /// @return The newly focused view, or null if the focus didn't change.
virtual View* MoveFocus(View* currentFocus, FocusMoveDirection direction, View* source) virtual SharedPtr<View> MoveFocus(const SharedPtr<View>& currentFocus, FocusMoveDirection direction, View* source)
{ {
if (_parent && _parent != source) if (_parent && _parent != source)
return _parent->MoveFocus(currentFocus, direction, this); return _parent->MoveFocus(currentFocus, direction, this);
@@ -57,6 +59,21 @@ public:
return false; return false;
} }
/// @brief Handles a pen down event.
/// @param touchPoint The touch point.
/// @param focusManager The focus manager.
virtual void HandlePenDown(const Point& touchPoint, FocusManager& focusManager) { }
/// @brief Handles a pen move event.
/// @param touchPoint The touch point.
/// @param focusManager The focus manager.
virtual void HandlePenMove(const Point& touchPoint, FocusManager& focusManager) { }
/// @brief Handles a pen up event.
/// @param lastTouchPoint The last touch point.
/// @param focusManager The focus manager.
virtual void HandlePenUp(const Point& lastTouchPoint, FocusManager& focusManager) { }
/// @brief Gets the bounds of the view. /// @brief Gets the bounds of the view.
/// @return The bounds of the view. /// @return The bounds of the view.
virtual Rectangle GetBounds() const = 0; virtual Rectangle GetBounds() const = 0;

View File

@@ -0,0 +1,58 @@
#include "common.h"
#include "ViewContainer.h"
void ViewContainer::InitVram(const VramContext& vramContext)
{
for (auto& view : _children)
{
view.InitVram(vramContext);
}
}
void ViewContainer::Update()
{
for (auto& view : _children)
{
view.Update();
}
}
void ViewContainer::Draw(GraphicsContext& graphicsContext)
{
for (auto& view : _children)
{
view.Draw(graphicsContext);
}
}
void ViewContainer::VBlank()
{
for (auto& view : _children)
{
view.VBlank();
}
}
void ViewContainer::HandlePenDown(const Point& touchPoint, FocusManager& focusManager)
{
for (auto& view : _children)
{
view.HandlePenDown(touchPoint, focusManager);
}
}
void ViewContainer::HandlePenMove(const Point& touchPoint, FocusManager& focusManager)
{
for (auto& view : _children)
{
view.HandlePenMove(touchPoint, focusManager);
}
}
void ViewContainer::HandlePenUp(const Point& lastTouchPoint, FocusManager& focusManager)
{
for (auto& view : _children)
{
view.HandlePenUp(lastTouchPoint, focusManager);
}
}

View File

@@ -6,37 +6,13 @@
class ViewContainer : public View class ViewContainer : public View
{ {
public: public:
void InitVram(const VramContext& vramContext) override void InitVram(const VramContext& vramContext) override;
{ void Update() override;
for (auto& view : _children) void Draw(GraphicsContext& graphicsContext) override;
{ void VBlank() override;
view.InitVram(vramContext); void HandlePenDown(const Point& touchPoint, FocusManager& focusManager) override;
} void HandlePenMove(const Point& touchPoint, FocusManager& focusManager) override;
} void HandlePenUp(const Point& lastTouchPoint, FocusManager& focusManager) override;
void Update() override
{
for (auto& view : _children)
{
view.Update();
}
}
void Draw(GraphicsContext& graphicsContext) override
{
for (auto& view : _children)
{
view.Draw(graphicsContext);
}
}
void VBlank() override
{
for (auto& view : _children)
{
view.VBlank();
}
}
protected: protected:
/// @brief Adds a child to the head of the list. /// @brief Adds a child to the head of the list.
@@ -55,6 +31,14 @@ protected:
view->SetParent(this); view->SetParent(this);
} }
/// @brief Removes a child from the list.
/// @param view The child to remove.
void RemoveChild(View* view)
{
_children.Remove(view);
view->SetParent(nullptr);
}
private: private:
LinkedList<View, &View::listLink> _children; LinkedList<View, &View::listLink> _children;
}; };

View File

@@ -167,6 +167,11 @@ int main(int argc, char* argv[])
rtc_init(); rtc_init();
if (argc >= 1)
{
pload_setLauncherPath(argv[0]);
}
memset(&gFatFs, 0, sizeof(gFatFs)); memset(&gFatFs, 0, sizeof(gFatFs));
if (dldi_init()) if (dldi_init())
{ {

View File

@@ -10,6 +10,7 @@
#include <libtwl/ipc/ipcFifoSystem.h> #include <libtwl/ipc/ipcFifoSystem.h>
#include "ipcChannels.h" #include "ipcChannels.h"
#include "fat/File.h" #include "fat/File.h"
#include "core/StringUtil.h"
#include "picoLoaderBootstrap.h" #include "picoLoaderBootstrap.h"
#define PICO_LOADER_9_PATH "/_pico/picoLoader9.bin" #define PICO_LOADER_9_PATH "/_pico/picoLoader9.bin"
@@ -18,7 +19,9 @@
typedef void (*pico_loader_9_func_t)(void); typedef void (*pico_loader_9_func_t)(void);
static pload_params_t sLoadParams; static pload_params_t sLoadParams;
static char sLauncherPath[256] alignas(32);
static PicoLoaderBootDrive sBootDrive; static PicoLoaderBootDrive sBootDrive;
static const pload_cheats_t* sCheatData = nullptr;
pload_params_t* pload_getLoadParams() pload_params_t* pload_getLoadParams()
{ {
@@ -30,6 +33,16 @@ void pload_setBootDrive(PicoLoaderBootDrive bootDrive)
sBootDrive = bootDrive; sBootDrive = bootDrive;
} }
void pload_setLauncherPath(const char* launcherPath)
{
StringUtil::Copy(sLauncherPath, launcherPath, sizeof(sLauncherPath));
}
void pload_setCheatData(const pload_cheats_t* cheatData)
{
sCheatData = cheatData;
}
void pload_start() void pload_start()
{ {
mem_setVramAMapping(MEM_VRAM_AB_LCDC); mem_setVramAMapping(MEM_VRAM_AB_LCDC);
@@ -78,8 +91,17 @@ void pload_start()
DC_InvalidateAll(); DC_InvalidateAll();
IC_InvalidateAll(); IC_InvalidateAll();
((pload_header7_t*)0x06840000)->bootDrive = sBootDrive; auto header = (pload_header7_t*)0x06840000;
dma_ntrCopy16(3, &sLoadParams, &((pload_header7_t*)0x06840000)->loadParams, sizeof(pload_params_t)); header->bootDrive = sBootDrive;
dma_ntrCopy16(3, &sLoadParams, &header->loadParams, sizeof(pload_params_t));
if (header->apiVersion >= 2)
{
dma_ntrCopy16(3, &sLauncherPath, &header->v2.launcherPath, sizeof(header->v2.launcherPath));
}
if (header->apiVersion >= 3)
{
header->v3.cheats = sCheatData;
}
mem_setVramCMapping(MEM_VRAM_C_ARM7_00000); mem_setVramCMapping(MEM_VRAM_C_ARM7_00000);
mem_setVramDMapping(MEM_VRAM_D_ARM7_20000); mem_setVramDMapping(MEM_VRAM_D_ARM7_20000);
ipc_sendFifoMessage(IPC_CHANNEL_LOADER, 1); ipc_sendFifoMessage(IPC_CHANNEL_LOADER, 1);

View File

@@ -3,4 +3,6 @@
pload_params_t* pload_getLoadParams(); pload_params_t* pload_getLoadParams();
void pload_setBootDrive(PicoLoaderBootDrive bootDrive); void pload_setBootDrive(PicoLoaderBootDrive bootDrive);
void pload_setLauncherPath(const char* launcherPath);
void pload_setCheatData(const pload_cheats_t* cheatData);
void pload_start(); void pload_start();

View File

@@ -3,6 +3,7 @@
#include "core/task/TaskQueue.h" #include "core/task/TaskQueue.h"
#include "../views/BannerListItemView.h" #include "../views/BannerListItemView.h"
#include "../Theme/IRomBrowserViewFactory.h" #include "../Theme/IRomBrowserViewFactory.h"
#include "romBrowser/viewModels/RomBrowserItemViewModel.h"
#include "BannerListFileRecyclerAdapter.h" #include "BannerListFileRecyclerAdapter.h"
void BannerListFileRecyclerAdapter::GetViewSize(int& width, int& height) const void BannerListFileRecyclerAdapter::GetViewSize(int& width, int& height) const
@@ -11,27 +12,24 @@ void BannerListFileRecyclerAdapter::GetViewSize(int& width, int& height) const
height = 44; height = 44;
} }
View* BannerListFileRecyclerAdapter::CreateView() const SharedPtr<View> BannerListFileRecyclerAdapter::CreateView() const
{ {
return _romBrowserViewFactory->CreateBannerListItemView(_vblankTextureLoader); return _romBrowserViewFactory->CreateBannerListItemView(
std::make_unique<RomBrowserItemViewModel>(_romBrowserController), _vblankTextureLoader);
} }
void BannerListFileRecyclerAdapter::DestroyView(View* view) const void BannerListFileRecyclerAdapter::BindView(SharedPtr<View> view, int index) const
{ {
delete static_cast<BannerListItemView*>(view); auto listItemView = static_cast<BannerListItemView*>(view.GetPointer());
}
void BannerListFileRecyclerAdapter::BindView(View* view, int index) const
{
auto listItemView = static_cast<BannerListItemView*>(view);
listItemView->SetGraphics(_bannerListItemViewGraphics); listItemView->SetGraphics(_bannerListItemViewGraphics);
FileRecyclerAdapter::BindView(view, index); FileRecyclerAdapter::BindView(view, index);
} }
TaskResult<void> BannerListFileRecyclerAdapter::BindView(View* view, int index, TaskResult<void> BannerListFileRecyclerAdapter::BindView(SharedPtr<View> view, int index,
const InternalFileInfo* internalFileInfo, const vu8& cancelRequested) const const InternalFileInfo* internalFileInfo, const vu8& cancelRequested) const
{ {
auto listItemView = static_cast<BannerListItemView*>(view); auto listItemView = static_cast<BannerListItemView*>(view.GetPointer());
listItemView->GetViewModel().SetIndex(index);
const auto& fileInfo = _fileInfoManager->GetItem(index); const auto& fileInfo = _fileInfoManager->GetItem(index);
bool fileNameAsTitle = true; bool fileNameAsTitle = true;
if (internalFileInfo) if (internalFileInfo)
@@ -70,12 +68,20 @@ TaskResult<void> BannerListFileRecyclerAdapter::BindView(View* view, int index,
return TaskResult<void>::Completed(); return TaskResult<void>::Completed();
} }
void BannerListFileRecyclerAdapter::ReleaseView(View* view, int index) const void BannerListFileRecyclerAdapter::SetQueueTask(const SharedPtr<View>& view, QueueTask<void> queueTask) const
{
auto listItemView = static_cast<BannerListItemView*>(view.GetPointer());
listItemView->GetViewModel().SetQueueTask(std::move(queueTask));
}
void BannerListFileRecyclerAdapter::ReleaseView(SharedPtr<View> view, int index) const
{ {
LOG_DEBUG("Releasing %d\n", index); LOG_DEBUG("Releasing %d\n", index);
auto listItemView = static_cast<BannerListItemView*>(view); auto listItemView = static_cast<BannerListItemView*>(view.GetPointer());
listItemView->SetIcon(nullptr); listItemView->SetIcon(nullptr);
listItemView->SetGameTitle(u""); listItemView->SetGameTitle(u"");
listItemView->GetViewModel().SetIndex(-1);
listItemView->GetViewModel().CancelQueueTask();
_fileInfoManager->ReleaseFileInfo(index); _fileInfoManager->ReleaseFileInfo(index);
} }

View File

@@ -8,19 +8,18 @@ class IRomBrowserViewFactory;
class BannerListFileRecyclerAdapter : public FileRecyclerAdapter class BannerListFileRecyclerAdapter : public FileRecyclerAdapter
{ {
public: public:
BannerListFileRecyclerAdapter(FileInfoManager* fileInfoManager, BannerListFileRecyclerAdapter(IRomBrowserController* romBrowserController, FileInfoManager* fileInfoManager,
TaskQueueBase* taskQueue, const IThemeFileIconFactory* themeFileIconFactory, TaskQueueBase* taskQueue, const IThemeFileIconFactory* themeFileIconFactory,
const IRomBrowserViewFactory* romBrowserViewFactory, const IRomBrowserViewFactory* romBrowserViewFactory,
VBlankTextureLoader* vblankTextureLoader) VBlankTextureLoader* vblankTextureLoader)
: FileRecyclerAdapter(fileInfoManager, taskQueue, themeFileIconFactory) : FileRecyclerAdapter(romBrowserController, fileInfoManager, taskQueue, themeFileIconFactory)
, _romBrowserViewFactory(romBrowserViewFactory) , _romBrowserViewFactory(romBrowserViewFactory)
, _vblankTextureLoader(vblankTextureLoader) { } , _vblankTextureLoader(vblankTextureLoader) { }
void GetViewSize(int& width, int& height) const override; void GetViewSize(int& width, int& height) const override;
View* CreateView() const override; SharedPtr<View> CreateView() const override;
void DestroyView(View* view) const override; void BindView(SharedPtr<View> view, int index) const override;
void BindView(View* view, int index) const override; void ReleaseView(SharedPtr<View> view, int index) const override;
void ReleaseView(View* view, int index) const override;
void InitVram(const VramContext& vramContext) override; void InitVram(const VramContext& vramContext) override;
@@ -29,6 +28,7 @@ private:
BannerListItemView::VramToken _bannerListItemViewGraphics; BannerListItemView::VramToken _bannerListItemViewGraphics;
VBlankTextureLoader* _vblankTextureLoader; VBlankTextureLoader* _vblankTextureLoader;
TaskResult<void> BindView(View* view, int index, TaskResult<void> BindView(SharedPtr<View> view, int index,
const InternalFileInfo* internalFileInfo, const vu8& cancelRequested) const override; const InternalFileInfo* internalFileInfo, const vu8& cancelRequested) const override;
void SetQueueTask(const SharedPtr<View>& view, QueueTask<void> queueTask) const override;
}; };

View File

@@ -13,21 +13,17 @@ void CoverFlowFileRecyclerAdapter::GetViewSize(int& width, int& height) const
height = 44; height = 44;
} }
View* CoverFlowFileRecyclerAdapter::CreateView() const SharedPtr<View> CoverFlowFileRecyclerAdapter::CreateView() const
{ {
return new CoverView(_vblankTextureLoader); return CoverView::CreateShared(
std::make_unique<RomBrowserItemViewModel>(_romBrowserController), _vblankTextureLoader);
} }
void CoverFlowFileRecyclerAdapter::DestroyView(View* view) const TaskResult<void> CoverFlowFileRecyclerAdapter::BindView(SharedPtr<View> view, int index,
{
auto coverView = static_cast<CoverView*>(view);
delete coverView;
}
TaskResult<void> CoverFlowFileRecyclerAdapter::BindView(View* view, int index,
const InternalFileInfo* internalFileInfo, const vu8& cancelRequested) const const InternalFileInfo* internalFileInfo, const vu8& cancelRequested) const
{ {
auto coverView = static_cast<CoverView*>(view); auto coverView = static_cast<CoverView*>(view.GetPointer());
coverView->GetViewModel().SetIndex(index);
auto cover = _fileInfoManager->GetFileCover(index); auto cover = _fileInfoManager->GetFileCover(index);
if (cancelRequested) if (cancelRequested)
{ {
@@ -45,11 +41,19 @@ TaskResult<void> CoverFlowFileRecyclerAdapter::BindView(View* view, int index,
return TaskResult<void>::Completed(); return TaskResult<void>::Completed();
} }
void CoverFlowFileRecyclerAdapter::ReleaseView(View* view, int index) const void CoverFlowFileRecyclerAdapter::SetQueueTask(const SharedPtr<View>& view, QueueTask<void> queueTask) const
{
auto coverView = static_cast<CoverView*>(view.GetPointer());
coverView->GetViewModel().SetQueueTask(std::move(queueTask));
}
void CoverFlowFileRecyclerAdapter::ReleaseView(SharedPtr<View> view, int index) const
{ {
LOG_DEBUG("Releasing %d\n", index); LOG_DEBUG("Releasing %d\n", index);
auto coverView = static_cast<CoverView*>(view); auto coverView = static_cast<CoverView*>(view.GetPointer());
coverView->ClearCover(); coverView->ClearCover();
coverView->GetViewModel().SetIndex(-1);
coverView->GetViewModel().CancelQueueTask();
_fileInfoManager->ReleaseFileInfo(index); _fileInfoManager->ReleaseFileInfo(index);
} }

View File

@@ -8,20 +8,19 @@ class ICoverRepository;
class CoverFlowFileRecyclerAdapter : public FileRecyclerAdapter class CoverFlowFileRecyclerAdapter : public FileRecyclerAdapter
{ {
public: public:
CoverFlowFileRecyclerAdapter(FileInfoManager* fileInfoManager, CoverFlowFileRecyclerAdapter(IRomBrowserController* romBrowserController, FileInfoManager* fileInfoManager,
TaskQueueBase* taskQueue, const IThemeFileIconFactory* themeFileIconFactory, TaskQueueBase* taskQueue, const IThemeFileIconFactory* themeFileIconFactory,
const IRomBrowserViewFactory* romBrowserViewFactory, const IRomBrowserViewFactory* romBrowserViewFactory,
VBlankTextureLoader* vblankTextureLoader, VBlankTextureLoader* vblankTextureLoader,
const ICoverRepository* coverRepository) const ICoverRepository* coverRepository)
: FileRecyclerAdapter(fileInfoManager, taskQueue, themeFileIconFactory) : FileRecyclerAdapter(romBrowserController, fileInfoManager, taskQueue, themeFileIconFactory)
, _romBrowserViewFactory(romBrowserViewFactory) , _romBrowserViewFactory(romBrowserViewFactory)
, _vblankTextureLoader(vblankTextureLoader) , _vblankTextureLoader(vblankTextureLoader)
, _coverRepository(coverRepository) { } , _coverRepository(coverRepository) { }
void GetViewSize(int& width, int& height) const override; void GetViewSize(int& width, int& height) const override;
View* CreateView() const override; SharedPtr<View> CreateView() const override;
void DestroyView(View* view) const override; void ReleaseView(SharedPtr<View> view, int index) const override;
void ReleaseView(View* view, int index) const override;
void InitVram(const VramContext& vramContext) override; void InitVram(const VramContext& vramContext) override;
@@ -30,6 +29,7 @@ private:
VBlankTextureLoader* _vblankTextureLoader; VBlankTextureLoader* _vblankTextureLoader;
const ICoverRepository* _coverRepository; const ICoverRepository* _coverRepository;
TaskResult<void> BindView(View* view, int index, TaskResult<void> BindView(SharedPtr<View> view, int index,
const InternalFileInfo* internalFileInfo, const vu8& cancelRequested) const override; const InternalFileInfo* internalFileInfo, const vu8& cancelRequested) const override;
void SetQueueTask(const SharedPtr<View>& view, QueueTask<void> queueTask) const override;
}; };

View File

@@ -3,6 +3,7 @@
#include "core/task/TaskQueue.h" #include "core/task/TaskQueue.h"
#include "../views/IconGridItemView.h" #include "../views/IconGridItemView.h"
#include "../Theme/IRomBrowserViewFactory.h" #include "../Theme/IRomBrowserViewFactory.h"
#include "romBrowser/viewModels/RomBrowserItemViewModel.h"
#include "IconGridFileRecyclerAdapter.h" #include "IconGridFileRecyclerAdapter.h"
void IconGridFileRecyclerAdapter::GetViewSize(int& width, int& height) const void IconGridFileRecyclerAdapter::GetViewSize(int& width, int& height) const
@@ -11,27 +12,23 @@ void IconGridFileRecyclerAdapter::GetViewSize(int& width, int& height) const
height = 44; height = 44;
} }
View* IconGridFileRecyclerAdapter::CreateView() const SharedPtr<View> IconGridFileRecyclerAdapter::CreateView() const
{ {
return _romBrowserViewFactory->CreateIconGridItemView(); return _romBrowserViewFactory->CreateIconGridItemView(std::make_unique<RomBrowserItemViewModel>(_romBrowserController));
} }
void IconGridFileRecyclerAdapter::DestroyView(View* view) const void IconGridFileRecyclerAdapter::BindView(SharedPtr<View> view, int index) const
{ {
delete static_cast<IconGridItemView*>(view); auto iconGridItemView = static_cast<IconGridItemView*>(view.GetPointer());
}
void IconGridFileRecyclerAdapter::BindView(View* view, int index) const
{
auto iconGridItemView = static_cast<IconGridItemView*>(view);
iconGridItemView->SetGraphics(_iconGridItemViewGraphics); iconGridItemView->SetGraphics(_iconGridItemViewGraphics);
FileRecyclerAdapter::BindView(view, index); FileRecyclerAdapter::BindView(view, index);
} }
TaskResult<void> IconGridFileRecyclerAdapter::BindView(View* view, int index, TaskResult<void> IconGridFileRecyclerAdapter::BindView(SharedPtr<View> view, int index,
const InternalFileInfo* internalFileInfo, const vu8& cancelRequested) const const InternalFileInfo* internalFileInfo, const vu8& cancelRequested) const
{ {
auto iconGridItemView = static_cast<IconGridItemView*>(view); auto iconGridItemView = static_cast<IconGridItemView*>(view.GetPointer());
iconGridItemView->GetViewModel().SetIndex(index);
auto icon = internalFileInfo ? internalFileInfo->CreateGameIcon() : nullptr; auto icon = internalFileInfo ? internalFileInfo->CreateGameIcon() : nullptr;
if (!icon) if (!icon)
{ {
@@ -59,11 +56,19 @@ TaskResult<void> IconGridFileRecyclerAdapter::BindView(View* view, int index,
return TaskResult<void>::Completed(); return TaskResult<void>::Completed();
} }
void IconGridFileRecyclerAdapter::ReleaseView(View* view, int index) const void IconGridFileRecyclerAdapter::SetQueueTask(const SharedPtr<View>& view, QueueTask<void> queueTask) const
{
auto iconGridItemView = static_cast<IconGridItemView*>(view.GetPointer());
iconGridItemView->GetViewModel().SetQueueTask(std::move(queueTask));
}
void IconGridFileRecyclerAdapter::ReleaseView(SharedPtr<View> view, int index) const
{ {
LOG_DEBUG("Releasing %d\n", index); LOG_DEBUG("Releasing %d\n", index);
auto iconGridItemView = static_cast<IconGridItemView*>(view); auto iconGridItemView = static_cast<IconGridItemView*>(view.GetPointer());
iconGridItemView->SetIcon(nullptr); iconGridItemView->SetIcon(nullptr);
iconGridItemView->GetViewModel().SetIndex(-1);
iconGridItemView->GetViewModel().CancelQueueTask();
_fileInfoManager->ReleaseFileInfo(index); _fileInfoManager->ReleaseFileInfo(index);
} }

View File

@@ -7,17 +7,16 @@ class IRomBrowserViewFactory;
class IconGridFileRecyclerAdapter : public FileRecyclerAdapter class IconGridFileRecyclerAdapter : public FileRecyclerAdapter
{ {
public: public:
IconGridFileRecyclerAdapter(FileInfoManager* fileInfoManager, IconGridFileRecyclerAdapter(IRomBrowserController* romBrowserController, FileInfoManager* fileInfoManager,
TaskQueueBase* taskQueue, const IThemeFileIconFactory* themeFileIconFactory, TaskQueueBase* taskQueue, const IThemeFileIconFactory* themeFileIconFactory,
const IRomBrowserViewFactory* romBrowserViewFactory) const IRomBrowserViewFactory* romBrowserViewFactory)
: FileRecyclerAdapter(fileInfoManager, taskQueue, themeFileIconFactory) : FileRecyclerAdapter(romBrowserController, fileInfoManager, taskQueue, themeFileIconFactory)
, _romBrowserViewFactory(romBrowserViewFactory) { } , _romBrowserViewFactory(romBrowserViewFactory) { }
void GetViewSize(int& width, int& height) const override; void GetViewSize(int& width, int& height) const override;
View* CreateView() const override; SharedPtr<View> CreateView() const override;
void DestroyView(View* view) const override; void BindView(SharedPtr<View> view, int index) const override;
void BindView(View* view, int index) const override; void ReleaseView(SharedPtr<View> view, int index) const override;
void ReleaseView(View* view, int index) const override;
void InitVram(const VramContext& vramContext) override; void InitVram(const VramContext& vramContext) override;
@@ -25,6 +24,7 @@ private:
const IRomBrowserViewFactory* _romBrowserViewFactory; const IRomBrowserViewFactory* _romBrowserViewFactory;
IconGridItemView::VramToken _iconGridItemViewGraphics; IconGridItemView::VramToken _iconGridItemViewGraphics;
TaskResult<void> BindView(View* view, int index, TaskResult<void> BindView(SharedPtr<View> view, int index,
const InternalFileInfo* internalFileInfo, const vu8& cancelRequested) const override; const InternalFileInfo* internalFileInfo, const vu8& cancelRequested) const override;
void SetQueueTask(const SharedPtr<View>& view, QueueTask<void> queueTask) const override;
}; };

View File

@@ -10,26 +10,26 @@ public:
bool IsVertical() const override { return true; } bool IsVertical() const override { return true; }
std::unique_ptr<AppBarView> CreateAppBarView(const IRomBrowserViewFactory* romBrowserViewFactory, SharedPtr<AppBarView> CreateAppBarView(const IRomBrowserViewFactory* romBrowserViewFactory,
int startButtonCount, int endButtonCount) const override int startButtonCount, int endButtonCount) const override
{ {
return romBrowserViewFactory->CreateAppBarView(0, 0, return romBrowserViewFactory->CreateAppBarView(0, 0,
AppBarView::Orientation::Vertical, startButtonCount, endButtonCount); AppBarView::Orientation::Vertical, startButtonCount, endButtonCount);
} }
std::unique_ptr<RecyclerViewBase> CreateRecyclerView(const IRomBrowserViewFactory* romBrowserViewFactory) const override SharedPtr<RecyclerViewBase> CreateRecyclerView(const IRomBrowserViewFactory* romBrowserViewFactory) const override
{ {
auto recyclerView = std::make_unique<RecyclerView>(42, 0, 256 - 42, 192, RecyclerView::Mode::VerticalList); auto recyclerView = RecyclerView::CreateShared(42, 0, 256 - 42, 192, RecyclerView::Mode::VerticalList);
recyclerView->SetPadding(0, 3); recyclerView->SetPadding(0, 3);
recyclerView->SetItemSpacing(0, 3); recyclerView->SetItemSpacing(0, 3);
return recyclerView; return recyclerView;
} }
FileRecyclerAdapter* CreateRecyclerAdapter( SharedPtr<FileRecyclerAdapter> CreateRecyclerAdapter(
RomBrowserViewModel* viewModel, const IThemeFileIconFactory* themeFileIconFactory, RomBrowserViewModel* viewModel, const IThemeFileIconFactory* themeFileIconFactory,
const IRomBrowserViewFactory* romBrowserViewFactory, VBlankTextureLoader* vblankTextureLoader) const override const IRomBrowserViewFactory* romBrowserViewFactory, VBlankTextureLoader* vblankTextureLoader) const override
{ {
return new BannerListFileRecyclerAdapter( return SharedPtr<BannerListFileRecyclerAdapter>::MakeShared(viewModel->GetRomBrowserController(),
&viewModel->GetFileInfoManager(), viewModel->GetIoTaskQueue(), themeFileIconFactory, &viewModel->GetFileInfoManager(), viewModel->GetIoTaskQueue(), themeFileIconFactory,
romBrowserViewFactory, vblankTextureLoader); romBrowserViewFactory, vblankTextureLoader);
} }

View File

@@ -14,10 +14,10 @@ class RomBrowserDisplayMode
public: public:
virtual bool IsVertical() const = 0; virtual bool IsVertical() const = 0;
virtual bool ShowCoverOnTopScreen() const { return true; } virtual bool ShowCoverOnTopScreen() const { return true; }
virtual std::unique_ptr<AppBarView> CreateAppBarView(const IRomBrowserViewFactory* romBrowserViewFactory, virtual SharedPtr<AppBarView> CreateAppBarView(const IRomBrowserViewFactory* romBrowserViewFactory,
int startButtonCount, int endButtonCount) const = 0; int startButtonCount, int endButtonCount) const = 0;
virtual std::unique_ptr<RecyclerViewBase> CreateRecyclerView(const IRomBrowserViewFactory* romBrowserViewFactory) const = 0; virtual SharedPtr<RecyclerViewBase> CreateRecyclerView(const IRomBrowserViewFactory* romBrowserViewFactory) const = 0;
virtual FileRecyclerAdapter* CreateRecyclerAdapter( virtual SharedPtr<FileRecyclerAdapter> CreateRecyclerAdapter(
RomBrowserViewModel* viewModel, const IThemeFileIconFactory* themeFileIconFactory, RomBrowserViewModel* viewModel, const IThemeFileIconFactory* themeFileIconFactory,
const IRomBrowserViewFactory* romBrowserViewFactory, VBlankTextureLoader* vblankTextureLoader) const = 0; const IRomBrowserViewFactory* romBrowserViewFactory, VBlankTextureLoader* vblankTextureLoader) const = 0;
}; };

View File

@@ -9,19 +9,19 @@ public:
bool IsVertical() const override { return false; } bool IsVertical() const override { return false; }
bool ShowCoverOnTopScreen() const override { return false; } bool ShowCoverOnTopScreen() const override { return false; }
std::unique_ptr<AppBarView> CreateAppBarView(const IRomBrowserViewFactory* romBrowserViewFactory, SharedPtr<AppBarView> CreateAppBarView(const IRomBrowserViewFactory* romBrowserViewFactory,
int startButtonCount, int endButtonCount) const override int startButtonCount, int endButtonCount) const override
{ {
return romBrowserViewFactory->CreateAppBarView(0, 0, return romBrowserViewFactory->CreateAppBarView(0, 0,
AppBarView::Orientation::Horizontal, startButtonCount, endButtonCount); AppBarView::Orientation::Horizontal, startButtonCount, endButtonCount);
} }
std::unique_ptr<RecyclerViewBase> CreateRecyclerView(const IRomBrowserViewFactory* romBrowserViewFactory) const override SharedPtr<RecyclerViewBase> CreateRecyclerView(const IRomBrowserViewFactory* romBrowserViewFactory) const override
{ {
return romBrowserViewFactory->CreateCoverFlowRecyclerView(); return romBrowserViewFactory->CreateCoverFlowRecyclerView();
} }
FileRecyclerAdapter* CreateRecyclerAdapter( SharedPtr<FileRecyclerAdapter> CreateRecyclerAdapter(
RomBrowserViewModel* viewModel, const IThemeFileIconFactory* themeFileIconFactory, RomBrowserViewModel* viewModel, const IThemeFileIconFactory* themeFileIconFactory,
const IRomBrowserViewFactory* romBrowserViewFactory, VBlankTextureLoader* vblankTextureLoader) const override const IRomBrowserViewFactory* romBrowserViewFactory, VBlankTextureLoader* vblankTextureLoader) const override
{ {

View File

@@ -10,26 +10,26 @@ public:
bool IsVertical() const override { return false; } bool IsVertical() const override { return false; }
std::unique_ptr<AppBarView> CreateAppBarView(const IRomBrowserViewFactory* romBrowserViewFactory, SharedPtr<AppBarView> CreateAppBarView(const IRomBrowserViewFactory* romBrowserViewFactory,
int startButtonCount, int endButtonCount) const override int startButtonCount, int endButtonCount) const override
{ {
return romBrowserViewFactory->CreateAppBarView(0, 0, return romBrowserViewFactory->CreateAppBarView(0, 0,
AppBarView::Orientation::Horizontal, startButtonCount, endButtonCount); AppBarView::Orientation::Horizontal, startButtonCount, endButtonCount);
} }
std::unique_ptr<RecyclerViewBase> CreateRecyclerView(const IRomBrowserViewFactory* romBrowserViewFactory) const override SharedPtr<RecyclerViewBase> CreateRecyclerView(const IRomBrowserViewFactory* romBrowserViewFactory) const override
{ {
auto recyclerView = std::make_unique<RecyclerView>(0, 42, 256, 192 - 42, RecyclerView::Mode::HorizontalGrid); auto recyclerView = RecyclerView::CreateShared(0, 42, 256, 192 - 42, RecyclerView::Mode::HorizontalGrid);
recyclerView->SetPadding(10, 0); recyclerView->SetPadding(10, 0);
recyclerView->SetItemSpacing(4, 4); recyclerView->SetItemSpacing(4, 4);
return recyclerView; return recyclerView;
} }
FileRecyclerAdapter* CreateRecyclerAdapter( SharedPtr<FileRecyclerAdapter> CreateRecyclerAdapter(
RomBrowserViewModel* viewModel, const IThemeFileIconFactory* themeFileIconFactory, RomBrowserViewModel* viewModel, const IThemeFileIconFactory* themeFileIconFactory,
const IRomBrowserViewFactory* romBrowserViewFactory, VBlankTextureLoader* vblankTextureLoader) const override const IRomBrowserViewFactory* romBrowserViewFactory, VBlankTextureLoader* vblankTextureLoader) const override
{ {
return new IconGridFileRecyclerAdapter( return SharedPtr<IconGridFileRecyclerAdapter>::MakeShared(viewModel->GetRomBrowserController(),
&viewModel->GetFileInfoManager(), viewModel->GetIoTaskQueue(), &viewModel->GetFileInfoManager(), viewModel->GetIoTaskQueue(),
themeFileIconFactory, romBrowserViewFactory); themeFileIconFactory, romBrowserViewFactory);
} }

View File

@@ -10,26 +10,26 @@ public:
bool IsVertical() const override { return true; } bool IsVertical() const override { return true; }
std::unique_ptr<AppBarView> CreateAppBarView(const IRomBrowserViewFactory* romBrowserViewFactory, SharedPtr<AppBarView> CreateAppBarView(const IRomBrowserViewFactory* romBrowserViewFactory,
int startButtonCount, int endButtonCount) const override int startButtonCount, int endButtonCount) const override
{ {
return romBrowserViewFactory->CreateAppBarView(0, 0, return romBrowserViewFactory->CreateAppBarView(0, 0,
AppBarView::Orientation::Vertical, startButtonCount, endButtonCount); AppBarView::Orientation::Vertical, startButtonCount, endButtonCount);
} }
std::unique_ptr<RecyclerViewBase> CreateRecyclerView(const IRomBrowserViewFactory* romBrowserViewFactory) const override SharedPtr<RecyclerViewBase> CreateRecyclerView(const IRomBrowserViewFactory* romBrowserViewFactory) const override
{ {
auto recyclerView = std::make_unique<RecyclerView>(42, 0, 256 - 42, 192, RecyclerView::Mode::VerticalGrid); auto recyclerView = RecyclerView::CreateShared(42, 0, 256 - 42, 192, RecyclerView::Mode::VerticalGrid);
recyclerView->SetPadding(0, 3); recyclerView->SetPadding(0, 3);
recyclerView->SetItemSpacing(9, 3); recyclerView->SetItemSpacing(9, 3);
return recyclerView; return recyclerView;
} }
FileRecyclerAdapter* CreateRecyclerAdapter( SharedPtr<FileRecyclerAdapter> CreateRecyclerAdapter(
RomBrowserViewModel* viewModel, const IThemeFileIconFactory* themeFileIconFactory, RomBrowserViewModel* viewModel, const IThemeFileIconFactory* themeFileIconFactory,
const IRomBrowserViewFactory* romBrowserViewFactory, VBlankTextureLoader* vblankTextureLoader) const override const IRomBrowserViewFactory* romBrowserViewFactory, VBlankTextureLoader* vblankTextureLoader) const override
{ {
return new IconGridFileRecyclerAdapter( return SharedPtr<IconGridFileRecyclerAdapter>::MakeShared(viewModel->GetRomBrowserController(),
&viewModel->GetFileInfoManager(), viewModel->GetIoTaskQueue(), &viewModel->GetFileInfoManager(), viewModel->GetIoTaskQueue(),
themeFileIconFactory, romBrowserViewFactory); themeFileIconFactory, romBrowserViewFactory);
} }

View File

@@ -4,15 +4,15 @@
#include "FileInfo.h" #include "FileInfo.h"
FileInfo::FileInfo(const FileInfo& fileInfo) FileInfo::FileInfo(const FileInfo& fileInfo)
: _type(fileInfo._type), _fastFileRef(fileInfo._fastFileRef) : _type(fileInfo._type), _fastFileRef(fileInfo._fastFileRef), _attributes(fileInfo._attributes)
{ {
u32 bufferLength = strlen(fileInfo.GetFileName()) + 1; u32 bufferLength = strlen(fileInfo.GetFileName()) + 1;
_name = std::make_unique_for_overwrite<TCHAR[]>(bufferLength); _name = std::make_unique_for_overwrite<TCHAR[]>(bufferLength);
StringUtil::Copy(_name.get(), fileInfo.GetFileName(), bufferLength); StringUtil::Copy(_name.get(), fileInfo.GetFileName(), bufferLength);
} }
FileInfo::FileInfo(const TCHAR* fileName, const FileType* type, const FastFileRef& fastFileRef) FileInfo::FileInfo(const TCHAR* fileName, const FileType* type, const FastFileRef& fastFileRef, u8 attributes)
: _type(type), _fastFileRef(fastFileRef) : _type(type), _fastFileRef(fastFileRef), _attributes(attributes)
{ {
u32 bufferLength = strlen(fileName) + 1; u32 bufferLength = strlen(fileName) + 1;
_name = std::make_unique_for_overwrite<TCHAR[]>(bufferLength); _name = std::make_unique_for_overwrite<TCHAR[]>(bufferLength);

View File

@@ -10,7 +10,7 @@ class FileInfo
public: public:
FileInfo() { } FileInfo() { }
FileInfo(const FileInfo& fileInfo); FileInfo(const FileInfo& fileInfo);
FileInfo(const TCHAR* fileName, const FileType* type, const FastFileRef& fastFileRef); FileInfo(const TCHAR* fileName, const FileType* type, const FastFileRef& fastFileRef, u8 attributes);
FileInfo &operator=(FileInfo&& rhs) FileInfo &operator=(FileInfo&& rhs)
{ {
@@ -35,8 +35,13 @@ public:
const FastFileRef& GetFastFileRef() const { return _fastFileRef; } const FastFileRef& GetFastFileRef() const { return _fastFileRef; }
bool IsReadOnly() const { return _attributes & AM_RDO; }
bool IsHidden() const { return _attributes & AM_HID; }
bool IsSystem() const { return _attributes & AM_SYS; }
private: private:
std::unique_ptr<TCHAR[]> _name; std::unique_ptr<TCHAR[]> _name;
const FileType* _type; const FileType* _type;
FastFileRef _fastFileRef; FastFileRef _fastFileRef;
u8 _attributes;
}; };

View File

@@ -15,6 +15,34 @@ FileInfoManager::~FileInfoManager()
} }
} }
void FileInfoManager::LoadFileInfo(int index)
{
auto internalFileInfo = _extraFileInfo[index].internalFileInfo;
if (!internalFileInfo)
{
internalFileInfo = _items[index]->CreateInternalFileInfo();
}
if (!_extraFileInfo[index].fileCover.Lock())
{
_extraFileInfo[index].fileCover = SharedPtr(_coverRepository.GetCoverForFile(*_items[index], internalFileInfo));
}
_extraFileInfo[index].internalFileInfo = internalFileInfo;
}
void FileInfoManager::ReleaseFileInfo(int index)
{
auto internalFileInfo = _extraFileInfo[index].internalFileInfo;
if (internalFileInfo)
{
_extraFileInfo[index].internalFileInfo = nullptr;
delete internalFileInfo;
}
_extraFileInfo[index].fileCover.Reset();
}
int FileInfoManager::GetItemIndex(const char* fileName) int FileInfoManager::GetItemIndex(const char* fileName)
{ {
if (fileName == nullptr) if (fileName == nullptr)

View File

@@ -4,7 +4,7 @@
#include "FileInfo.h" #include "FileInfo.h"
#include "FileType/FileCover.h" #include "FileType/FileCover.h"
#include "ICoverRepository.h" #include "ICoverRepository.h"
#include "core/SharedPtr.h" #include "core/AtomicSharedPtr.h"
#include "FileType/InternalFileInfo.h" #include "FileType/InternalFileInfo.h"
class FileInfoManager class FileInfoManager
@@ -20,36 +20,12 @@ public:
SharedPtr<FileCover> GetFileCover(int index) SharedPtr<FileCover> GetFileCover(int index)
{ {
return _extraFileInfo[index].fileCover; return _extraFileInfo[index].fileCover.Lock();
} }
void LoadFileInfo(int index) void LoadFileInfo(int index);
{
auto internalFileInfo = GetInternalFileInfo(index);
if (!internalFileInfo)
{
internalFileInfo = _items[index]->CreateInternalFileInfo();
}
if (!_extraFileInfo[index].fileCover.IsValid()) void ReleaseFileInfo(int index);
{
_extraFileInfo[index].fileCover = SharedPtr(_coverRepository.GetCoverForFile(*_items[index], internalFileInfo));
}
_extraFileInfo[index].internalFileInfo = internalFileInfo;
}
void ReleaseFileInfo(int index)
{
auto internalFileInfo = GetInternalFileInfo(index);
if (internalFileInfo)
{
delete internalFileInfo;
_extraFileInfo[index].internalFileInfo = nullptr;
}
_extraFileInfo[index].fileCover.Reset();
}
int GetItemIndex(const char* fileName); int GetItemIndex(const char* fileName);
@@ -60,7 +36,7 @@ private:
struct ExtraFileInfo struct ExtraFileInfo
{ {
const InternalFileInfo* internalFileInfo; const InternalFileInfo* internalFileInfo;
SharedPtr<FileCover> fileCover; AtomicSharedPtr<FileCover> fileCover;
}; };
std::unique_ptr<const FileInfo*[]> _items; std::unique_ptr<const FileInfo*[]> _items;

View File

@@ -8,11 +8,17 @@ u32 FileRecyclerAdapter::GetItemCount() const
return _fileInfoManager->GetItemCount(); return _fileInfoManager->GetItemCount();
} }
void FileRecyclerAdapter::BindView(View* view, int index) const void FileRecyclerAdapter::BindView(SharedPtr<View> view, int index) const
{ {
LOG_DEBUG("Binding %d\n", index); LOG_DEBUG("Binding %d\n", index);
_taskQueue->Enqueue([=, this] (const vu8& cancelRequested) auto queueTask = _taskQueue->Enqueue([=, this] (const vu8& cancelRequested)
{ {
if (cancelRequested)
{
LOG_DEBUG("Task to load %d was canceled\n", index);
return TaskResult<void>::Canceled();
}
LOG_DEBUG("Started task to load %d\n", index); LOG_DEBUG("Started task to load %d\n", index);
_fileInfoManager->LoadFileInfo(index); _fileInfoManager->LoadFileInfo(index);
auto internalFileInfo = _fileInfoManager->GetInternalFileInfo(index); auto internalFileInfo = _fileInfoManager->GetInternalFileInfo(index);
@@ -23,4 +29,5 @@ void FileRecyclerAdapter::BindView(View* view, int index) const
} }
return BindView(view, index, internalFileInfo, cancelRequested); return BindView(view, index, internalFileInfo, cancelRequested);
}); });
SetQueueTask(view, std::move(queueTask));
} }

View File

@@ -7,12 +7,13 @@ class IVramManager;
class InternalFileInfo; class InternalFileInfo;
class IThemeFileIconFactory; class IThemeFileIconFactory;
class VramContext; class VramContext;
class IRomBrowserController;
class FileRecyclerAdapter : public RecyclerAdapter class FileRecyclerAdapter : public RecyclerAdapter
{ {
public: public:
u32 GetItemCount() const override; u32 GetItemCount() const override;
void BindView(View* view, int index) const override; void BindView(SharedPtr<View> view, int index) const override;
void SetIconFrameCounter(u32 iconFrameCounter) void SetIconFrameCounter(u32 iconFrameCounter)
{ {
@@ -22,16 +23,18 @@ public:
virtual void InitVram(const VramContext& vramContext) { } virtual void InitVram(const VramContext& vramContext) { }
protected: protected:
IRomBrowserController* _romBrowserController;
FileInfoManager* _fileInfoManager; FileInfoManager* _fileInfoManager;
TaskQueueBase* _taskQueue; TaskQueueBase* _taskQueue;
u32 _iconFrameCounter; u32 _iconFrameCounter;
const IThemeFileIconFactory* _themeFileIconFactory; const IThemeFileIconFactory* _themeFileIconFactory;
FileRecyclerAdapter(FileInfoManager* fileInfoManager, TaskQueueBase* taskQueue, FileRecyclerAdapter(IRomBrowserController* romBrowserController, FileInfoManager* fileInfoManager,
const IThemeFileIconFactory* themeFileIconFactory) TaskQueueBase* taskQueue, const IThemeFileIconFactory* themeFileIconFactory)
: _fileInfoManager(fileInfoManager), _taskQueue(taskQueue) : _romBrowserController(romBrowserController), _fileInfoManager(fileInfoManager), _taskQueue(taskQueue)
, _iconFrameCounter(0), _themeFileIconFactory(themeFileIconFactory) { } , _iconFrameCounter(0), _themeFileIconFactory(themeFileIconFactory) { }
virtual TaskResult<void> BindView(View* view, int index, virtual TaskResult<void> BindView(SharedPtr<View> view, int index,
const InternalFileInfo* internalFileInfo, const vu8& cancelRequested) const = 0; const InternalFileInfo* internalFileInfo, const vu8& cancelRequested) const = 0;
virtual void SetQueueTask(const SharedPtr<View>& view, QueueTask<void> queueTask) const = 0;
}; };

View File

@@ -5,7 +5,7 @@
class PaletteManager; class PaletteManager;
class GraphicsContext; class GraphicsContext;
#define FILE_ICON_VRAM_SIZE 4096 #define FILE_ICON_VRAM_SIZE 1024
/// @brief Abstract base class representing a file icon. /// @brief Abstract base class representing a file icon.
class FileIcon class FileIcon
@@ -13,9 +13,17 @@ class FileIcon
public: public:
virtual ~FileIcon() = 0; virtual ~FileIcon() = 0;
/// @brief Uploads the graphics of this icon to the specified \p vram address. /// @brief Sets the OBJ vram address and offset to use.
/// @param vram The vram address to load the graphics to. /// @param objVramAddress The OBJ vram address to use.
virtual void UploadGraphics(vu16* vram) const = 0; /// @param objVramOffset The OBJ vram offset to use.
virtual void SetVramAddress(vu16* objVramAddress, u32 objVramOffset)
{
_vramAddress = objVramAddress;
_vramOffset = objVramOffset;
}
/// @brief Uploads the graphics of this icon to the vram address specified by SetVramAddress.
virtual void UploadGraphics() = 0;
/// @brief Updates this icon. /// @brief Updates this icon.
virtual void Update() { } virtual void Update() { }
@@ -25,10 +33,6 @@ public:
/// @param backgroundColor The color on which the icon is drawn. /// @param backgroundColor The color on which the icon is drawn.
virtual void Draw(GraphicsContext& graphicsContext, const Rgb<8, 8, 8>& backgroundColor) = 0; virtual void Draw(GraphicsContext& graphicsContext, const Rgb<8, 8, 8>& backgroundColor) = 0;
/// @brief Sets the OBJ vram offset of this icon.
/// @param offset The OBJ vram offset.
void SetObjVramOffset(u32 offset) { _vramOffset = offset; }
/// @brief Sets the icon animation frame. /// @brief Sets the icon animation frame.
/// @param frame The animation frame. /// @param frame The animation frame.
void SetAnimFrame(u32 frame) void SetAnimFrame(u32 frame)
@@ -50,6 +54,7 @@ public:
} }
protected: protected:
vu16* _vramAddress = nullptr;
u32 _vramOffset = 0; u32 _vramOffset = 0;
u32 _frame = 0; u32 _frame = 0;
Point _position; Point _position;

View File

@@ -1,5 +1,4 @@
#include "common.h" #include "common.h"
#include <libtwl/math/mathDiv.h>
#include <libtwl/dma/dmaNitro.h> #include <libtwl/dma/dmaNitro.h>
#include "gui/PaletteManager.h" #include "gui/PaletteManager.h"
#include "gui/OamManager.h" #include "gui/OamManager.h"
@@ -30,25 +29,36 @@ NdsFileIcon::NdsFileIcon(const nds_banner_t* banner)
break; break;
} }
else else
{
length += token.duration; length += token.duration;
} }
}
_animLength = length; _animLength = length;
_tokenStartTimes[NDS_BANNER_ANIM_TOKEN_COUNT] = _animLength; _tokenStartTimes[NDS_BANNER_ANIM_TOKEN_COUNT] = _animLength;
} }
} }
void NdsFileIcon::UploadGraphics(vu16* vram) const void NdsFileIcon::SetVramAddress(vu16* objVramAddress, u32 objVramOffset)
{ {
if (_animated) FileIcon::SetVramAddress(objVramAddress, objVramOffset);
dma_ntrCopy32(3, _banner->animation.iconGfx, vram, sizeof(_banner->animation.iconGfx)); _currentVramSlot = 0;
else _currentGfxIdx = -1;
dma_ntrCopy32(3, _banner->iconGfx, vram, sizeof(_banner->iconGfx)); }
void NdsFileIcon::UploadGraphics()
{
if (_vramAddress != nullptr && !_animated)
{
dma_ntrCopy32(3, _banner->iconGfx, _vramAddress, sizeof(_banner->iconGfx));
}
} }
void NdsFileIcon::Update() void NdsFileIcon::Update()
{ {
if (!_animated) if (!_animated)
{
return; return;
}
_frame %= _animLength; _frame %= _animLength;
@@ -71,48 +81,31 @@ void NdsFileIcon::Update()
break; break;
} }
else if (_frame < midTime) else if (_frame < midTime)
{
end = mid - 1; end = mid - 1;
}
else else
{
start = mid + 1; start = mid + 1;
} }
}
_animTokenIdx = start; _animTokenIdx = start;
} }
if (++_frame == _animLength) if (++_frame == _animLength)
{
_frame = 0; _frame = 0;
}
// if (++_durationCounter < _banner->animation.animTokens[_animTokenIdx].duration)
// return;
// _durationCounter = 0;
// if (++_animTokenIdx >= NDS_BANNER_ANIM_TOKEN_COUNT)
// {
// _animTokenIdx = 0;
// return;
// }
// if (_banner->animation.animTokens[_animTokenIdx].duration == NDS_BANNER_ANIM_DURATION_CONTROL_FRAME)
// {
// switch (_banner->animation.animTokens[_animTokenIdx].control)
// {
// case NDS_BANNER_ANIM_CONTROL_LOOP:
// _animTokenIdx = 0;
// break;
// case NDS_BANNER_ANIM_CONTROL_STOP:
// _animTokenIdx--;
// break;
// }
// }
} }
void NdsFileIcon::Draw(GraphicsContext& graphicsContext, const Rgb<8, 8, 8>& backgroundColor) void NdsFileIcon::Draw(GraphicsContext& graphicsContext, const Rgb<8, 8, 8>& backgroundColor)
{ {
if (!graphicsContext.IsVisible(Rectangle(_position, 32, 32))) if (!graphicsContext.IsVisible(Rectangle(_position, 32, 32)) ||
_vramAddress == nullptr)
{
return; return;
}
const u16* palette = _animated const u16* palette = _animated
? _banner->animation.iconPltt[_banner->animation.animTokens[_animTokenIdx].plttIdx] ? _banner->animation.iconPltt[_banner->animation.animTokens[_animTokenIdx].plttIdx]
@@ -123,7 +116,17 @@ void NdsFileIcon::Draw(GraphicsContext& graphicsContext, const Rgb<8, 8, 8>& bac
u32 vramOffset = _vramOffset; u32 vramOffset = _vramOffset;
if (_animated) if (_animated)
vramOffset += _banner->animation.animTokens[_animTokenIdx].gfxIdx * 512; {
int gfxIdx = _banner->animation.animTokens[_animTokenIdx].gfxIdx;
if (gfxIdx != _currentGfxIdx)
{
_currentVramSlot = 1 - _currentVramSlot;
_currentGfxIdx = gfxIdx;
dma_ntrCopy32(3, &_banner->animation.iconGfx[gfxIdx][0], (u8*)_vramAddress + (_currentVramSlot * NDS_BANNER_ICON_SIZE), NDS_BANNER_ICON_SIZE);
}
vramOffset += _currentVramSlot * NDS_BANNER_ICON_SIZE;
}
auto builder = OamBuilder::OamWithSize<32, 32>( auto builder = OamBuilder::OamWithSize<32, 32>(
_position, vramOffset >> 7) _position, vramOffset >> 7)

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