Merge branch 'master' (early part) into medusa
10
CHANGES
|
@ -49,6 +49,7 @@ Features:
|
|||
- ARM disassembler now resolves addresses to symbol names
|
||||
- Add Game Boy Player feature support to ports
|
||||
- Individual window types can now be toggled in debugging views
|
||||
- Support for the Wii U GamePad when running as an injected VC title
|
||||
Emulation fixes:
|
||||
- ARM: Fix ALU reading PC after shifting
|
||||
- ARM: Fix STR storing PC after address calculation
|
||||
|
@ -88,14 +89,17 @@ Other fixes:
|
|||
- 3DS: Fix thread cleanup
|
||||
- All: Improve export headers (fixes mgba.io/i/1738)
|
||||
- CMake: Fix build with downstream minizip that exports incompatible symbols
|
||||
- CMake: Link with correct OpenGL library (fixes mgba.io/i/1872)
|
||||
- Core: Ensure ELF regions can be written before trying
|
||||
- Core: Fix threading improperly setting paused state while interrupted
|
||||
- Core: Fix loading ELF files that have unexpected empty program headers
|
||||
- Debugger: Don't skip undefined instructions when debugger attached
|
||||
- Debugger: Close trace log when done tracing
|
||||
- Debugger: Fix change watchpoints (fixes mgba.io/i/1947)
|
||||
- FFmpeg: Fix some small memory leaks
|
||||
- FFmpeg: Fix encoding of time base
|
||||
- GB Video: Fix SGB video logs
|
||||
- GBA: Fix loading multiboot ELF files (fixes mgba.io/i/1949)
|
||||
- mGUI: Don't attempt to preload files larger than can fit in RAM
|
||||
- Qt: Force OpenGL paint engine creation thread (fixes mgba.io/i/1642)
|
||||
- Qt: Fix static compilation in MinGW (fixes mgba.io/i/1769)
|
||||
|
@ -105,22 +109,27 @@ Other fixes:
|
|||
- Qt: Fix game display sometimes disappearing after closing load/save state screen
|
||||
- Qt: Fix cancelling pausing before the frame ends
|
||||
- Qt: Fix gamepad event dispatching (fixes mgba.io/i/1922)
|
||||
- Qt: Pre-attach GDB stub when launching with -g (fixes mgba.io/i/1950)
|
||||
- SM83: Simplify register pair access on big endian
|
||||
- SM83: Disassemble STOP as one byte
|
||||
- Wii: Fix crash on unloading irregularly sized GBA ROMs
|
||||
Misc:
|
||||
- 3DS: Use "wide mode" where applicable for slightly better filtering
|
||||
- 3DS: Batch directory reads
|
||||
- Core: Add savedataUpdated callback
|
||||
- Core: Add shutdown callback
|
||||
- Core: Rework thread state synchronization
|
||||
- GB: Allow pausing event loop while CPU is blocked
|
||||
- GB: Add support for sleep and shutdown callbacks
|
||||
- GB I/O: Implement preliminary support for PCM12/PCM34 (closes mgba.io/i/1468)
|
||||
- GBA: Allow pausing event loop while CPU is blocked
|
||||
- GBA BIOS: Division by zero should emit a FATAL error
|
||||
- GBA Video: Convert OpenGL VRAM texture to integer
|
||||
- GBA Video: Skip attempting to render offscreen sprites in OpenGL
|
||||
- Debugger: Keep track of global cycle count
|
||||
- FFmpeg: Add looping option for GIF/APNG
|
||||
- mGUI: Show battery percentage
|
||||
- mGUI: Skip second scan loop when possible
|
||||
- Qt: Renderer can be changed while a game is running
|
||||
- Qt: Add hex index to palette view
|
||||
- Qt: Add transformation matrix info to sprite view
|
||||
|
@ -128,6 +137,7 @@ Misc:
|
|||
- Qt: Add copy button to GB printer dialog
|
||||
- Qt: Window title updates can be disabled (closes mgba.io/i/1912)
|
||||
- Qt: Redo OpenGL context thread handling (fixes mgba.io/i/1724)
|
||||
- Qt: Discard additional frame draws if waiting fails
|
||||
- Util: Reset vector size on deinit
|
||||
- VFS: Change semantics of VFile.sync on mapped files (fixes mgba.io/i/1730)
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ if(POLICY CMP0025)
|
|||
endif()
|
||||
if(POLICY CMP0072)
|
||||
cmake_policy(SET CMP0072 NEW)
|
||||
set(OpenGL_GL_PREFERENCE LEGACY)
|
||||
endif()
|
||||
project(medusa)
|
||||
set(BINARY_NAME medusa-emu CACHE INTERNAL "Name of output binaries")
|
||||
|
@ -427,6 +428,7 @@ endif()
|
|||
# Feature dependencies
|
||||
set(FEATURE_DEFINES)
|
||||
set(FEATURE_FLAGS)
|
||||
set(FEATURE_SRC)
|
||||
set(FEATURES)
|
||||
set(ENABLES)
|
||||
if(CMAKE_SYSTEM_NAME MATCHES ".*BSD|DragonFly")
|
||||
|
@ -441,6 +443,8 @@ if(BUILD_GL)
|
|||
find_package(OpenGL QUIET)
|
||||
if(NOT OPENGL_FOUND)
|
||||
set(BUILD_GL OFF CACHE BOOL "OpenGL not found" FORCE)
|
||||
elseif(UNIX AND NOT APPLE AND TARGET OpenGL::GL)
|
||||
set(OPENGL_LIBRARY OpenGL::GL)
|
||||
endif()
|
||||
endif()
|
||||
if(NOT BUILD_GL)
|
||||
|
@ -457,18 +461,21 @@ if(NOT BUILD_GLES2)
|
|||
set(OPENGLES2_LIBRARY "" CACHE PATH "" FORCE)
|
||||
endif()
|
||||
if(BUILD_GL)
|
||||
list(APPEND OS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c)
|
||||
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c)
|
||||
list(APPEND FEATURE_DEFINES BUILD_GL)
|
||||
list(APPEND DEPENDENCY_LIB ${OPENGL_LIBRARY})
|
||||
include_directories(${OPENGL_INCLUDE_DIR})
|
||||
endif()
|
||||
if(BUILD_GLES2)
|
||||
list(APPEND OS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c)
|
||||
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c)
|
||||
list(APPEND FEATURE_DEFINES BUILD_GLES2)
|
||||
list(APPEND DEPENDENCY_LIB ${OPENGLES2_LIBRARY})
|
||||
include_directories(${OPENGLES2_INCLUDE_DIR})
|
||||
endif()
|
||||
if(BUILD_GLES3)
|
||||
find_path(OPENGLES3_INCLUDE_DIR NAMES GLES3/gl3.h)
|
||||
find_library(OPENGLES3_LIBRARY NAMES GLESv3 GLESv2)
|
||||
list(APPEND FEATURE_DEFINES BUILD_GLES3)
|
||||
if(NOT OPENGLES3_INCLUDE_DIR OR NOT OPENGLES3_LIBRARY)
|
||||
set(BUILD_GLES3 OFF CACHE BOOL "OpenGL|ES 3 not found" FORCE)
|
||||
endif()
|
||||
|
@ -517,7 +524,6 @@ endif()
|
|||
add_subdirectory(src/debugger)
|
||||
add_subdirectory(src/feature)
|
||||
|
||||
set(FEATURE_SRC)
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6")
|
||||
|
||||
if(USE_EDITLINE)
|
||||
|
@ -730,7 +736,7 @@ if (USE_LZMA)
|
|||
endif()
|
||||
|
||||
if(USE_EPOXY)
|
||||
list(APPEND OS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c)
|
||||
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c)
|
||||
add_definitions(-DBUILD_GL -DBUILD_GLES2)
|
||||
list(APPEND FEATURES EPOXY)
|
||||
include_directories(AFTER ${EPOXY_INCLUDE_DIRS})
|
||||
|
@ -937,18 +943,6 @@ else()
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if(BUILD_GL)
|
||||
add_definitions(-DBUILD_GL)
|
||||
endif()
|
||||
|
||||
if(BUILD_GLES2)
|
||||
add_definitions(-DBUILD_GLES2)
|
||||
endif()
|
||||
|
||||
if(BUILD_GLES3)
|
||||
add_definitions(-DBUILD_GLES3)
|
||||
endif()
|
||||
|
||||
if(DISABLE_FRONTENDS)
|
||||
set(BUILD_SDL OFF)
|
||||
set(BUILD_QT OFF)
|
||||
|
|
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.2 KiB |
|
@ -1,6 +1,3 @@
|
|||
[testinfo]
|
||||
fail=1
|
||||
|
||||
[ports.cinema]
|
||||
gb.model=SGB
|
||||
|
||||
|
|
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 621 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.4 KiB |
|
@ -1,6 +1,3 @@
|
|||
[testinfo]
|
||||
fail=1
|
||||
|
||||
[ports.cinema]
|
||||
gb.model=CGB
|
||||
|
||||
|
|
Before Width: | Height: | Size: 853 B |
After Width: | Height: | Size: 1.3 KiB |
|
@ -57,6 +57,7 @@ struct mVideoLogger {
|
|||
void* dataContext;
|
||||
|
||||
bool block;
|
||||
bool waitOnFlush;
|
||||
void (*init)(struct mVideoLogger*);
|
||||
void (*deinit)(struct mVideoLogger*);
|
||||
void (*reset)(struct mVideoLogger*);
|
||||
|
|
|
@ -248,14 +248,17 @@ struct ARMInstructionInfo {
|
|||
struct ARMCoprocessor cp;
|
||||
};
|
||||
|
||||
struct mDebuggerSymbols;
|
||||
void ARMDecodeARM(uint32_t opcode, struct ARMInstructionInfo* info);
|
||||
void ARMDecodeThumb(uint16_t opcode, struct ARMInstructionInfo* info);
|
||||
bool ARMDecodeThumbCombine(struct ARMInstructionInfo* info1, struct ARMInstructionInfo* info2,
|
||||
struct ARMInstructionInfo* out);
|
||||
int ARMDisassemble(struct ARMInstructionInfo* info, struct ARMCore* core, const struct mDebuggerSymbols* symbols, uint32_t pc, char* buffer, int blen);
|
||||
uint32_t ARMResolveMemoryAccess(struct ARMInstructionInfo* info, struct ARMRegisterFile* regs, uint32_t pc);
|
||||
|
||||
#ifdef USE_DEBUGGERS
|
||||
struct mDebuggerSymbols;
|
||||
int ARMDisassemble(struct ARMInstructionInfo* info, struct ARMCore* core, const struct mDebuggerSymbols* symbols, uint32_t pc, char* buffer, int blen);
|
||||
#endif
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <mgba/internal/debugger/symbols.h>
|
||||
#include <mgba-util/string.h>
|
||||
|
||||
#ifdef USE_DEBUGGERS
|
||||
#define ADVANCE(AMOUNT) \
|
||||
if (AMOUNT >= blen) { \
|
||||
buffer[blen - 1] = '\0'; \
|
||||
|
@ -589,6 +590,7 @@ int ARMDisassemble(struct ARMInstructionInfo* info, struct ARMCore* cpu, const s
|
|||
buffer[blen - 1] = '\0';
|
||||
return total;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t ARMResolveMemoryAccess(struct ARMInstructionInfo* info, struct ARMRegisterFile* regs, uint32_t pc) {
|
||||
uint32_t address = 0;
|
||||
|
|
|
@ -395,6 +395,9 @@ bool mCoreLoadELF(struct mCore* core, struct ELF* elf) {
|
|||
for (i = 0; i < ELFProgramHeadersSize(&ph); ++i) {
|
||||
size_t bsize, esize;
|
||||
Elf32_Phdr* phdr = ELFProgramHeadersGetPointer(&ph, i);
|
||||
if (!phdr->p_filesz) {
|
||||
continue;
|
||||
}
|
||||
void* block = mCoreGetMemoryBlockMasked(core, phdr->p_paddr, &bsize, mCORE_MEMORY_WRITE | mCORE_MEMORY_WORM);
|
||||
char* bytes = ELFBytes(elf, &esize);
|
||||
if (block && bsize >= phdr->p_filesz && esize > phdr->p_offset && esize >= phdr->p_filesz + phdr->p_offset) {
|
||||
|
|
|
@ -49,11 +49,11 @@ bool mCoreSyncWaitFrameStart(struct mCoreSync* sync) {
|
|||
}
|
||||
|
||||
MutexLock(&sync->videoFrameMutex);
|
||||
ConditionWake(&sync->videoFrameRequiredCond);
|
||||
if (!sync->videoFrameWait && !sync->videoFramePending) {
|
||||
return false;
|
||||
}
|
||||
if (sync->videoFrameWait) {
|
||||
ConditionWake(&sync->videoFrameRequiredCond);
|
||||
if (ConditionWaitTimed(&sync->videoFrameAvailableCond, &sync->videoFrameMutex, 50)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -67,6 +67,7 @@ void mCoreSyncWaitFrameEnd(struct mCoreSync* sync) {
|
|||
return;
|
||||
}
|
||||
|
||||
ConditionWake(&sync->videoFrameRequiredCond);
|
||||
MutexUnlock(&sync->videoFrameMutex);
|
||||
}
|
||||
|
||||
|
|
|
@ -119,7 +119,6 @@ static inline size_t _roundUp(size_t value, int shift) {
|
|||
void mVideoLoggerRendererCreate(struct mVideoLogger* logger, bool readonly) {
|
||||
if (readonly) {
|
||||
logger->writeData = _writeNull;
|
||||
logger->block = true;
|
||||
} else {
|
||||
logger->writeData = _writeData;
|
||||
}
|
||||
|
@ -134,6 +133,9 @@ void mVideoLoggerRendererCreate(struct mVideoLogger* logger, bool readonly) {
|
|||
logger->unlock = NULL;
|
||||
logger->wait = NULL;
|
||||
logger->wake = NULL;
|
||||
|
||||
logger->block = readonly;
|
||||
logger->waitOnFlush = !readonly;
|
||||
}
|
||||
|
||||
void mVideoLoggerRendererInit(struct mVideoLogger* logger) {
|
||||
|
@ -263,7 +265,7 @@ void mVideoLoggerRendererFlush(struct mVideoLogger* logger) {
|
|||
0xDEADBEEF,
|
||||
};
|
||||
logger->writeData(logger, &dirty, sizeof(dirty));
|
||||
if (logger->wait) {
|
||||
if (logger->waitOnFlush && logger->wait) {
|
||||
logger->wait(logger);
|
||||
}
|
||||
}
|
||||
|
|
16
src/gb/io.c
|
@ -216,7 +216,7 @@ void GBIOReset(struct GB* gb) {
|
|||
GBIOWrite(gb, GB_REG_HDMA4, 0xFF);
|
||||
gb->memory.io[GB_REG_HDMA5] = 0xFF;
|
||||
} else {
|
||||
memset(&gb->memory.io[GB_REG_KEY0], 0xFF, GB_REG_PCM34 - GB_REG_KEY0);
|
||||
memset(&gb->memory.io[GB_REG_KEY0], 0xFF, GB_REG_PCM34 - GB_REG_KEY0 + 1);
|
||||
}
|
||||
|
||||
if (gb->model & GB_MODEL_SGB) {
|
||||
|
@ -622,6 +622,20 @@ uint8_t GBIORead(struct GB* gb, unsigned address) {
|
|||
return gb->audio.ch3.wavedata8[address - GB_REG_WAVE_0];
|
||||
}
|
||||
break;
|
||||
case GB_REG_PCM12:
|
||||
if (gb->model < GB_MODEL_CGB) {
|
||||
mLOG(GB_IO, GAME_ERROR, "Reading from CGB register FF%02X in DMG mode", address);
|
||||
} else if (gb->audio.enable) {
|
||||
return (gb->audio.ch1.sample) | (gb->audio.ch2.sample << 4);
|
||||
}
|
||||
break;
|
||||
case GB_REG_PCM34:
|
||||
if (gb->model < GB_MODEL_CGB) {
|
||||
mLOG(GB_IO, GAME_ERROR, "Reading from CGB register FF%02X in DMG mode", address);
|
||||
} else if (gb->audio.enable) {
|
||||
return (gb->audio.ch3.sample) | (gb->audio.ch4.sample << 4);
|
||||
}
|
||||
break;
|
||||
case GB_REG_SB:
|
||||
case GB_REG_SC:
|
||||
case GB_REG_IF:
|
||||
|
|
|
@ -498,9 +498,14 @@ static bool _GBACoreLoadROM(struct mCore* core, struct VFile* vf) {
|
|||
#ifdef USE_ELF
|
||||
struct ELF* elf = ELFOpen(vf);
|
||||
if (elf) {
|
||||
GBALoadNull(core->board);
|
||||
if (ELFEntry(elf) == BASE_CART0) {
|
||||
GBALoadNull(core->board);
|
||||
}
|
||||
bool success = mCoreLoadELF(core, elf);
|
||||
ELFClose(elf);
|
||||
if (success) {
|
||||
vf->close(vf);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1675,6 +1675,11 @@ void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GB
|
|||
spriteY -= 256;
|
||||
}
|
||||
|
||||
if (x + totalWidth <= 0 || x >= GBA_VIDEO_HORIZONTAL_PIXELS) {
|
||||
// These sprites aren't displayed but affect cycle counting
|
||||
return;
|
||||
}
|
||||
|
||||
const struct GBAVideoGLShader* shader = &renderer->objShader[GBAObjAttributesAGet256Color(sprite->a)];
|
||||
const GLuint* uniforms = shader->uniforms;
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[GBA_GL_FBO_OBJ]);
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include <mgba-util/memory.h>
|
||||
#include <mgba-util/string.h>
|
||||
|
||||
#define MAX_ENT 4
|
||||
|
||||
struct VFile3DS {
|
||||
struct VFile d;
|
||||
|
||||
|
@ -18,7 +20,9 @@ struct VFile3DS {
|
|||
|
||||
struct VDirEntry3DS {
|
||||
struct VDirEntry d;
|
||||
FS_DirectoryEntry ent;
|
||||
FS_DirectoryEntry ent[MAX_ENT];
|
||||
u32 entCount;
|
||||
u32 currentEnt;
|
||||
char utf8Name[256];
|
||||
};
|
||||
|
||||
|
@ -198,6 +202,7 @@ struct VDir* VDirOpen(const char* path) {
|
|||
|
||||
vd3d->vde.d.name = _vd3deName;
|
||||
vd3d->vde.d.type = _vd3deType;
|
||||
vd3d->vde.entCount = 0;
|
||||
|
||||
return &vd3d->d;
|
||||
}
|
||||
|
@ -222,12 +227,16 @@ static void _vd3dRewind(struct VDir* vd) {
|
|||
|
||||
static struct VDirEntry* _vd3dListNext(struct VDir* vd) {
|
||||
struct VDir3DS* vd3d = (struct VDir3DS*) vd;
|
||||
u32 n = 0;
|
||||
memset(&vd3d->vde.ent, 0, sizeof(vd3d->vde.ent));
|
||||
memset(vd3d->vde.utf8Name, 0, sizeof(vd3d->vde.utf8Name));
|
||||
FSDIR_Read(vd3d->handle, &n, 1, &vd3d->vde.ent);
|
||||
if (!n) {
|
||||
return 0;
|
||||
if (!vd3d->vde.entCount || vd3d->vde.currentEnt + 1 >= vd3d->vde.entCount) {
|
||||
memset(&vd3d->vde.ent, 0, sizeof(vd3d->vde.ent));
|
||||
FSDIR_Read(vd3d->handle, &vd3d->vde.entCount, MAX_ENT, vd3d->vde.ent);
|
||||
vd3d->vde.currentEnt = 0;
|
||||
} else {
|
||||
++vd3d->vde.currentEnt;
|
||||
}
|
||||
if (!vd3d->vde.entCount) {
|
||||
return NULL;
|
||||
}
|
||||
return &vd3d->vde.d;
|
||||
}
|
||||
|
@ -296,14 +305,14 @@ static bool _vd3dDeleteFile(struct VDir* vd, const char* path) {
|
|||
static const char* _vd3deName(struct VDirEntry* vde) {
|
||||
struct VDirEntry3DS* vd3de = (struct VDirEntry3DS*) vde;
|
||||
if (!vd3de->utf8Name[0]) {
|
||||
utf16_to_utf8((uint8_t*) vd3de->utf8Name, vd3de->ent.name, sizeof(vd3de->utf8Name));
|
||||
utf16_to_utf8((uint8_t*) vd3de->utf8Name, vd3de->ent[vd3de->currentEnt].name, sizeof(vd3de->utf8Name));
|
||||
}
|
||||
return vd3de->utf8Name;
|
||||
}
|
||||
|
||||
static enum VFSType _vd3deType(struct VDirEntry* vde) {
|
||||
struct VDirEntry3DS* vd3de = (struct VDirEntry3DS*) vde;
|
||||
if (vd3de->ent.attributes & FS_ATTRIBUTE_DIRECTORY) {
|
||||
if (vd3de->ent[vd3de->currentEnt].attributes & FS_ATTRIBUTE_DIRECTORY) {
|
||||
return VFS_DIRECTORY;
|
||||
}
|
||||
return VFS_FILE;
|
||||
|
|
|
@ -325,10 +325,7 @@ install(TARGETS ${BINARY_NAME}-qt
|
|||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-qt
|
||||
BUNDLE DESTINATION Applications COMPONENT ${BINARY_NAME}-qt)
|
||||
if(UNIX AND NOT APPLE)
|
||||
find_program(DESKTOP_FILE_INSTALL desktop-file-install)
|
||||
if(DESKTOP_FILE_INSTALL)
|
||||
install(CODE "execute_process(COMMAND ${DESKTOP_FILE_INSTALL} \"${CMAKE_SOURCE_DIR}/res/${BINARY_NAME}-qt.desktop\" --dir \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/applications/\")" COMPONENT ${BINARY_NAME}-qt)
|
||||
endif()
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/res/${BINARY_NAME}-qt.desktop DESTINATION share/applications COMPONENT ${BINARY_NAME}-qt)
|
||||
endif()
|
||||
if(UNIX)
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/doc/${BINARY_NAME}-qt.6 DESTINATION ${MANDIR}/man6 COMPONENT ${BINARY_NAME}-qt)
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include <QTimer>
|
||||
#include <QWindow>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba-util/math.h>
|
||||
#ifdef BUILD_GL
|
||||
|
@ -435,26 +437,39 @@ void PainterGL::draw() {
|
|||
if (!m_active || m_queue.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (m_lagging >= 1) {
|
||||
return;
|
||||
}
|
||||
mCoreSync* sync = &m_context->thread()->impl->sync;
|
||||
mCoreSyncWaitFrameStart(sync);
|
||||
if (!mCoreSyncWaitFrameStart(sync)) {
|
||||
mCoreSyncWaitFrameEnd(sync);
|
||||
++m_lagging;
|
||||
if (m_delayTimer.elapsed() < 1000 / m_surface->screen()->refreshRate()) {
|
||||
QTimer::singleShot(1, this, &PainterGL::draw);
|
||||
}
|
||||
return;
|
||||
}
|
||||
dequeue();
|
||||
if (m_videoProxy) {
|
||||
// Only block on the next frame if we're trying to run at roughly 60fps via audio
|
||||
m_videoProxy->setBlocking(sync->audioWait && std::abs(60.f - sync->fpsTarget) < 0.1f);
|
||||
}
|
||||
if (!m_delayTimer.isValid()) {
|
||||
m_delayTimer.start();
|
||||
} else if (sync->audioWait || sync->videoFrameWait) {
|
||||
while (m_delayTimer.nsecsElapsed() + 2000000 < 1000000000 / sync->fpsTarget) {
|
||||
QThread::usleep(500);
|
||||
}
|
||||
m_delayTimer.restart();
|
||||
}
|
||||
mCoreSyncWaitFrameEnd(sync);
|
||||
|
||||
forceDraw();
|
||||
performDraw();
|
||||
m_backend->swap(m_backend);
|
||||
m_delayTimer.restart();
|
||||
}
|
||||
|
||||
void PainterGL::forceDraw() {
|
||||
m_painter.begin(m_window.get());
|
||||
performDraw();
|
||||
m_painter.end();
|
||||
if (!m_context->thread()->impl->sync.audioWait && !m_context->thread()->impl->sync.videoFrameWait) {
|
||||
if (m_delayTimer.elapsed() < 1000 / m_surface->screen()->refreshRate()) {
|
||||
return;
|
||||
|
@ -485,10 +500,12 @@ void PainterGL::pause() {
|
|||
}
|
||||
|
||||
void PainterGL::unpause() {
|
||||
m_lagging = 0;
|
||||
m_active = true;
|
||||
}
|
||||
|
||||
void PainterGL::performDraw() {
|
||||
m_painter.begin(m_window.get());
|
||||
m_painter.beginNativePainting();
|
||||
float r = m_surface->devicePixelRatio();
|
||||
m_backend->resized(m_backend, m_size.width() * r, m_size.height() * r);
|
||||
|
@ -500,6 +517,7 @@ void PainterGL::performDraw() {
|
|||
if (m_showOSD && m_messagePainter) {
|
||||
m_messagePainter->paint(&m_painter);
|
||||
}
|
||||
m_painter.end();
|
||||
}
|
||||
|
||||
void PainterGL::enqueue(const uint32_t* backing) {
|
||||
|
@ -516,6 +534,7 @@ void PainterGL::enqueue(const uint32_t* backing) {
|
|||
memcpy(buffer, backing, size.width() * size.height() * BYTES_PER_PIXEL);
|
||||
}
|
||||
}
|
||||
m_lagging = 0;
|
||||
m_queue.enqueue(buffer);
|
||||
}
|
||||
|
||||
|
@ -530,6 +549,7 @@ void PainterGL::dequeue() {
|
|||
m_buffer = nullptr;
|
||||
}
|
||||
m_buffer = buffer;
|
||||
return;
|
||||
}
|
||||
|
||||
void PainterGL::dequeueAll() {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#include <QAtomicInt>
|
||||
#include <QElapsedTimer>
|
||||
#include <QOpenGLContext>
|
||||
#include <QList>
|
||||
|
@ -130,6 +131,7 @@ private:
|
|||
std::array<std::array<uint32_t, 0x100000>, 3> m_buffers;
|
||||
QList<uint32_t*> m_free;
|
||||
QQueue<uint32_t*> m_queue;
|
||||
QAtomicInt m_lagging = 0;
|
||||
uint32_t* m_buffer;
|
||||
QPainter m_painter;
|
||||
QMutex m_mutex;
|
||||
|
|
|
@ -14,6 +14,7 @@ using namespace QGBA;
|
|||
VideoProxy::VideoProxy() {
|
||||
mVideoLoggerRendererCreate(&m_logger.d, false);
|
||||
m_logger.d.block = true;
|
||||
m_logger.d.waitOnFlush = false;
|
||||
|
||||
m_logger.d.init = &cbind<&VideoProxy::init>;
|
||||
m_logger.d.reset = &cbind<&VideoProxy::reset>;
|
||||
|
|
|
@ -24,6 +24,7 @@ public:
|
|||
|
||||
void attach(CoreController*);
|
||||
void detach(CoreController*);
|
||||
void setBlocking(bool block) { m_logger.d.waitOnFlush = block; }
|
||||
|
||||
signals:
|
||||
void dataAvailable();
|
||||
|
|
|
@ -214,10 +214,6 @@ void Window::argumentsPassed(mArguments* args) {
|
|||
m_pendingState = args->savestate;
|
||||
}
|
||||
|
||||
if (args->fname) {
|
||||
setController(m_manager->loadGame(args->fname), args->fname);
|
||||
}
|
||||
|
||||
#ifdef USE_GDB_STUB
|
||||
if (args->debuggerType == DEBUGGER_GDB) {
|
||||
if (!m_gdbController) {
|
||||
|
@ -225,10 +221,15 @@ void Window::argumentsPassed(mArguments* args) {
|
|||
if (m_controller) {
|
||||
m_gdbController->setController(m_controller);
|
||||
}
|
||||
m_gdbController->attach();
|
||||
m_gdbController->listen();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (args->fname) {
|
||||
setController(m_manager->loadGame(args->fname), args->fname);
|
||||
}
|
||||
}
|
||||
|
||||
void Window::resizeFrame(const QSize& size) {
|
||||
|
|
|
@ -3,6 +3,11 @@ find_program(GXTEXCONV gxtexconv)
|
|||
find_program(RAW2C raw2c)
|
||||
find_program(WIILOAD wiiload)
|
||||
|
||||
find_library(WIIDRC_LIBRARY wiidrc)
|
||||
if(WIIDRC_LIBRARY)
|
||||
add_definitions(-DWIIDRC)
|
||||
endif()
|
||||
|
||||
set(OS_DEFINES _GNU_SOURCE COLOR_16_BIT COLOR_5_6_5 USE_VFS_FILE IOAPI_NO_64 FIXED_ROM_BUFFER)
|
||||
list(APPEND CORE_VFS_SRC ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-file.c ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-dirent.c ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-devlist.c)
|
||||
|
||||
|
@ -20,6 +25,9 @@ set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/font.c PROPERTIES GENERA
|
|||
add_executable(${BINARY_NAME}.elf ${GUI_SRC} main.c)
|
||||
set_target_properties(${BINARY_NAME}.elf PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
|
||||
target_link_libraries(${BINARY_NAME}.elf ${BINARY_NAME} ${OS_LIB})
|
||||
if(WIIDRC_LIBRARY)
|
||||
target_link_libraries(${BINARY_NAME}.elf ${WIIDRC_LIBRARY})
|
||||
endif()
|
||||
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/font.tpl
|
||||
COMMAND ${GXTEXCONV} -i ${CMAKE_SOURCE_DIR}/res/font2x.png -o font.tpl colfmt=5 mipmap=no
|
||||
|
|
|
@ -28,10 +28,15 @@
|
|||
#include <mgba-util/memory.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
#ifdef WIIDRC
|
||||
#include <wiidrc/wiidrc.h>
|
||||
#endif
|
||||
|
||||
#define GCN1_INPUT 0x47434E31
|
||||
#define GCN2_INPUT 0x47434E32
|
||||
#define WIIMOTE_INPUT 0x5749494D
|
||||
#define CLASSIC_INPUT 0x57494943
|
||||
#define DRC_INPUT 0x44524355
|
||||
|
||||
#define TEX_W 256
|
||||
#define TEX_H 224
|
||||
|
@ -254,6 +259,9 @@ int main(int argc, char* argv[]) {
|
|||
PAD_Init();
|
||||
WPAD_Init();
|
||||
WPAD_SetDataFormat(0, WPAD_FMT_BTNS_ACC_IR);
|
||||
#ifdef WIIDRC
|
||||
WiiDRC_Init();
|
||||
#endif
|
||||
AUDIO_Init(0);
|
||||
AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ);
|
||||
AUDIO_RegisterDMACallback(_audioDMA);
|
||||
|
@ -452,6 +460,31 @@ int main(int argc, char* argv[]) {
|
|||
},
|
||||
.nKeys = 32
|
||||
},
|
||||
#ifdef WIIDRC
|
||||
{
|
||||
.name = "Wii U GamePad Input",
|
||||
.id = DRC_INPUT,
|
||||
.keyNames = (const char*[]) {
|
||||
0, // Sync
|
||||
"\1\xE",
|
||||
"-",
|
||||
"+",
|
||||
"R",
|
||||
"L",
|
||||
"ZR",
|
||||
"ZL",
|
||||
"Down",
|
||||
"Up",
|
||||
"Right",
|
||||
"Left",
|
||||
"Y",
|
||||
"X",
|
||||
"B",
|
||||
"A",
|
||||
},
|
||||
.nKeys = 16
|
||||
},
|
||||
#endif
|
||||
{ .id = 0 }
|
||||
},
|
||||
.configExtra = (struct GUIMenuItem[]) {
|
||||
|
@ -576,6 +609,15 @@ int main(int argc, char* argv[]) {
|
|||
_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GUI_INPUT_LEFT);
|
||||
_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GUI_INPUT_RIGHT);
|
||||
|
||||
#ifdef WIIDRC
|
||||
_mapKey(&runner.params.keyMap, DRC_INPUT, WIIDRC_BUTTON_A, GUI_INPUT_SELECT);
|
||||
_mapKey(&runner.params.keyMap, DRC_INPUT, WIIDRC_BUTTON_B, GUI_INPUT_BACK);
|
||||
_mapKey(&runner.params.keyMap, DRC_INPUT, WIIDRC_BUTTON_X, GUI_INPUT_CANCEL);
|
||||
_mapKey(&runner.params.keyMap, DRC_INPUT, WIIDRC_BUTTON_UP, GUI_INPUT_UP);
|
||||
_mapKey(&runner.params.keyMap, DRC_INPUT, WIIDRC_BUTTON_DOWN, GUI_INPUT_DOWN);
|
||||
_mapKey(&runner.params.keyMap, DRC_INPUT, WIIDRC_BUTTON_LEFT, GUI_INPUT_LEFT);
|
||||
_mapKey(&runner.params.keyMap, DRC_INPUT, WIIDRC_BUTTON_RIGHT, GUI_INPUT_RIGHT);
|
||||
#endif
|
||||
|
||||
float stretch = 0;
|
||||
if (mCoreConfigGetFloatValue(&runner.config, "stretchWidth", &stretch)) {
|
||||
|
@ -701,29 +743,48 @@ static uint32_t _pollInput(const struct mInputMap* map) {
|
|||
u32 ext = 0;
|
||||
WPAD_Probe(0, &ext);
|
||||
|
||||
#ifdef WIIDRC
|
||||
u32 drckeys = 0;
|
||||
if (WiiDRC_ScanPads()) {
|
||||
drckeys = WiiDRC_ButtonsHeld();
|
||||
}
|
||||
#endif
|
||||
|
||||
int keys = 0;
|
||||
keys |= mInputMapKeyBits(map, GCN1_INPUT, padkeys, 0);
|
||||
keys |= mInputMapKeyBits(map, GCN2_INPUT, padkeys, 0);
|
||||
keys |= mInputMapKeyBits(map, WIIMOTE_INPUT, wiiPad, 0);
|
||||
#ifdef WIIDRC
|
||||
keys |= mInputMapKeyBits(map, DRC_INPUT, drckeys, 0);
|
||||
#endif
|
||||
if (ext == WPAD_EXP_CLASSIC) {
|
||||
keys |= mInputMapKeyBits(map, CLASSIC_INPUT, wiiPad, 0);
|
||||
}
|
||||
int x = PAD_StickX(0);
|
||||
int y = PAD_StickY(0);
|
||||
int w_x = WPAD_StickX(0, 0);
|
||||
int w_y = WPAD_StickY(0, 0);
|
||||
if (x < -ANALOG_DEADZONE || w_x < -ANALOG_DEADZONE) {
|
||||
int wX = WPAD_StickX(0, 0);
|
||||
int wY = WPAD_StickY(0, 0);
|
||||
ATTRIBUTE_UNUSED int drcX = 0;
|
||||
ATTRIBUTE_UNUSED int drcY = 0;
|
||||
#ifdef WIIDRC
|
||||
if (WiiDRC_Connected()) {
|
||||
drcX = WiiDRC_lStickX();
|
||||
drcY = WiiDRC_lStickY();
|
||||
}
|
||||
#endif
|
||||
if (x < -ANALOG_DEADZONE || wX < -ANALOG_DEADZONE || drcX < -ANALOG_DEADZONE) {
|
||||
keys |= 1 << GUI_INPUT_LEFT;
|
||||
}
|
||||
if (x > ANALOG_DEADZONE || w_x > ANALOG_DEADZONE) {
|
||||
if (x > ANALOG_DEADZONE || wX > ANALOG_DEADZONE || drcX > ANALOG_DEADZONE) {
|
||||
keys |= 1 << GUI_INPUT_RIGHT;
|
||||
}
|
||||
if (y < -ANALOG_DEADZONE || w_y < -ANALOG_DEADZONE) {
|
||||
if (y < -ANALOG_DEADZONE || wY < -ANALOG_DEADZONE || drcY < -ANALOG_DEADZONE) {
|
||||
keys |= 1 << GUI_INPUT_DOWN;
|
||||
}
|
||||
if (y > ANALOG_DEADZONE || w_y > ANALOG_DEADZONE) {
|
||||
if (y > ANALOG_DEADZONE || wY > ANALOG_DEADZONE || drcY > ANALOG_DEADZONE) {
|
||||
keys |= 1 << GUI_INPUT_UP;
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
|
@ -803,12 +864,27 @@ void _setup(struct mGUIRunner* runner) {
|
|||
_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_L, GBA_KEY_L);
|
||||
_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_R, GBA_KEY_R);
|
||||
|
||||
#ifdef WIIDRC
|
||||
_mapKey(&runner->core->inputMap, DRC_INPUT, WIIDRC_BUTTON_A, GBA_KEY_A);
|
||||
_mapKey(&runner->core->inputMap, DRC_INPUT, WIIDRC_BUTTON_B, GBA_KEY_B);
|
||||
_mapKey(&runner->core->inputMap, DRC_INPUT, WIIDRC_BUTTON_PLUS, GBA_KEY_START);
|
||||
_mapKey(&runner->core->inputMap, DRC_INPUT, WIIDRC_BUTTON_MINUS, GBA_KEY_SELECT);
|
||||
_mapKey(&runner->core->inputMap, DRC_INPUT, WIIDRC_BUTTON_UP, GBA_KEY_UP);
|
||||
_mapKey(&runner->core->inputMap, DRC_INPUT, WIIDRC_BUTTON_DOWN, GBA_KEY_DOWN);
|
||||
_mapKey(&runner->core->inputMap, DRC_INPUT, WIIDRC_BUTTON_LEFT, GBA_KEY_LEFT);
|
||||
_mapKey(&runner->core->inputMap, DRC_INPUT, WIIDRC_BUTTON_RIGHT, GBA_KEY_RIGHT);
|
||||
_mapKey(&runner->core->inputMap, DRC_INPUT, WIIDRC_BUTTON_L, GBA_KEY_L);
|
||||
_mapKey(&runner->core->inputMap, DRC_INPUT, WIIDRC_BUTTON_R, GBA_KEY_R);
|
||||
#endif
|
||||
|
||||
struct mInputAxis desc = { GBA_KEY_RIGHT, GBA_KEY_LEFT, ANALOG_DEADZONE, -ANALOG_DEADZONE };
|
||||
mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 0, &desc);
|
||||
mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, &desc);
|
||||
mInputBindAxis(&runner->core->inputMap, DRC_INPUT, 0, &desc);
|
||||
desc = (struct mInputAxis) { GBA_KEY_UP, GBA_KEY_DOWN, ANALOG_DEADZONE, -ANALOG_DEADZONE };
|
||||
mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 1, &desc);
|
||||
mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, &desc);
|
||||
mInputBindAxis(&runner->core->inputMap, DRC_INPUT, 1, &desc);
|
||||
|
||||
outputBuffer = memalign(32, TEX_W * TEX_H * BYTES_PER_PIXEL);
|
||||
runner->core->setVideoBuffer(runner->core, outputBuffer, TEX_W);
|
||||
|
@ -1033,9 +1109,18 @@ uint16_t _pollGameInput(struct mGUIRunner* runner) {
|
|||
u32 wiiPad = WPAD_ButtonsHeld(0);
|
||||
u32 ext = 0;
|
||||
WPAD_Probe(0, &ext);
|
||||
#ifdef WIIDRC
|
||||
u32 drckeys = 0;
|
||||
if (WiiDRC_ScanPads()) {
|
||||
drckeys = WiiDRC_ButtonsHeld();
|
||||
}
|
||||
#endif
|
||||
uint16_t keys = mInputMapKeyBits(&runner->core->inputMap, GCN1_INPUT, padkeys, 0);
|
||||
keys |= mInputMapKeyBits(&runner->core->inputMap, GCN2_INPUT, padkeys, 0);
|
||||
keys |= mInputMapKeyBits(&runner->core->inputMap, WIIMOTE_INPUT, wiiPad, 0);
|
||||
#ifdef WIIDRC
|
||||
keys |= mInputMapKeyBits(&runner->core->inputMap, DRC_INPUT, drckeys, 0);
|
||||
#endif
|
||||
|
||||
enum GBAKey angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 0, PAD_StickX(0));
|
||||
if (angles != GBA_KEY_NONE) {
|
||||
|
@ -1056,6 +1141,19 @@ uint16_t _pollGameInput(struct mGUIRunner* runner) {
|
|||
keys |= 1 << angles;
|
||||
}
|
||||
}
|
||||
#ifdef WIIDRC
|
||||
if (WiiDRC_Connected()) {
|
||||
keys |= mInputMapKeyBits(&runner->core->inputMap, DRC_INPUT, drckeys, 0);
|
||||
angles = mInputMapAxis(&runner->core->inputMap, DRC_INPUT, 0, WiiDRC_lStickX());
|
||||
if (angles != GBA_KEY_NONE) {
|
||||
keys |= 1 << angles;
|
||||
}
|
||||
angles = mInputMapAxis(&runner->core->inputMap, DRC_INPUT, 1, WiiDRC_lStickY());
|
||||
if (angles != GBA_KEY_NONE) {
|
||||
keys |= 1 << angles;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
|
|
@ -89,6 +89,8 @@ static bool _refreshDirectory(struct GUIParams* params, const char* currentPath,
|
|||
char* n2 = malloc(len);
|
||||
snprintf(n2, len, "%s/", name);
|
||||
name = n2;
|
||||
} else if (filterName && !filterName(name)) {
|
||||
continue;
|
||||
} else {
|
||||
name = strdup(name);
|
||||
}
|
||||
|
@ -96,59 +98,58 @@ static bool _refreshDirectory(struct GUIParams* params, const char* currentPath,
|
|||
++items;
|
||||
}
|
||||
qsort(GUIMenuItemListGetPointer(currentFiles, 1), GUIMenuItemListSize(currentFiles) - 1, sizeof(struct GUIMenuItem), _strpcmp);
|
||||
i = 0;
|
||||
size_t item = 0;
|
||||
while (item < GUIMenuItemListSize(currentFiles)) {
|
||||
++i;
|
||||
if (!(i % SCANNING_THRESHOLD_2)) {
|
||||
uint32_t input = 0;
|
||||
GUIPollInput(params, &input, 0);
|
||||
if (input & (1 << GUI_INPUT_CANCEL)) {
|
||||
dir->close(dir);
|
||||
return false;
|
||||
}
|
||||
|
||||
params->drawStart();
|
||||
if (params->guiPrepare) {
|
||||
params->guiPrepare();
|
||||
}
|
||||
GUIFontPrintf(params->font, 0, GUIFontHeight(params->font), GUI_ALIGN_LEFT, 0xFFFFFFFF, "(scanning item %"PRIz"u of %"PRIz"u)", i, items);
|
||||
GUIFontPrintf(params->font, 0, GUIFontHeight(params->font) * 2, GUI_ALIGN_LEFT, 0xFFFFFFFF, "%s", currentPath);
|
||||
if (params->guiFinish) {
|
||||
params->guiFinish();
|
||||
}
|
||||
params->drawEnd();
|
||||
}
|
||||
struct GUIMenuItem* testItem = GUIMenuItemListGetPointer(currentFiles, item);
|
||||
if (testItem->data != (void*) VFS_FILE) {
|
||||
++item;
|
||||
continue;
|
||||
}
|
||||
bool failed = false;
|
||||
if (filterName && !filterName(testItem->title)) {
|
||||
failed = true;
|
||||
}
|
||||
|
||||
if (!failed && filterContents) {
|
||||
struct VFile* vf = dir->openFile(dir, testItem->title, O_RDONLY);
|
||||
if (!vf) {
|
||||
failed = true;
|
||||
} else {
|
||||
if (!filterContents(vf)) {
|
||||
failed = true;
|
||||
if (preselect || filterContents) {
|
||||
i = 0;
|
||||
size_t item = 0;
|
||||
while (item < GUIMenuItemListSize(currentFiles)) {
|
||||
++i;
|
||||
// If we're not filtering the contents, this loop is fast, so there's no need to show updates
|
||||
if (filterContents && !(i % SCANNING_THRESHOLD_2)) {
|
||||
uint32_t input = 0;
|
||||
GUIPollInput(params, &input, 0);
|
||||
if (input & (1 << GUI_INPUT_CANCEL)) {
|
||||
dir->close(dir);
|
||||
return false;
|
||||
}
|
||||
vf->close(vf);
|
||||
}
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
free((char*) testItem->title);
|
||||
GUIMenuItemListShift(currentFiles, item, 1);
|
||||
} else {
|
||||
if (preselect && strncmp(testItem->title, preselect, PATH_MAX) == 0) {
|
||||
params->fileIndex = item;
|
||||
params->drawStart();
|
||||
if (params->guiPrepare) {
|
||||
params->guiPrepare();
|
||||
}
|
||||
GUIFontPrintf(params->font, 0, GUIFontHeight(params->font), GUI_ALIGN_LEFT, 0xFFFFFFFF, "(scanning item %"PRIz"u of %"PRIz"u)", i, items);
|
||||
GUIFontPrintf(params->font, 0, GUIFontHeight(params->font) * 2, GUI_ALIGN_LEFT, 0xFFFFFFFF, "%s", currentPath);
|
||||
if (params->guiFinish) {
|
||||
params->guiFinish();
|
||||
}
|
||||
params->drawEnd();
|
||||
}
|
||||
struct GUIMenuItem* testItem = GUIMenuItemListGetPointer(currentFiles, item);
|
||||
if (testItem->data != (void*) VFS_FILE) {
|
||||
++item;
|
||||
continue;
|
||||
}
|
||||
bool failed = false;
|
||||
if (filterContents) {
|
||||
struct VFile* vf = dir->openFile(dir, testItem->title, O_RDONLY);
|
||||
if (!vf) {
|
||||
failed = true;
|
||||
} else {
|
||||
if (!filterContents(vf)) {
|
||||
failed = true;
|
||||
}
|
||||
vf->close(vf);
|
||||
}
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
free((char*) testItem->title);
|
||||
GUIMenuItemListShift(currentFiles, item, 1);
|
||||
} else {
|
||||
if (preselect && strncmp(testItem->title, preselect, PATH_MAX) == 0) {
|
||||
params->fileIndex = item;
|
||||
}
|
||||
++item;
|
||||
}
|
||||
++item;
|
||||
}
|
||||
}
|
||||
dir->close(dir);
|
||||
|
|