Merge branch 'master' (early part) into medusa

This commit is contained in:
Vicki Pfau 2021-06-29 20:43:48 -07:00
commit cff95f3213
59 changed files with 4227 additions and 3666 deletions

View File

@ -36,6 +36,13 @@ Misc:
0.10.0: (Future)
Features:
- Tool for converting scanned pictures of e-Reader cards to raw dotcode data
- Cheat code support in homebrew ports
Emulation fixes:
- GBA Video: Revert scanline latching changes (fixes mgba.io/i/2153, mgba.io/i/2149)
Other fixes:
- Core: Fix memory leak in opening games from the library
- Qt: Fix infrequent deadlock when using sync to video
- Qt: Fix applying savetype-only overrides
Misc:
- Qt: Rearrange menus some

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -14,6 +14,9 @@ CXX_GUARD_START
#include <mgba/core/input.h>
#include <mgba-util/vector.h>
#define MAX_KEYBOARD_LEN 256
#define MAX_KEYBOARD_TITLE_LEN 128
struct GUIFont;
enum GUIInput {
@ -40,6 +43,11 @@ enum GUICursorState {
GUI_CURSOR_DRAGGING
};
enum GUIKeyboardStatus {
GUI_KEYBOARD_DONE = 0,
GUI_KEYBOARD_CANCEL,
};
enum {
BATTERY_EMPTY = 0,
BATTERY_LOW = 25,
@ -57,6 +65,13 @@ struct GUIBackground {
void (*draw)(struct GUIBackground*, void* context);
};
struct GUIKeyboardParams {
char title[MAX_KEYBOARD_TITLE_LEN];
char result[MAX_KEYBOARD_LEN];
size_t maxLen;
bool multiline;
};
struct GUIParams {
unsigned width;
unsigned height;
@ -70,6 +85,7 @@ struct GUIParams {
int (*batteryState)(void);
void (*guiPrepare)(void);
void (*guiFinish)(void);
enum GUIKeyboardStatus (*getText)(struct GUIKeyboardParams*);
// State
struct mInputMap keyMap;
@ -87,6 +103,8 @@ void GUIPollInput(struct GUIParams* params, uint32_t* newInput, uint32_t* heldIn
enum GUICursorState GUIPollCursor(struct GUIParams* params, unsigned* x, unsigned* y);
void GUIInvalidateKeys(struct GUIParams* params);
void GUIKeyboardParamsInit(struct GUIKeyboardParams*);
CXX_GUARD_END
#endif

View File

@ -28,6 +28,7 @@ bool endswith(const char* restrict s1, const char* restrict end);
bool startswith(const char* restrict s1, const char* restrict start);
size_t toUtf8(uint32_t unichar, char* buffer);
size_t toUtf16(uint32_t unichar, uint16_t* buffer);
int utfcmp(const uint16_t* utf16, const char* utf8, size_t utf16Length, size_t utf8Length);
char* utf16to8(const uint16_t* utf16, size_t length);
uint32_t utf8Char(const char** unicode, size_t* length);

View File

@ -456,6 +456,11 @@ struct VFile* mLibraryOpenVFile(struct mLibrary* library, const struct mLibraryE
break;
}
}
for (i = 0; i < mLibraryListingSize(&entries); ++i) {
struct mLibraryEntry* e = mLibraryListingGetPointer(&entries, i);
mLibraryEntryFree(e);
}
mLibraryListingDeinit(&entries);
return vf;
}

View File

@ -5,6 +5,7 @@ set(SOURCE_FILES
video-logger.c)
set(GUI_FILES
gui/cheats.c
gui/gui-config.c
gui/gui-runner.c
gui/remap.c)

158
src/feature/gui/cheats.c Normal file
View File

@ -0,0 +1,158 @@
/* Copyright (c) 2013-2021 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gui-config.h"
#include <mgba/core/cheats.h>
#include <mgba/core/core.h>
#include "feature/gui/gui-runner.h"
#include <mgba-util/gui/menu.h>
enum mGUICheatAction {
CHEAT_ADD_LINE = 1,
CHEAT_RENAME,
CHEAT_DELETE,
};
static const char* const offOn[] = { "Off", "On" };
static void mGUIShowCheatSet(struct mGUIRunner* runner, struct mCheatDevice* device, struct mCheatSet* set) {
char cheatName[64];
snprintf(cheatName, sizeof(cheatName), "Edit cheat: %s", set->name);
struct GUIMenu menu = {
.title = cheatName,
.index = 0,
.background = &runner->background.d
};
GUIMenuItemListInit(&menu.items, 0);
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Enable",
.state = set->enabled,
.validStates = offOn,
.nStates = 2
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Add line",
.data = (void*) CHEAT_ADD_LINE,
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Rename",
.data = (void*) CHEAT_RENAME,
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Delete",
.data = (void*) CHEAT_DELETE,
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Back",
.data = 0,
};
while (true) {
struct GUIKeyboardParams keyboard;
GUIKeyboardParamsInit(&keyboard);
struct GUIMenuItem* item;
enum GUIMenuExitReason reason = GUIShowMenu(&runner->params, &menu, &item);
set->enabled = GUIMenuItemListGetPointer(&menu.items, 0)->state;
if (reason != GUI_MENU_EXIT_ACCEPT || !item->data) {
break;
}
enum mGUICheatAction action = (enum mGUICheatAction) item->data;
switch (action) {
case CHEAT_ADD_LINE:
strlcpy(keyboard.title, "Add line", sizeof(keyboard.title));
keyboard.maxLen = 12;
if (runner->params.getText(&keyboard) == GUI_KEYBOARD_DONE) {
mCheatAddLine(set, keyboard.result, 0);
}
break;
case CHEAT_RENAME:
strlcpy(keyboard.title, "Rename cheat", sizeof(keyboard.title));
strlcpy(keyboard.result, set->name, sizeof(keyboard.result));
keyboard.maxLen = 50;
if (runner->params.getText(&keyboard) == GUI_KEYBOARD_DONE) {
mCheatSetRename(set, keyboard.result);
snprintf(cheatName, sizeof(cheatName), "Edit cheat: %s", set->name);
}
break;
case CHEAT_DELETE:
mCheatRemoveSet(device, set);
break;
}
if (action == CHEAT_DELETE) {
break;
}
}
GUIMenuItemListDeinit(&menu.items);
}
void mGUIShowCheats(struct mGUIRunner* runner) {
struct mCheatDevice* device = runner->core->cheatDevice(runner->core);
if (!device) {
return;
}
struct GUIMenu menu = {
.title = "Cheats",
.index = 0,
.background = &runner->background.d
};
GUIMenuItemListInit(&menu.items, 0);
while (true) {
size_t i;
for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) {
struct mCheatSet* set = *mCheatSetsGetPointer(&device->cheats, i);
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = set->name,
.data = set,
.state = set->enabled,
.validStates = offOn,
.nStates = 2
};
}
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Add new cheat set",
.data = 0,
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Back",
.data = (void*) -1,
};
struct GUIMenuItem* item;
enum GUIMenuExitReason reason = GUIShowMenu(&runner->params, &menu, &item);
for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) {
struct mCheatSet* set = *mCheatSetsGetPointer(&device->cheats, i);
struct GUIMenuItem* item = GUIMenuItemListGetPointer(&menu.items, i);
set->enabled = item->state;
}
if (reason != GUI_MENU_EXIT_ACCEPT || item->data == (void*) -1) {
break;
}
struct mCheatSet* set = NULL;
if (!item->data) {
struct GUIKeyboardParams keyboard;
GUIKeyboardParamsInit(&keyboard);
keyboard.maxLen = 50;
strlcpy(keyboard.title, "Cheat name", sizeof(keyboard.title));
strlcpy(keyboard.result, "New cheat", sizeof(keyboard.result));
if (runner->params.getText(&keyboard) == GUI_KEYBOARD_DONE) {
set = device->createSet(device, keyboard.result);
mCheatAddSet(device, set);
}
} else {
set = item->data;
}
if (set) {
mGUIShowCheatSet(runner, device, set);
}
GUIMenuItemListClear(&menu.items);
}
GUIMenuItemListDeinit(&menu.items);
mCheatAutosave(device);
}

18
src/feature/gui/cheats.h Normal file
View File

@ -0,0 +1,18 @@
/* Copyright (c) 2013-2021 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GUI_CHEATS_H
#define GUI_CHEATS_H
#include <mgba-util/common.h>
CXX_GUARD_START
struct mGUIRunner;
void mGUIShowCheats(struct mGUIRunner* runner);
CXX_GUARD_END
#endif

View File

@ -380,4 +380,5 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
}
}
}
GUIMenuItemListDeinit(&menu.items);
}

View File

@ -8,6 +8,7 @@
#include <mgba/core/core.h>
#include <mgba/core/serialize.h>
#include "feature/gui/gui-config.h"
#include "feature/gui/cheats.h"
#include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/input.h>
#include <mgba/gba/interface.h>
@ -35,6 +36,7 @@ enum {
RUNNER_SCREENSHOT,
RUNNER_CONFIG,
RUNNER_RESET,
RUNNER_CHEATS,
RUNNER_COMMAND_MASK = 0xFFFF
};
@ -333,7 +335,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
};
GUIMenuItemListInit(&pauseMenu.items, 0);
GUIMenuItemListInit(&stateSaveMenu.items, 9);
GUIMenuItemListInit(&stateLoadMenu.items, 9);
GUIMenuItemListInit(&stateLoadMenu.items, 10);
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Unpause", .data = (void*) RUNNER_CONTINUE };
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Save state", .submenu = &stateSaveMenu };
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Load state", .submenu = &stateLoadMenu };
@ -360,6 +362,9 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 9", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(9)) };
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Take screenshot", .data = (void*) RUNNER_SCREENSHOT };
if (runner->params.getText) {
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Cheats", .data = (void*) RUNNER_CHEATS };
}
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Configure", .data = (void*) RUNNER_CONFIG };
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Reset game", .data = (void*) RUNNER_RESET };
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Exit game", .data = (void*) RUNNER_EXIT };
@ -616,6 +621,9 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
case RUNNER_CONFIG:
mGUIShowConfig(runner, runner->configExtra, runner->nConfigExtra);
break;
case RUNNER_CHEATS:
mGUIShowCheats(runner);
break;
case RUNNER_CONTINUE:
break;
}

View File

@ -84,4 +84,5 @@ void mGUIRemapKeys(struct GUIParams* params, struct mInputMap* map, const struct
// TODO: Open remap menu
}
}
GUIMenuItemListDeinit(&menu.items);
}

View File

@ -35,6 +35,9 @@ static const struct GBACartridgeOverride _overrides[] = {
{ "AC8E", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false },
{ "AC8P", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false },
// DigiCommunication Nyo - Datou! Black Gemagema Dan
{ "BDKJ", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false },
// Dragon Ball Z - The Legacy of Goku
{ "ALGP", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false },
@ -50,6 +53,7 @@ static const struct GBACartridgeOverride _overrides[] = {
// Drill Dozer
{ "V49J", SAVEDATA_SRAM, HW_RUMBLE, IDLE_LOOP_NONE, false },
{ "V49E", SAVEDATA_SRAM, HW_RUMBLE, IDLE_LOOP_NONE, false },
{ "V49P", SAVEDATA_SRAM, HW_RUMBLE, IDLE_LOOP_NONE, false },
// e-Reader
{ "PEAJ", SAVEDATA_FLASH1M, HW_EREADER, IDLE_LOOP_NONE },

View File

@ -218,7 +218,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
PREPARE_OBJWIN;
#define TEST_LAYER_ENABLED(X) \
(softwareRenderer->bg[X].enabled == 3 && \
(softwareRenderer->bg[X].enabled == 4 && \
(GBAWindowControlIsBg ## X ## Enable(softwareRenderer->currentWindow.packed) || \
(GBARegisterDISPCNTIsObjwinEnable(softwareRenderer->dispcnt) && GBAWindowControlIsBg ## X ## Enable (softwareRenderer->objwin.packed))) && \
softwareRenderer->bg[X].priority == priority)

View File

@ -699,19 +699,19 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render
softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy;
}
if (softwareRenderer->bg[0].enabled > 0 && softwareRenderer->bg[0].enabled < 3) {
if (softwareRenderer->bg[0].enabled > 0 && softwareRenderer->bg[0].enabled < 4) {
++softwareRenderer->bg[0].enabled;
DIRTY_SCANLINE(softwareRenderer, y);
}
if (softwareRenderer->bg[1].enabled > 0 && softwareRenderer->bg[1].enabled < 3) {
if (softwareRenderer->bg[1].enabled > 0 && softwareRenderer->bg[1].enabled < 4) {
++softwareRenderer->bg[1].enabled;
DIRTY_SCANLINE(softwareRenderer, y);
}
if (softwareRenderer->bg[2].enabled > 0 && softwareRenderer->bg[2].enabled < 3) {
if (softwareRenderer->bg[2].enabled > 0 && softwareRenderer->bg[2].enabled < 4) {
++softwareRenderer->bg[2].enabled;
DIRTY_SCANLINE(softwareRenderer, y);
}
if (softwareRenderer->bg[3].enabled > 0 && softwareRenderer->bg[3].enabled < 3) {
if (softwareRenderer->bg[3].enabled > 0 && softwareRenderer->bg[3].enabled < 4) {
++softwareRenderer->bg[3].enabled;
DIRTY_SCANLINE(softwareRenderer, y);
}
@ -759,16 +759,16 @@ static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* rendere
softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy;
if (softwareRenderer->bg[0].enabled > 0) {
softwareRenderer->bg[0].enabled = 3;
softwareRenderer->bg[0].enabled = 4;
}
if (softwareRenderer->bg[1].enabled > 0) {
softwareRenderer->bg[1].enabled = 3;
softwareRenderer->bg[1].enabled = 4;
}
if (softwareRenderer->bg[2].enabled > 0) {
softwareRenderer->bg[2].enabled = 3;
softwareRenderer->bg[2].enabled = 4;
}
if (softwareRenderer->bg[3].enabled > 0) {
softwareRenderer->bg[3].enabled = 3;
softwareRenderer->bg[3].enabled = 4;
}
}
@ -795,7 +795,7 @@ static void _enableBg(struct GBAVideoSoftwareRenderer* renderer, int bg, bool ac
} else if (!wasActive && active) {
if (renderer->nextY == 0 || GBARegisterDISPCNTGetMode(renderer->dispcnt) > 2) {
// TODO: Investigate in more depth how switching background works in different modes
renderer->bg[bg].enabled = 3;
renderer->bg[bg].enabled = 4;
} else {
renderer->bg[bg].enabled = 1;
}

View File

@ -182,9 +182,6 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
video->p->memory.io[REG_VCOUNT >> 1] = video->vcount;
if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS) {
if (video->frameskipCounter <= 0) {
video->renderer->drawScanline(video->renderer, video->vcount);
}
video->shouldStall = 1;
}
@ -236,6 +233,9 @@ void _startHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) {
// Begin Hblank
GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1];
dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS && video->frameskipCounter <= 0) {
video->renderer->drawScanline(video->renderer, video->vcount);
}
if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS) {
GBADMARunHblank(video->p, -cyclesLate);

View File

@ -797,6 +797,22 @@ static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* rig
}
}
static enum GUIKeyboardStatus _keyboardRun(struct GUIKeyboardParams* keyboard) {
SwkbdState swkbd;
swkbdInit(&swkbd, SWKBD_TYPE_NORMAL, 2, keyboard->maxLen);
swkbdSetInitialText(&swkbd, keyboard->result);
if (keyboard->multiline) {
swkbdSetFeatures(&swkbd, SWKBD_MULTILINE);
}
SwkbdButton button = swkbdInputText(&swkbd, keyboard->result, sizeof( keyboard->result));
if (button == SWKBD_BUTTON_CONFIRM) {
return GUI_KEYBOARD_DONE;
} else {
return GUI_KEYBOARD_CANCEL;
}
}
THREAD_ENTRY _core2Test(void* context) {
UNUSED(context);
}
@ -893,6 +909,7 @@ int main() {
_pollInput, _pollCursor,
_batteryState,
_guiPrepare, _guiFinish,
_keyboardRun,
},
.keySources = (struct GUIInputKeys[]) {
{

View File

@ -22,6 +22,7 @@ set(OS_LIB -lvita2d -l${M_LIBRARY}
-lSceCtrl_stub
-lSceDisplay_stub
-lSceGxm_stub
-lSceIme_stub
-lSceMotion_stub
-lScePgf_stub
-lScePhotoExport_stub

View File

@ -11,10 +11,12 @@
#include <mgba-util/gui/font.h>
#include <mgba-util/gui/file-select.h>
#include <mgba-util/gui/menu.h>
#include <mgba-util/string.h>
#include <psp2/apputil.h>
#include <psp2/ctrl.h>
#include <psp2/display.h>
#include <psp2/ime_dialog.h>
#include <psp2/kernel/processmgr.h>
#include <psp2/kernel/threadmgr.h>
#include <psp2/power.h>
@ -36,6 +38,7 @@ static void _drawStart(void) {
static void _drawEnd(void) {
vita2d_end_drawing();
vita2d_common_dialog_update();
vita2d_swap_buffers();
}
@ -81,6 +84,66 @@ static int _batteryState(void) {
return state | charge;
}
static enum GUIKeyboardStatus _keyboardRun(struct GUIKeyboardParams* keyboard) {
SceImeDialogParam params;
sceImeDialogParamInit(&params);
params.supportedLanguages = 0x0001FFFF;
params.languagesForced = SCE_TRUE;
params.type = SCE_IME_TYPE_DEFAULT;
params.option = 0;
if (keyboard->multiline) {
params.option = SCE_IME_OPTION_MULTILINE;
}
params.dialogMode = SCE_IME_DIALOG_DIALOG_MODE_WITH_CANCEL;
params.maxTextLength = keyboard->maxLen;
params.title = calloc(sizeof(SceWChar16), MAX_KEYBOARD_TITLE_LEN + 1);
params.inputTextBuffer = calloc(sizeof(SceWChar16), keyboard->maxLen + 1);
params.initialText = calloc(sizeof(SceWChar16), keyboard->maxLen + 1);
uint16_t* utf16Buffer = params.initialText;
char* utf8Buffer = keyboard->result;
size_t i = keyboard->maxLen;
while (i > 0 && *utf8Buffer) {
uint32_t unichar = utf8Char((const char**) &utf8Buffer, &i);
utf16Buffer += toUtf16(unichar, utf16Buffer);
}
utf16Buffer = params.title;
utf8Buffer = keyboard->title;
i = MAX_KEYBOARD_TITLE_LEN;
while (i > 0 && *utf8Buffer) {
uint32_t unichar = utf8Char((const char**) &utf8Buffer, &i);
utf16Buffer += toUtf16(unichar, utf16Buffer);
}
sceImeDialogInit(&params);
SceCommonDialogStatus status = SCE_COMMON_DIALOG_STATUS_RUNNING;
while (status == SCE_COMMON_DIALOG_STATUS_RUNNING) {
_drawStart();
status = sceImeDialogGetStatus();
_drawEnd();
}
SceImeDialogResult result;
memset(&result, 0, sizeof(SceImeDialogResult));
sceImeDialogGetResult(&result);
sceImeDialogTerm();
utf16Buffer = params.inputTextBuffer;
utf8Buffer = keyboard->result;
i = keyboard->maxLen;
while (i > 0 && *utf16Buffer) {
uint32_t unichar = utf16Char((const uint16_t**) &utf16Buffer, &i);
utf8Buffer += toUtf8(unichar, utf8Buffer);
}
utf8Buffer[0] = 0;
free(params.initialText);
free(params.inputTextBuffer);
return result.button == SCE_IME_DIALOG_BUTTON_ENTER ? GUI_KEYBOARD_DONE : GUI_KEYBOARD_CANCEL;
}
int main() {
vita2d_init();
struct GUIFont* font = GUIFontCreate();
@ -92,6 +155,7 @@ int main() {
_pollInput, _pollCursor,
_batteryState,
0, 0,
_keyboardRun,
},
.configExtra = (struct GUIMenuItem[]) {
{
@ -167,6 +231,7 @@ int main() {
sceCtrlSetSamplingModeExt(SCE_CTRL_MODE_ANALOG_WIDE);
sceSysmoduleLoadModule(SCE_SYSMODULE_PHOTO_EXPORT);
sceSysmoduleLoadModule(SCE_SYSMODULE_APPUTIL);
sceSysmoduleLoadModule(SCE_SYSMODULE_IME);
mGUIInit(&runner, "psvita");

View File

@ -161,7 +161,6 @@ void DisplayGL::stopDrawing() {
void DisplayGL::pauseDrawing() {
if (m_hasStarted) {
m_isDrawing = false;
CoreController::Interrupter interrupter(m_context);
QMetaObject::invokeMethod(m_painter.get(), "pause", Qt::BlockingQueuedConnection);
#ifndef Q_OS_MAC
setUpdatesEnabled(true);
@ -172,7 +171,6 @@ void DisplayGL::pauseDrawing() {
void DisplayGL::unpauseDrawing() {
if (m_hasStarted) {
m_isDrawing = true;
CoreController::Interrupter interrupter(m_context);
QMetaObject::invokeMethod(m_painter.get(), "unpause", Qt::BlockingQueuedConnection);
#ifndef Q_OS_MAC
setUpdatesEnabled(false);
@ -464,11 +462,6 @@ void PainterGL::draw() {
performDraw();
m_backend->swap(m_backend);
}
QMutexLocker locker(&m_mutex);
if (!m_queue.isEmpty()) {
QTimer::singleShot(1, this, &PainterGL::draw);
}
}
void PainterGL::forceDraw() {

View File

@ -146,6 +146,9 @@ void OverrideView::updateOverrides() {
gba->override.vbaBugCompat = false;
gba->vbaBugCompatSet = false;
if (gba->override.savetype != SAVEDATA_AUTODETECT) {
hasOverride = true;
}
if (!m_ui.hwAutodetect->isChecked()) {
hasOverride = true;
gba->override.hardware = HW_NONE;

View File

@ -13,29 +13,35 @@
using namespace QGBA;
LibraryEntry::LibraryEntry(mLibraryEntry* entry)
: entry(entry)
, m_fullpath(QString("%1/%2").arg(entry->base, entry->filename))
LibraryEntry::LibraryEntry(const mLibraryEntry* entry)
: base(entry->base)
, filename(entry->filename)
, fullpath(QString("%1/%2").arg(entry->base, entry->filename))
, title(entry->title)
, internalTitle(entry->internalTitle)
, internalCode(entry->internalCode)
, platform(entry->platform)
, filesize(entry->filesize)
, crc32(entry->crc32)
{
}
void AbstractGameList::addEntries(QList<LibraryEntryRef> items) {
for (LibraryEntryRef o : items) {
addEntry(o);
}
void AbstractGameList::addEntry(const LibraryEntry& item) {
addEntries({item});
}
void AbstractGameList::removeEntries(QList<LibraryEntryRef> items) {
for (LibraryEntryRef o : items) {
removeEntry(o);
}
void AbstractGameList::updateEntry(const LibraryEntry& item) {
updateEntries({item});
}
void AbstractGameList::removeEntry(const QString& item) {
removeEntries({item});
}
LibraryController::LibraryController(QWidget* parent, const QString& path, ConfigController* config)
: QStackedWidget(parent)
, m_config(config)
{
mLibraryListingInit(&m_listing, 0);
if (!path.isNull()) {
// This can return NULL if the library is already open
m_library = std::shared_ptr<mLibrary>(mLibraryLoad(path.toUtf8().constData()), mLibraryDestroy);
@ -58,8 +64,6 @@ LibraryController::LibraryController(QWidget* parent, const QString& path, Confi
}
LibraryController::~LibraryController() {
freeLibrary();
mLibraryListingDeinit(&m_listing);
}
void LibraryController::setViewStyle(LibraryStyle newStyle) {
@ -74,45 +78,53 @@ void LibraryController::setViewStyle(LibraryStyle newStyle) {
} else {
newCurrentList = m_libraryGrid.get();
}
newCurrentList->selectEntry(selectedEntry());
newCurrentList->selectEntry(selectedEntry().fullpath);
newCurrentList->setViewStyle(newStyle);
setCurrentWidget(newCurrentList->widget());
m_currentList = newCurrentList;
}
void LibraryController::selectEntry(LibraryEntryRef entry) {
void LibraryController::selectEntry(const QString& fullpath) {
if (!m_currentList) {
return;
}
m_currentList->selectEntry(entry);
m_currentList->selectEntry(fullpath);
}
LibraryEntryRef LibraryController::selectedEntry() {
LibraryEntry LibraryController::selectedEntry() {
if (!m_currentList) {
return LibraryEntryRef();
return {};
}
return m_currentList->selectedEntry();
return m_entries.value(m_currentList->selectedEntry());
}
VFile* LibraryController::selectedVFile() {
LibraryEntryRef entry = selectedEntry();
if (entry) {
return mLibraryOpenVFile(m_library.get(), entry->entry);
LibraryEntry entry = selectedEntry();
if (!entry.isNull()) {
mLibraryEntry libentry = {0};
QByteArray baseUtf8(entry.base.toUtf8());
QByteArray filenameUtf8(entry.filename.toUtf8());
libentry.base = baseUtf8.constData();
libentry.filename = filenameUtf8.constData();
return mLibraryOpenVFile(m_library.get(), &libentry);
} else {
return nullptr;
}
}
QPair<QString, QString> LibraryController::selectedPath() {
LibraryEntryRef e = selectedEntry();
return e ? qMakePair(e->base(), e->filename()) : qMakePair<QString, QString>("", "");
LibraryEntry entry = selectedEntry();
if (!entry.isNull()) {
return qMakePair(QString(entry.base), QString(entry.filename));
} else {
return qMakePair(QString(), QString());
}
}
void LibraryController::addDirectory(const QString& dir, bool recursive) {
// The worker thread temporarily owns the library
std::shared_ptr<mLibrary> library = m_library;
m_libraryJob = GBAApp::app()->submitWorkerJob(std::bind(&LibraryController::loadDirectory, this, dir, recursive), this, [this, library]() {
m_libraryJob = -1;
refresh();
});
}
@ -133,38 +145,47 @@ void LibraryController::refresh() {
setDisabled(true);
QStringList allEntries;
QList<LibraryEntryRef> newEntries;
QHash<QString, LibraryEntry> removedEntries = m_entries;
QHash<QString, LibraryEntry> updatedEntries;
QList<LibraryEntry> newEntries;
freeLibrary();
mLibraryGetEntries(m_library.get(), &m_listing, 0, 0, nullptr);
for (size_t i = 0; i < mLibraryListingSize(&m_listing); i++) {
mLibraryEntry* entry = mLibraryListingGetPointer(&m_listing, i);
QString fullpath = QString("%1/%2").arg(entry->base, entry->filename);
if (m_entries.contains(fullpath)) {
m_entries.value(fullpath)->entry = entry;
mLibraryListing listing;
mLibraryListingInit(&listing, 0);
mLibraryGetEntries(m_library.get(), &listing, 0, 0, nullptr);
for (size_t i = 0; i < mLibraryListingSize(&listing); i++) {
LibraryEntry entry = mLibraryListingGetConstPointer(&listing, i);
if (!m_entries.contains(entry.fullpath)) {
newEntries.append(entry);
} else {
LibraryEntryRef libentry = std::make_shared<LibraryEntry>(entry);
m_entries.insert(fullpath, libentry);
newEntries.append(libentry);
updatedEntries[entry.fullpath] = entry;
}
allEntries.append(fullpath);
m_entries[entry.fullpath] = entry;
removedEntries.remove(entry.fullpath);
}
// Check for entries that were removed
QList<LibraryEntryRef> removedEntries;
for (QString& path : m_entries.keys()) {
if (!allEntries.contains(path)) {
removedEntries.append(m_entries.value(path));
m_entries.remove(path);
}
for (QString& path : removedEntries.keys()) {
m_entries.remove(path);
}
m_libraryTree->addEntries(newEntries);
m_libraryGrid->addEntries(newEntries);
if (!removedEntries.size() && !newEntries.size()) {
m_libraryTree->updateEntries(updatedEntries.values());
m_libraryGrid->updateEntries(updatedEntries.values());
} else if (!updatedEntries.size()) {
m_libraryTree->removeEntries(removedEntries.keys());
m_libraryGrid->removeEntries(removedEntries.keys());
m_libraryTree->removeEntries(removedEntries);
m_libraryGrid->removeEntries(removedEntries);
m_libraryTree->addEntries(newEntries);
m_libraryGrid->addEntries(newEntries);
} else {
m_libraryTree->resetEntries(m_entries.values());
m_libraryGrid->resetEntries(m_entries.values());
}
for (size_t i = 0; i < mLibraryListingSize(&listing); ++i) {
mLibraryEntryFree(mLibraryListingGetPointer(&listing, i));
}
mLibraryListingDeinit(&listing);
setDisabled(false);
selectLastBootedGame();
@ -177,19 +198,14 @@ void LibraryController::selectLastBootedGame() {
}
const QString lastfile = m_config->getMRU().first();
if (m_entries.contains(lastfile)) {
selectEntry(m_entries.value(lastfile));
selectEntry(lastfile);
}
}
void LibraryController::loadDirectory(const QString& dir, bool recursive) {
// This class can get deleted during this function (sigh) so we need to hold onto this
std::shared_ptr<mLibrary> library = m_library;
qint64 libraryJob = m_libraryJob;
mLibraryLoadDirectory(library.get(), dir.toUtf8().constData(), recursive);
}
void LibraryController::freeLibrary() {
for (size_t i = 0; i < mLibraryListingSize(&m_listing); ++i) {
mLibraryEntryFree(mLibraryListingGetPointer(&m_listing, i));
}
mLibraryListingClear(&m_listing);
m_libraryJob.testAndSetOrdered(libraryJob, -1);
}

View File

@ -1,5 +1,5 @@
/* Copyright (c) 2014-2017 waddlesplash
* Copyright (c) 2014-2020 Jeffrey Pfau
* Copyright (c) 2013-2021 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
@ -8,8 +8,9 @@
#include <memory>
#include <QAtomicInteger>
#include <QHash>
#include <QList>
#include <QMap>
#include <QStackedWidget>
#include <mgba/core/library.h>
@ -28,40 +29,42 @@ enum class LibraryStyle {
STYLE_ICON
};
class LibraryEntry final {
public:
LibraryEntry(mLibraryEntry* entry);
struct LibraryEntry {
LibraryEntry() {}
LibraryEntry(const mLibraryEntry* entry);
QString displayTitle() const { return title().isNull() ? filename() : title(); }
bool isNull() const { return fullpath.isNull(); }
QString base() const { return QString(entry->base); }
QString filename() const { return QString(entry->filename); }
QString fullpath() const { return m_fullpath; }
QString title() const { return QString(entry->title); }
QByteArray internalTitle() const { return QByteArray(entry->internalTitle); }
QByteArray internalCode() const { return QByteArray(entry->internalCode); }
mPlatform platform() const { return entry->platform; }
size_t filesize() const { return entry->filesize; }
uint32_t crc32() const { return entry->crc32; }
QString displayTitle() const { return title.isNull() ? filename : title; }
const mLibraryEntry* entry;
private:
const QString m_fullpath;
QString base;
QString filename;
QString fullpath;
QString title;
QByteArray internalTitle;
QByteArray internalCode;
mPlatform platform;
size_t filesize;
uint32_t crc32;
bool operator==(const LibraryEntry& other) const { return other.fullpath == fullpath; }
};
typedef std::shared_ptr<LibraryEntry> LibraryEntryRef;
class AbstractGameList {
public:
virtual LibraryEntryRef selectedEntry() = 0;
virtual void selectEntry(LibraryEntryRef game) = 0;
virtual QString selectedEntry() = 0;
virtual void selectEntry(const QString& fullpath) = 0;
virtual void setViewStyle(LibraryStyle newStyle) = 0;
virtual void addEntry(LibraryEntryRef item) = 0;
virtual void addEntries(QList<LibraryEntryRef> items);
virtual void resetEntries(const QList<LibraryEntry>&) = 0;
virtual void addEntries(const QList<LibraryEntry>&) = 0;
virtual void updateEntries(const QList<LibraryEntry>&) = 0;
virtual void removeEntries(const QList<QString>&) = 0;
virtual void removeEntry(LibraryEntryRef item) = 0;
virtual void removeEntries(QList<LibraryEntryRef> items);
virtual void addEntry(const LibraryEntry&);
virtual void updateEntry(const LibraryEntry&);
virtual void removeEntry(const QString&);
virtual QWidget* widget() = 0;
};
@ -77,8 +80,8 @@ public:
LibraryStyle viewStyle() const { return m_currentStyle; }
void setViewStyle(LibraryStyle newStyle);
void selectEntry(LibraryEntryRef entry);
LibraryEntryRef selectedEntry();
void selectEntry(const QString& fullpath);
LibraryEntry selectedEntry();
VFile* selectedVFile();
QPair<QString, QString> selectedPath();
@ -98,13 +101,11 @@ private slots:
private:
void loadDirectory(const QString&, bool recursive = true); // Called on separate thread
void freeLibrary();
ConfigController* m_config = nullptr;
std::shared_ptr<mLibrary> m_library;
qint64 m_libraryJob = -1;
mLibraryListing m_listing;
QMap<QString, LibraryEntryRef> m_entries;
QAtomicInteger<qint64> m_libraryJob = -1;
QHash<QString, LibraryEntry> m_entries;
LibraryStyle m_currentStyle;
AbstractGameList* m_currentList = nullptr;

View File

@ -1,4 +1,5 @@
/* Copyright (c) 2014-2017 waddlesplash
* Copyright (c) 2013-2021 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
@ -23,22 +24,16 @@ LibraryGrid::~LibraryGrid() {
delete m_widget;
}
LibraryEntryRef LibraryGrid::selectedEntry() {
QString LibraryGrid::selectedEntry() {
if (!m_widget->selectedItems().empty()) {
return m_items.key(m_widget->selectedItems().at(0));
} else {
return LibraryEntryRef();
return {};
}
}
void LibraryGrid::selectEntry(LibraryEntryRef game) {
if (!game) {
return;
}
if (!m_widget->selectedItems().empty()) {
m_widget->selectedItems().at(0)->setSelected(false);
}
m_items.value(game)->setSelected(true);
void LibraryGrid::selectEntry(const QString& game) {
m_widget->setCurrentItem(m_items.value(game));
}
void LibraryGrid::setViewStyle(LibraryStyle newStyle) {
@ -47,33 +42,62 @@ void LibraryGrid::setViewStyle(LibraryStyle newStyle) {
m_widget->setIconSize(QSize(GRID_BANNER_WIDTH, GRID_BANNER_HEIGHT));
m_widget->setViewMode(QListView::IconMode);
} else {
m_currentStyle = LibraryStyle::STYLE_ICON;
m_widget->setIconSize(QSize(ICON_BANNER_WIDTH, ICON_BANNER_HEIGHT));
m_widget->setViewMode(QListView::ListMode);
}
m_currentStyle = newStyle;
// QListView resets this when you change the view mode, so let's set it again
m_widget->setDragEnabled(false);
}
void LibraryGrid::addEntry(LibraryEntryRef item) {
if (m_items.contains(item)) {
void LibraryGrid::resetEntries(const QList<LibraryEntry>& items) {
m_widget->clear();
m_items.clear();
addEntries(items);
}
void LibraryGrid::addEntries(const QList<LibraryEntry>& items) {
for (const auto& item : items) {
addEntry(item);
}
}
void LibraryGrid::addEntry(const LibraryEntry& item) {
if (m_items.contains(item.fullpath)) {
return;
}
QListWidgetItem* i = new QListWidgetItem;
i->setText(item->displayTitle());
i->setText(item.displayTitle());
m_widget->addItem(i);
m_items.insert(item, i);
m_items.insert(item.fullpath, i);
}
void LibraryGrid::removeEntry(LibraryEntryRef entry) {
if (!m_items.contains(entry)) {
void LibraryGrid::updateEntries(const QList<LibraryEntry>& items) {
for (const auto& item : items) {
updateEntry(item);
}
}
void LibraryGrid::updateEntry(const LibraryEntry& item) {
QListWidgetItem* i = m_items.value(item.fullpath);
i->setText(item.displayTitle());
}
void LibraryGrid::removeEntries(const QList<QString>& items) {
for (const auto& item : items) {
removeEntry(item);
}
}
void LibraryGrid::removeEntry(const QString& item) {
if (!m_items.contains(item)) {
return;
}
delete m_items.take(entry);
delete m_items.take(item);
}
}

View File

@ -16,16 +16,21 @@ public:
explicit LibraryGrid(LibraryController* parent = nullptr);
~LibraryGrid();
// AbstractGameList stuff
virtual LibraryEntryRef selectedEntry() override;
virtual void selectEntry(LibraryEntryRef game) override;
QString selectedEntry() override;
void selectEntry(const QString& fullpath) override;
virtual void setViewStyle(LibraryStyle newStyle) override;
void setViewStyle(LibraryStyle newStyle) override;
virtual void addEntry(LibraryEntryRef item) override;
virtual void removeEntry(LibraryEntryRef entry) override;
void resetEntries(const QList<LibraryEntry>& items) override;
void addEntries(const QList<LibraryEntry>& items) override;
void updateEntries(const QList<LibraryEntry>& items) override;
void removeEntries(const QList<QString>& items) override;
virtual QWidget* widget() override { return m_widget; }
void addEntry(const LibraryEntry& items) override;
void updateEntry(const LibraryEntry& items) override;
void removeEntry(const QString& items) override;
QWidget* widget() override { return m_widget; }
signals:
void startGame();
@ -40,7 +45,7 @@ private:
const quint32 ICON_BANNER_WIDTH = 64;
const quint32 ICON_BANNER_HEIGHT = 64;
QMap<LibraryEntryRef, QListWidgetItem*> m_items;
QHash<QString, QListWidgetItem*> m_items;
LibraryStyle m_currentStyle;
};

View File

@ -10,11 +10,13 @@
#include <QApplication>
#include <QDir>
using namespace QGBA;
namespace QGBA {
class TreeWidgetItem : public QTreeWidgetItem {
class LibraryTreeItem : public QTreeWidgetItem {
public:
TreeWidgetItem(QTreeWidget* parent = nullptr) : QTreeWidgetItem(parent) {}
LibraryTreeItem(QTreeWidget* parent = nullptr) : QTreeWidgetItem(parent) {}
void setFilesize(size_t size);
virtual bool operator<(const QTreeWidgetItem& other) const override;
@ -22,15 +24,17 @@ protected:
size_t m_size = 0;
};
void TreeWidgetItem::setFilesize(size_t size) {
}
void LibraryTreeItem::setFilesize(size_t size) {
m_size = size;
setText(LibraryTree::COL_SIZE, niceSizeFormat(size));
}
bool TreeWidgetItem::operator<(const QTreeWidgetItem& other) const {
bool LibraryTreeItem::operator<(const QTreeWidgetItem& other) const {
const int column = treeWidget()->sortColumn();
return ((column == LibraryTree::COL_SIZE) ?
m_size < dynamic_cast<const TreeWidgetItem*>(&other)->m_size :
m_size < dynamic_cast<const LibraryTreeItem*>(&other)->m_size :
QTreeWidgetItem::operator<(other));
}
@ -56,16 +60,14 @@ LibraryTree::LibraryTree(LibraryController* parent)
m_widget->sortByColumn(COL_NAME, Qt::AscendingOrder);
QObject::connect(m_widget, &QTreeWidget::itemActivated, [this](QTreeWidgetItem* item, int) -> void {
if (!m_pathNodes.values().contains(item)) {
if (m_items.values().contains(item)) {
emit m_controller->startGame();
}
});
}
LibraryTree::~LibraryTree() {
for (QTreeWidgetItem* i : m_items.values()) {
delete i;
}
m_widget->clear();
}
void LibraryTree::resizeAllCols() {
@ -74,75 +76,97 @@ void LibraryTree::resizeAllCols() {
}
}
LibraryEntryRef LibraryTree::selectedEntry() {
QString LibraryTree::selectedEntry() {
if (!m_widget->selectedItems().empty()) {
return m_items.key(m_widget->selectedItems().at(0));
} else {
return LibraryEntryRef();
return {};
}
}
void LibraryTree::selectEntry(LibraryEntryRef game) {
if (!game) {
void LibraryTree::selectEntry(const QString& game) {
if (game.isNull()) {
return;
}
if (!m_widget->selectedItems().empty()) {
m_widget->selectedItems().at(0)->setSelected(false);
}
m_items.value(game)->setSelected(true);
m_widget->setCurrentItem(m_items.value(game));
}
void LibraryTree::setViewStyle(LibraryStyle newStyle) {
if (newStyle == LibraryStyle::STYLE_LIST) {
m_currentStyle = LibraryStyle::STYLE_LIST;
m_widget->setIndentation(0);
rebuildTree();
} else {
m_currentStyle = LibraryStyle::STYLE_TREE;
m_widget->setIndentation(20);
rebuildTree();
}
m_currentStyle = newStyle;
rebuildTree();
}
void LibraryTree::addEntries(QList<LibraryEntryRef> items) {
void LibraryTree::resetEntries(const QList<LibraryEntry>& items) {
m_deferredTreeRebuild = true;
AbstractGameList::addEntries(items);
m_entries.clear();
m_pathNodes.clear();
addEntries(items);
}
void LibraryTree::addEntries(const QList<LibraryEntry>& items) {
m_deferredTreeRebuild = true;
for (const auto& item : items) {
addEntry(item);
}
m_deferredTreeRebuild = false;
rebuildTree();
}
void LibraryTree::addEntry(LibraryEntryRef item) {
if (m_items.contains(item)) {
return;
}
void LibraryTree::addEntry(const LibraryEntry& item) {
m_entries[item.fullpath] = item;
QString folder = item->base();
QString folder = item.base;
if (!m_pathNodes.contains(folder)) {
QTreeWidgetItem* i = new TreeWidgetItem;
i->setText(0, folder.section("/", -1));
m_pathNodes.insert(folder, i);
if (m_currentStyle == LibraryStyle::STYLE_TREE) {
m_widget->addTopLevelItem(i);
}
m_pathNodes.insert(folder, 1);
} else {
++m_pathNodes[folder];
}
TreeWidgetItem* i = new TreeWidgetItem;
i->setText(COL_NAME, item->displayTitle());
i->setText(COL_LOCATION, QDir::toNativeSeparators(item->base()));
i->setText(COL_PLATFORM, nicePlatformFormat(item->platform()));
i->setFilesize(item->filesize());
i->setTextAlignment(COL_SIZE, Qt::AlignRight);
i->setText(COL_CRC32, QString("%0").arg(item->crc32(), 8, 16, QChar('0')));
m_items.insert(item, i);
rebuildTree();
}
void LibraryTree::removeEntry(LibraryEntryRef item) {
if (!m_items.contains(item)) {
void LibraryTree::updateEntries(const QList<LibraryEntry>& items) {
for (const auto& item : items) {
updateEntry(item);
}
}
void LibraryTree::updateEntry(const LibraryEntry& item) {
m_entries[item.fullpath] = item;
LibraryTreeItem* i = static_cast<LibraryTreeItem*>(m_items.value(item.fullpath));
i->setText(COL_NAME, item.displayTitle());
i->setText(COL_PLATFORM, nicePlatformFormat(item.platform));
i->setFilesize(item.filesize);
i->setText(COL_CRC32, QString("%0").arg(item.crc32, 8, 16, QChar('0')));
}
void LibraryTree::removeEntries(const QList<QString>& items) {
m_deferredTreeRebuild = true;
for (const auto& item : items) {
removeEntry(item);
}
m_deferredTreeRebuild = false;
rebuildTree();
}
void LibraryTree::removeEntry(const QString& item) {
if (!m_entries.contains(item)) {
return;
}
delete m_items.take(item);
QString folder = m_entries.value(item).base;
--m_pathNodes[folder];
if (m_pathNodes[folder] <= 0) {
m_pathNodes.remove(folder);
}
m_entries.remove(item);
rebuildTree();
}
void LibraryTree::rebuildTree() {
@ -150,26 +174,33 @@ void LibraryTree::rebuildTree() {
return;
}
LibraryEntryRef currentGame = selectedEntry();
int count = m_widget->topLevelItemCount();
for (int a = count - 1; a >= 0; --a) {
m_widget->takeTopLevelItem(a);
}
for (QTreeWidgetItem* i : m_pathNodes.values()) {
i->takeChildren();
}
QString currentGame = selectedEntry();
m_widget->clear();
m_items.clear();
QHash<QString, QTreeWidgetItem*> pathNodes;
if (m_currentStyle == LibraryStyle::STYLE_TREE) {
for (QTreeWidgetItem* i : m_pathNodes.values()) {
for (const QString& folder : m_pathNodes.keys()) {
QTreeWidgetItem* i = new LibraryTreeItem;
pathNodes.insert(folder, i);
i->setText(0, folder.section("/", -1));
m_widget->addTopLevelItem(i);
}
for (QTreeWidgetItem* i : m_items.values()) {
m_pathNodes.value(m_items.key(i)->base())->addChild(i);
}
} else {
for (QTreeWidgetItem* i : m_items.values()) {
}
for (const auto& item : m_entries.values()) {
LibraryTreeItem* i = new LibraryTreeItem;
i->setText(COL_NAME, item.displayTitle());
i->setText(COL_LOCATION, QDir::toNativeSeparators(item.base));
i->setText(COL_PLATFORM, nicePlatformFormat(item.platform));
i->setFilesize(item.filesize);
i->setTextAlignment(COL_SIZE, Qt::AlignRight);
i->setText(COL_CRC32, QString("%0").arg(item.crc32, 8, 16, QChar('0')));
m_items.insert(item.fullpath, i);
if (m_currentStyle == LibraryStyle::STYLE_TREE) {
pathNodes.value(item.base)->addChild(i);
} else {
m_widget->addTopLevelItem(i);
}
}
@ -178,5 +209,3 @@ void LibraryTree::rebuildTree() {
resizeAllCols();
selectEntry(currentGame);
}
}

View File

@ -11,6 +11,8 @@
namespace QGBA {
class LibraryTreeItem;
class LibraryTree final : public AbstractGameList {
public:
@ -25,17 +27,21 @@ public:
explicit LibraryTree(LibraryController* parent = nullptr);
~LibraryTree();
// AbstractGameList stuff
virtual LibraryEntryRef selectedEntry() override;
virtual void selectEntry(LibraryEntryRef game) override;
QString selectedEntry() override;
void selectEntry(const QString& fullpath) override;
virtual void setViewStyle(LibraryStyle newStyle) override;
void setViewStyle(LibraryStyle newStyle) override;
virtual void addEntries(QList<LibraryEntryRef> items) override;
virtual void addEntry(LibraryEntryRef item) override;
virtual void removeEntry(LibraryEntryRef item) override;
void resetEntries(const QList<LibraryEntry>& items) override;
void addEntries(const QList<LibraryEntry>& items) override;
void updateEntries(const QList<LibraryEntry>& items) override;
void removeEntries(const QList<QString>& items) override;
virtual QWidget* widget() override { return m_widget; }
void addEntry(const LibraryEntry& items) override;
void updateEntry(const LibraryEntry& items) override;
void removeEntry(const QString& items) override;
QWidget* widget() override { return m_widget; }
private:
QTreeWidget* m_widget;
@ -44,8 +50,9 @@ private:
LibraryController* m_controller;
bool m_deferredTreeRebuild = false;
QMap<LibraryEntryRef, QTreeWidgetItem*> m_items;
QMap<QString, QTreeWidgetItem*> m_pathNodes;
QHash<QString, LibraryEntry> m_entries;
QHash<QString, QTreeWidgetItem*> m_items;
QHash<QString, int> m_pathNodes;
void rebuildTree();
void resizeAllCols();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -124,7 +124,7 @@ static enum ScreenMode {
SM_MAX
} screenMode = SM_PA;
static bool initEgl() {
static bool eglInit() {
s_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (!s_display) {
goto _fail0;
@ -173,7 +173,7 @@ _fail0:
return false;
}
static void deinitEgl() {
static void eglDeinit() {
if (s_display) {
eglMakeCurrent(s_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (s_context) {
@ -186,7 +186,7 @@ static void deinitEgl() {
}
}
static void _mapKey(struct mInputMap* map, uint32_t binding, int nativeKey, enum GBAKey key) {
static void _mapKey(struct mInputMap* map, uint32_t binding, int nativeKey, int key) {
mInputBindKey(map, binding, __builtin_ctz(nativeKey), key);
}
@ -674,29 +674,25 @@ static void _guiFinish(void) {
GUIFontDrawSubmit(font);
}
int main(int argc, char* argv[]) {
NWindow* window = nwindowGetDefault();
nwindowSetDimensions(window, 1920, 1080);
static enum GUIKeyboardStatus _keyboardRun(struct GUIKeyboardParams* keyboard) {
SwkbdConfig swkbd;
swkbdCreate(&swkbd, 0);
swkbdConfigMakePresetDefault(&swkbd);
swkbdConfigSetStringLenMax(&swkbd, keyboard->maxLen);
swkbdConfigSetInitialText(&swkbd, keyboard->result);
swkbdConfigSetHeaderText(&swkbd, keyboard->title);
swkbdConfigSetReturnButtonFlag(&swkbd, keyboard->multiline);
socketInitializeDefault();
nxlinkStdio();
initEgl();
romfsInit();
audoutInitialize();
psmInitialize();
font = GUIFontCreate();
vmode = appletGetOperationMode();
if (vmode == AppletOperationMode_Console) {
vwidth = 1920;
vheight = 1080;
Result rc = swkbdShow(&swkbd, keyboard->result, sizeof(keyboard->result));
swkbdClose(&swkbd);
if (R_SUCCEEDED(rc)) {
return GUI_KEYBOARD_DONE;
} else {
vwidth = 1280;
vheight = 720;
return GUI_KEYBOARD_CANCEL;
}
nwindowSetCrop(window, 0, 0, vwidth, vheight);
}
static void glInit(void) {
glViewport(0, 1080 - vheight, vwidth, vheight);
glClearColor(0.f, 0.f, 0.f, 1.f);
@ -783,7 +779,23 @@ int main(int argc, char* argv[]) {
glVertexAttribPointer(offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(offsetLocation);
glBindVertexArray(0);
}
static void glDeinit(void) {
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glDeleteBuffers(1, &pbo);
glDeleteFramebuffers(1, &copyFbo);
glDeleteTextures(1, &tex);
glDeleteTextures(1, &oldTex);
glDeleteBuffers(1, &vbo);
glDeleteProgram(program);
glDeleteVertexArrays(1, &vao);
}
static void hidSetup(void) {
hidInitializeTouchScreen();
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
padInitializeDefault(&pad);
@ -808,6 +820,40 @@ int main(int argc, char* argv[]) {
lightSensor.d.readLuminance = _lightSensorRead;
lightSensor.d.sample = _lightSensorSample;
}
static void hidTeardown(void) {
hidStopSixAxisSensor(sixaxisHandles[0]);
hidStopSixAxisSensor(sixaxisHandles[1]);
hidStopSixAxisSensor(sixaxisHandles[2]);
hidStopSixAxisSensor(sixaxisHandles[3]);
}
int main(int argc, char* argv[]) {
NWindow* window = nwindowGetDefault();
nwindowSetDimensions(window, 1920, 1080);
socketInitializeDefault();
nxlinkStdio();
eglInit();
romfsInit();
audoutInitialize();
psmInitialize();
font = GUIFontCreate();
vmode = appletGetOperationMode();
if (vmode == AppletOperationMode_Console) {
vwidth = 1920;
vheight = 1080;
} else {
vwidth = 1280;
vheight = 720;
}
nwindowSetCrop(window, 0, 0, vwidth, vheight);
glInit();
hidSetup();
stream.videoDimensionsChanged = NULL;
stream.postVideoFrame = NULL;
@ -837,6 +883,7 @@ int main(int argc, char* argv[]) {
_pollInput, _pollCursor,
_batteryState,
_guiPrepare, _guiFinish,
_keyboardRun,
},
.keySources = (struct GUIInputKeys[]) {
{
@ -1028,27 +1075,13 @@ int main(int argc, char* argv[]) {
audoutStopAudioOut();
GUIFontDestroy(font);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glDeleteBuffers(1, &pbo);
glDeleteFramebuffers(1, &copyFbo);
glDeleteTextures(1, &tex);
glDeleteTextures(1, &oldTex);
glDeleteBuffers(1, &vbo);
glDeleteProgram(program);
glDeleteVertexArrays(1, &vao);
hidStopSixAxisSensor(sixaxisHandles[0]);
hidStopSixAxisSensor(sixaxisHandles[1]);
hidStopSixAxisSensor(sixaxisHandles[2]);
hidStopSixAxisSensor(sixaxisHandles[3]);
glDeinit();
hidTeardown();
psmExit();
audoutExit();
romfsExit();
deinitEgl();
eglDeinit();
socketExit();
return 0;
}

View File

@ -72,3 +72,10 @@ void GUIInvalidateKeys(struct GUIParams* params) {
params->inputHistory[i] = 0;
}
}
void GUIKeyboardParamsInit(struct GUIKeyboardParams* keyboard) {
memset(keyboard->title, 0, sizeof(keyboard->title));
memset(keyboard->result, 0, sizeof(keyboard->result));
keyboard->maxLen = sizeof(keyboard->result);
keyboard->multiline = false;
}

View File

@ -16,6 +16,34 @@
DEFINE_VECTOR(GUIMenuItemList, struct GUIMenuItem);
void _itemNext(struct GUIMenuItem* item, bool wrap) {
if (item->state < item->nStates - 1) {
unsigned oldState = item->state;
do {
++item->state;
} while (!item->validStates[item->state] && item->state < item->nStates - 1);
if (!item->validStates[item->state]) {
item->state = oldState;
}
} else if (wrap) {
item->state = 0;
}
}
void _itemPrev(struct GUIMenuItem* item, bool wrap) {
if (item->state > 0) {
unsigned oldState = item->state;
do {
--item->state;
} while (!item->validStates[item->state] && item->state > 0);
if (!item->validStates[item->state]) {
item->state = oldState;
}
} else if (wrap) {
item->state = item->nStates - 1;
}
}
enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* menu, struct GUIMenuItem** item) {
size_t start = 0;
size_t lineHeight = GUIFontHeight(params->font);
@ -52,15 +80,7 @@ enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* men
if (newInput & (1 << GUI_INPUT_LEFT)) {
struct GUIMenuItem* item = GUIMenuItemListGetPointer(&menu->items, menu->index);
if (item->validStates) {
if (item->state > 0) {
unsigned oldState = item->state;
do {
--item->state;
} while (!item->validStates[item->state] && item->state > 0);
if (!item->validStates[item->state]) {
item->state = oldState;
}
}
_itemPrev(item, false);
} else if (menu->index >= pageSize) {
menu->index -= pageSize;
} else {
@ -70,15 +90,7 @@ enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* men
if (newInput & (1 << GUI_INPUT_RIGHT)) {
struct GUIMenuItem* item = GUIMenuItemListGetPointer(&menu->items, menu->index);
if (item->validStates) {
if (item->state < item->nStates - 1) {
unsigned oldState = item->state;
do {
++item->state;
} while (!item->validStates[item->state] && item->state < item->nStates - 1);
if (!item->validStates[item->state]) {
item->state = oldState;
}
}
_itemNext(item, false);
} else if (menu->index + pageSize < GUIMenuItemListSize(&menu->items)) {
menu->index += pageSize;
} else {
@ -125,6 +137,8 @@ enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* men
if (reason != GUI_MENU_EXIT_BACK) {
return reason;
}
} else if ((*item)->validStates && !(*item)->data) {
_itemNext(*item, true);
} else {
return GUI_MENU_EXIT_ACCEPT;
}

View File

@ -180,6 +180,30 @@ size_t toUtf8(uint32_t unichar, char* buffer) {
return 0;
}
size_t toUtf16(uint32_t unichar, uint16_t* buffer) {
if (unichar < 0xD800) {
buffer[0] = unichar;
return 1;
}
if (unichar < 0xE000) {
// Orphan surrogate, invalid
return 0;
}
if (unichar < 0x10000) {
buffer[0] = unichar;
return 1;
}
if (unichar < 0x110000) {
unichar -= 0x10000;
buffer[0] = 0xD800 | (unichar >> 10);
buffer[1] = 0xDC00 | (unichar & 0x3FF);
return 2;
}
// Invalid code point
return 0;
}
int utfcmp(const uint16_t* utf16, const char* utf8, size_t utf16Length, size_t utf8Length) {
uint32_t char1 = 0, char2 = 0;
while (utf16Length > 0 && utf8Length > 0) {
@ -548,4 +572,4 @@ bool wildcard(const char* search, const char* string) {
}
}
return false;
}
}