mirror of
https://github.com/LNH-team/pico-launcher.git
synced 2026-06-02 17:16:57 +02:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b087565651 | ||
|
|
281a781c13 | ||
|
|
5569c005a5 | ||
|
|
9f9f0143a2 | ||
|
|
97762b14d3 | ||
|
|
21a8790ebc | ||
|
|
bec797ffe7 | ||
|
|
127de36b1c | ||
|
|
5442c02341 | ||
|
|
cf9ce63db5 | ||
|
|
53727e5fdd | ||
|
|
9ca3e38668 | ||
|
|
3f780fdd69 | ||
|
|
7c06abf224 | ||
|
|
2c142caa98 | ||
|
|
b7d7f9f352 | ||
|
|
601fd6371e | ||
|
|
a4ecea6802 | ||
|
|
6c34d9324d | ||
|
|
43b1bf7afa | ||
|
|
12ebd482d4 | ||
|
|
4d9318b0b9 | ||
|
|
10431c4615 | ||
|
|
e2e42115e7 | ||
|
|
a9425eea7c | ||
|
|
7f35d524ae | ||
|
|
126b898ac4 | ||
|
|
f54a379ff2 | ||
|
|
dddee0bb94 | ||
|
|
f73c8b0547 | ||
|
|
a102068433 |
1
.github/workflows/nightly.yml
vendored
1
.github/workflows/nightly.yml
vendored
@@ -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:
|
||||||
|
|||||||
37
CHANGELOG.md
Normal file
37
CHANGELOG.md
Normal 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
|
||||||
@@ -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 \
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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
146
arm7/source/touchScreen.cpp
Normal 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;
|
||||||
|
}
|
||||||
5
arm7/source/touchScreen.h
Normal file
5
arm7/source/touchScreen.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <nds/touch.h>
|
||||||
|
|
||||||
|
void touch_init();
|
||||||
|
bool touch_update(touchPosition& touchPos);
|
||||||
7
arm9/gfx/cheatSelector.grit
Normal file
7
arm9/gfx/cheatSelector.grit
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# tile format
|
||||||
|
-gt
|
||||||
|
|
||||||
|
# graphics bit depth is 4 (16 color)
|
||||||
|
-gB4
|
||||||
|
|
||||||
|
-p!
|
||||||
BIN
arm9/gfx/cheatSelector.png
Normal file
BIN
arm9/gfx/cheatSelector.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 155 B |
7
arm9/gfx/checkboxChecked.grit
Normal file
7
arm9/gfx/checkboxChecked.grit
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# tile format
|
||||||
|
-gt
|
||||||
|
|
||||||
|
# graphics bit depth is 4 (16 color)
|
||||||
|
-gB4
|
||||||
|
|
||||||
|
-p!
|
||||||
BIN
arm9/gfx/checkboxChecked.png
Normal file
BIN
arm9/gfx/checkboxChecked.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 203 B |
7
arm9/gfx/checkboxUnchecked.grit
Normal file
7
arm9/gfx/checkboxUnchecked.grit
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# tile format
|
||||||
|
-gt
|
||||||
|
|
||||||
|
# graphics bit depth is 4 (16 color)
|
||||||
|
-gB4
|
||||||
|
|
||||||
|
-p!
|
||||||
BIN
arm9/gfx/checkboxUnchecked.png
Normal file
BIN
arm9/gfx/checkboxUnchecked.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 167 B |
7
arm9/gfx/folderIcon.grit
Normal file
7
arm9/gfx/folderIcon.grit
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# tile format
|
||||||
|
-gt
|
||||||
|
|
||||||
|
# graphics bit depth is 4 (16 color)
|
||||||
|
-gB4
|
||||||
|
|
||||||
|
-p!
|
||||||
BIN
arm9/gfx/folderIcon.png
Normal file
BIN
arm9/gfx/folderIcon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 177 B |
7
arm9/gfx/upIcon.grit
Normal file
7
arm9/gfx/upIcon.grit
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# tile format
|
||||||
|
-gt
|
||||||
|
|
||||||
|
# graphics bit depth is 4 (16 color)
|
||||||
|
-gB4
|
||||||
|
|
||||||
|
-p!
|
||||||
BIN
arm9/gfx/upIcon.png
Normal file
BIN
arm9/gfx/upIcon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 179 B |
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -16,10 +16,12 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -152,4 +154,28 @@ 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
190
arm9/source/cheats/CheatEntry.h
Normal file
190
arm9/source/cheats/CheatEntry.h
Normal 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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
17
arm9/source/cheats/EmptyCheatRepository.h
Normal file
17
arm9/source/cheats/EmptyCheatRepository.h
Normal 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
|
||||||
|
}
|
||||||
|
};
|
||||||
32
arm9/source/cheats/GameCheats.h
Normal file
32
arm9/source/cheats/GameCheats.h
Normal 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;
|
||||||
|
};
|
||||||
22
arm9/source/cheats/ICheatRepository.h
Normal file
22
arm9/source/cheats/ICheatRepository.h
Normal 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() { }
|
||||||
|
};
|
||||||
80
arm9/source/cheats/PicoLoaderCheatDataFactory.cpp
Normal file
80
arm9/source/cheats/PicoLoaderCheatDataFactory.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
arm9/source/cheats/PicoLoaderCheatDataFactory.h
Normal file
18
arm9/source/cheats/PicoLoaderCheatDataFactory.h
Normal 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;
|
||||||
|
};
|
||||||
12
arm9/source/cheats/UsrCheatDat.h
Normal file
12
arm9/source/cheats/UsrCheatDat.h
Normal 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);
|
||||||
225
arm9/source/cheats/UsrCheatRepository.cpp
Normal file
225
arm9/source/cheats/UsrCheatRepository.cpp
Normal 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);
|
||||||
|
}
|
||||||
28
arm9/source/cheats/UsrCheatRepository.h
Normal file
28
arm9/source/cheats/UsrCheatRepository.h
Normal 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;
|
||||||
|
};
|
||||||
87
arm9/source/cheats/UsrCheatRepositoryFactory.cpp
Normal file
87
arm9/source/cheats/UsrCheatRepositoryFactory.cpp
Normal 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);
|
||||||
|
}
|
||||||
13
arm9/source/cheats/UsrCheatRepositoryFactory.h
Normal file
13
arm9/source/cheats/UsrCheatRepositoryFactory.h
Normal 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);
|
||||||
|
};
|
||||||
35
arm9/source/core/AtomicSharedPtr.cpp
Normal file
35
arm9/source/core/AtomicSharedPtr.cpp
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
134
arm9/source/core/AtomicSharedPtr.h
Normal file
134
arm9/source/core/AtomicSharedPtr.h
Normal 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
42
arm9/source/core/EnableSharedFromThis.h
Normal file
42
arm9/source/core/EnableSharedFromThis.h
Normal 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
58
arm9/source/core/RefCount.h
Normal file
58
arm9/source/core/RefCount.h
Normal 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);
|
||||||
|
};
|
||||||
31
arm9/source/core/SharedPtr.cpp
Normal file
31
arm9/source/core/SharedPtr.cpp
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
class SharedPtrBase
|
||||||
{
|
{
|
||||||
T* _pointer;
|
|
||||||
vu32* _refCount;
|
|
||||||
|
|
||||||
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)
|
_refCount = other._refCount;
|
||||||
|
if (_refCount)
|
||||||
{
|
{
|
||||||
vu32* refCount = _refCount;
|
shared_ptr_increase_ref_count(_refCount->refCount);
|
||||||
u32 newValue = *refCount - 1;
|
}
|
||||||
*refCount = newValue;
|
return *this;
|
||||||
_pointer = other._pointer;
|
}
|
||||||
_refCount = other._refCount;
|
|
||||||
if (_pointer)
|
template <class Y> requires std::assignable_from<T*&, Y*>
|
||||||
{
|
SharedPtr<T>& operator=(const SharedPtr<Y>& other)
|
||||||
(*_refCount)++;
|
{
|
||||||
}
|
Reset();
|
||||||
arm_restoreIrqs(irq);
|
_object = static_cast<T*>(other.GetPointer());
|
||||||
if (newValue == 0)
|
_refCount = other._refCount;
|
||||||
{
|
if (_refCount)
|
||||||
delete pointer;
|
{
|
||||||
delete refCount;
|
shared_ptr_increase_ref_count(_refCount->refCount);
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_pointer = other._pointer;
|
|
||||||
_refCount = other._refCount;
|
|
||||||
if (_pointer)
|
|
||||||
{
|
|
||||||
(*_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)
|
_refCount = other._refCount;
|
||||||
{
|
other._object = nullptr;
|
||||||
vu32* refCount = _refCount;
|
other._refCount = nullptr;
|
||||||
u32 newValue = *refCount - 1;
|
|
||||||
*refCount = newValue;
|
|
||||||
_pointer = other._pointer;
|
|
||||||
_refCount = other._refCount;
|
|
||||||
other._pointer = nullptr;
|
|
||||||
other._refCount = nullptr;
|
|
||||||
arm_restoreIrqs(irq);
|
|
||||||
if (newValue == 0)
|
|
||||||
{
|
|
||||||
delete pointer;
|
|
||||||
delete refCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_pointer = other._pointer;
|
|
||||||
_refCount = other._refCount;
|
|
||||||
other._pointer = nullptr;
|
|
||||||
other._refCount = nullptr;
|
|
||||||
arm_restoreIrqs(irq);
|
|
||||||
}
|
|
||||||
return *this;
|
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:
|
||||||
|
|||||||
15
arm9/source/core/SharedPtrAsm.s
Normal file
15
arm9/source/core/SharedPtrAsm.s
Normal 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
|
||||||
35
arm9/source/core/WeakPtr.cpp
Normal file
35
arm9/source/core/WeakPtr.cpp
Normal 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
158
arm9/source/core/WeakPtr.h
Normal 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) { }
|
||||||
|
};
|
||||||
@@ -74,4 +74,6 @@ 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>;
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public:
|
|||||||
|
|
||||||
constexpr fixed(long double value)
|
constexpr fixed(long double value)
|
||||||
: _value(std::round(value * (1 << FractionBits))) { }
|
: _value(std::round(value * (1 << FractionBits))) { }
|
||||||
|
|
||||||
constexpr T Int() const
|
constexpr T Int() const
|
||||||
{
|
{
|
||||||
return _value >> FractionBits;
|
return _value >> FractionBits;
|
||||||
@@ -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());
|
||||||
|
|||||||
@@ -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,7 +23,9 @@ 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)
|
||||||
|
|||||||
@@ -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); }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,25 +12,13 @@ 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();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -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];
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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())
|
||||||
while (focusView)
|
|
||||||
{
|
{
|
||||||
if (view == focusView)
|
auto focusView = currentFocus.GetPointer();
|
||||||
return true;
|
while (focusView)
|
||||||
focusView = focusView->GetParent();
|
{
|
||||||
|
if (view == focusView)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
focusView = focusView->GetParent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
// by returning we only render complete glyphs
|
if (renderParams->onlyRenderWholeGlyphs)
|
||||||
return;
|
{
|
||||||
// old code for rendering partial glyphs
|
return;
|
||||||
// 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;
|
||||||
}
|
}
|
||||||
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
14
arm9/source/gui/input/ITouchInputSource.h
Normal file
14
arm9/source/gui/input/ITouchInputSource.h
Normal 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;
|
||||||
|
};
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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) { }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,7 +27,9 @@ void InputRepeater::Update()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
_state = State::Idle;
|
_state = State::Idle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (_state == State::NextRepeat)
|
else if (_state == State::NextRepeat)
|
||||||
{
|
{
|
||||||
@@ -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()
|
||||||
|
|||||||
@@ -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) { }
|
||||||
|
|
||||||
|
|||||||
@@ -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)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
24
arm9/source/gui/input/TouchInputSource.h
Normal file
24
arm9/source/gui/input/TouchInputSource.h
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -4,17 +4,21 @@
|
|||||||
|
|
||||||
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;
|
||||||
VBlankTextureLoader* _vblankTextureLoader;
|
VBlankTextureLoader* _vblankTextureLoader;
|
||||||
VBlankTextureLoadRequest _textureLoadRequest;
|
VBlankTextureLoadRequest _textureLoadRequest;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
};
|
};
|
||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
|
SetSelectedItem(std::clamp(idx, 0, ((int)_itemCount - 1) / _columns * _columns));
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
int idx = (-_yOffset + currentFocus->GetPosition().y - _yPadding + ((_ySpacing + _itemHeight) >> 1)) / (_ySpacing + _itemHeight) * _columns;
|
return nullptr;
|
||||||
SetSelectedItem(std::clamp(idx, 0, ((int)_itemCount - 1) / _columns * _columns));
|
|
||||||
return _selectedItem != nullptr ? _selectedItem->view : this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|||||||
@@ -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);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
58
arm9/source/gui/views/ViewContainer.cpp
Normal file
58
arm9/source/gui/views/ViewContainer.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ 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 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()
|
||||||
{
|
{
|
||||||
@@ -37,6 +38,11 @@ void pload_setLauncherPath(const char* launcherPath)
|
|||||||
StringUtil::Copy(sLauncherPath, launcherPath, sizeof(sLauncherPath));
|
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);
|
||||||
@@ -92,6 +98,10 @@ void pload_start()
|
|||||||
{
|
{
|
||||||
dma_ntrCopy16(3, &sLauncherPath, &header->v2.launcherPath, sizeof(header->v2.launcherPath));
|
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);
|
||||||
|
|||||||
@@ -4,4 +4,5 @@
|
|||||||
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_setLauncherPath(const char* launcherPath);
|
||||||
|
void pload_setCheatData(const pload_cheats_t* cheatData);
|
||||||
void pload_start();
|
void pload_start();
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -23,10 +51,10 @@ int FileInfoManager::GetItemIndex(const char* fileName)
|
|||||||
}
|
}
|
||||||
for (u32 i = 0; i < _itemCount; i++)
|
for (u32 i = 0; i < _itemCount; i++)
|
||||||
{
|
{
|
||||||
if (strcmp(fileName, _items[i]->GetFileName()) == 0)
|
if (strcmp(fileName, _items[i]->GetFileName()) == 0)
|
||||||
{
|
{
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -25,30 +24,41 @@ NdsFileIcon::NdsFileIcon(const nds_banner_t* banner)
|
|||||||
const auto& token = _banner->animation.animTokens[i];
|
const auto& token = _banner->animation.animTokens[i];
|
||||||
if (token.duration == NDS_BANNER_ANIM_DURATION_CONTROL_FRAME)
|
if (token.duration == NDS_BANNER_ANIM_DURATION_CONTROL_FRAME)
|
||||||
{
|
{
|
||||||
_loop = token.control != NDS_BANNER_ANIM_CONTROL_STOP;
|
_loop = token.control != NDS_BANNER_ANIM_CONTROL_STOP;
|
||||||
_lastAnimToken = i - 1;
|
_lastAnimToken = i - 1;
|
||||||
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)
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ class NdsFileIcon : public FileIcon
|
|||||||
public:
|
public:
|
||||||
explicit NdsFileIcon(const nds_banner_t* banner);
|
explicit NdsFileIcon(const nds_banner_t* banner);
|
||||||
|
|
||||||
void UploadGraphics(vu16* vram) const override;
|
void SetVramAddress(vu16* objVramAddress, u32 objVramOffset) override;
|
||||||
|
void UploadGraphics() override;
|
||||||
void Update() override;
|
void Update() override;
|
||||||
void Draw(GraphicsContext& graphicsContext, const Rgb<8, 8, 8>& backgroundColor) override;
|
void Draw(GraphicsContext& graphicsContext, const Rgb<8, 8, 8>& backgroundColor) override;
|
||||||
|
|
||||||
@@ -16,9 +17,11 @@ private:
|
|||||||
const nds_banner_t* _banner;
|
const nds_banner_t* _banner;
|
||||||
bool _animated;
|
bool _animated;
|
||||||
int _animTokenIdx;
|
int _animTokenIdx;
|
||||||
// int _durationCounter;
|
|
||||||
u32 _lastAnimToken;
|
u32 _lastAnimToken;
|
||||||
u32 _animLength;
|
u32 _animLength;
|
||||||
u16 _tokenStartTimes[65];
|
u16 _tokenStartTimes[65];
|
||||||
bool _loop;
|
bool _loop;
|
||||||
|
|
||||||
|
int _currentVramSlot = 0;
|
||||||
|
int _currentGfxIdx = -1;
|
||||||
};
|
};
|
||||||
@@ -2,52 +2,60 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <nds/arm9/cache.h>
|
#include <nds/arm9/cache.h>
|
||||||
#include "fat/File.h"
|
#include "fat/File.h"
|
||||||
#include "romBrowser/ICoverRepository.h"
|
|
||||||
#include "NdsInternalFileInfo.h"
|
#include "NdsInternalFileInfo.h"
|
||||||
|
|
||||||
NdsInternalFileInfo::NdsInternalFileInfo(const FastFileRef& fastFileRef)
|
NdsInternalFileInfo::NdsInternalFileInfo(const FastFileRef& fastFileRef)
|
||||||
{
|
{
|
||||||
const auto file = std::make_unique<File>();
|
const auto file = std::make_unique<File>();
|
||||||
_hasBanner = false;
|
|
||||||
memset(_gameCode, 0, sizeof(_gameCode));
|
memset(_gameCode, 0, sizeof(_gameCode));
|
||||||
|
|
||||||
file->Open(fastFileRef, FA_READ);
|
file->Open(fastFileRef, FA_READ);
|
||||||
|
|
||||||
if (file->Seek(0xC) != FR_OK)
|
|
||||||
return;
|
|
||||||
|
|
||||||
u32 bytesRead;
|
|
||||||
if (file->Read(_gameCode, 4, bytesRead) != FR_OK)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (file->Seek(0x68) != FR_OK)
|
|
||||||
return;
|
|
||||||
|
|
||||||
u32 bannerOffset;
|
u32 bannerOffset;
|
||||||
if (file->Read(&bannerOffset, 4, bytesRead) != FR_OK)
|
if (file->Seek(0xC) != FR_OK ||
|
||||||
return;
|
!file->ReadExact(_gameCode, 4) ||
|
||||||
|
file->Seek(0x68) != FR_OK ||
|
||||||
if (bannerOffset == 0)
|
!file->ReadExact(&bannerOffset, 4) ||
|
||||||
return;
|
bannerOffset == 0 ||
|
||||||
|
bannerOffset >= file->GetSize() ||
|
||||||
if (file->Seek(bannerOffset) != FR_OK)
|
file->Seek(bannerOffset) != FR_OK ||
|
||||||
return;
|
!file->ReadExact(&_banner, 0x840))
|
||||||
|
|
||||||
if (file->Read(&_banner, 0xA00, bytesRead) != FR_OK)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_banner.header.version >= NDS_BANNER_VERSION_103)
|
|
||||||
{
|
{
|
||||||
if (file->Read(((u8*)&_banner) + 0xA00, 0x19C0, bytesRead) != FR_OK)
|
return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_banner.header.version >= NDS_BANNER_VERSION_2 &&
|
||||||
|
!file->ReadExact(((u8*)&_banner) + 0x840, 0x100))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_banner.header.version >= NDS_BANNER_VERSION_3 &&
|
||||||
|
!file->ReadExact(((u8*)&_banner) + 0x940, 0x100))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_banner.header.version >= NDS_BANNER_VERSION_103 &&
|
||||||
|
!file->ReadExact(((u8*)&_banner) + 0xA40, 0x1980))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_hasBanner = true;
|
_hasBanner = true;
|
||||||
DC_FlushRange(&_banner, sizeof(_banner));
|
DC_FlushRange(&_banner, sizeof(_banner));
|
||||||
}
|
}
|
||||||
|
|
||||||
const char16_t* NdsInternalFileInfo::GetGameTitle() const
|
const char16_t* NdsInternalFileInfo::GetGameTitle() const
|
||||||
{
|
{
|
||||||
if (!_hasBanner)
|
return _hasBanner
|
||||||
return nullptr;
|
? _banner.title[NDS_BANNER_TITLE_LANGUAGE_ENGLISH]
|
||||||
return _banner.title[NDS_BANNER_TITLE_LANGUAGE_ENGLISH];
|
: nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<FileIcon> NdsInternalFileInfo::CreateGameIcon() const
|
||||||
|
{
|
||||||
|
return _hasBanner
|
||||||
|
? std::make_unique<NdsFileIcon>(&_banner)
|
||||||
|
: nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,18 +12,11 @@ public:
|
|||||||
|
|
||||||
constexpr const char* GetGameCode() const override { return _gameCode; }
|
constexpr const char* GetGameCode() const override { return _gameCode; }
|
||||||
const char16_t* GetGameTitle() const override;
|
const char16_t* GetGameTitle() const override;
|
||||||
|
std::unique_ptr<FileIcon> CreateGameIcon() const override;
|
||||||
std::unique_ptr<FileIcon> CreateGameIcon() const override
|
|
||||||
{
|
|
||||||
if (!_hasBanner)
|
|
||||||
return nullptr;
|
|
||||||
return std::make_unique<NdsFileIcon>(&_banner);
|
|
||||||
}
|
|
||||||
|
|
||||||
const nds_banner_t& GetBanner() const { return _banner; }
|
const nds_banner_t& GetBanner() const { return _banner; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
nds_banner_t _banner alignas(32);
|
nds_banner_t _banner alignas(32);
|
||||||
bool _hasBanner;
|
bool _hasBanner = false;
|
||||||
char _gameCode[5];
|
char _gameCode[5];
|
||||||
};
|
};
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user