diff --git a/CHANGES b/CHANGES index 06f30ba0d..647dfbe1c 100644 --- a/CHANGES +++ b/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) diff --git a/CMakeLists.txt b/CMakeLists.txt index a602c7cfc..bc98375b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/cinema/gb/mooneye-gb/acceptance/boot_div-S/xbaseline_0000.png b/cinema/gb/mooneye-gb/acceptance/boot_div-S/xbaseline_0000.png new file mode 100644 index 000000000..7cd244f25 Binary files /dev/null and b/cinema/gb/mooneye-gb/acceptance/boot_div-S/xbaseline_0000.png differ diff --git a/cinema/gb/mooneye-gb/acceptance/boot_div-dmg0/xbaseline_0000.png b/cinema/gb/mooneye-gb/acceptance/boot_div-dmg0/xbaseline_0000.png new file mode 100644 index 000000000..f1b1b859c Binary files /dev/null and b/cinema/gb/mooneye-gb/acceptance/boot_div-dmg0/xbaseline_0000.png differ diff --git a/cinema/gb/mooneye-gb/acceptance/boot_div-dmgABCmgb/xbaseline_0000.png b/cinema/gb/mooneye-gb/acceptance/boot_div-dmgABCmgb/xbaseline_0000.png new file mode 100644 index 000000000..7da91f958 Binary files /dev/null and b/cinema/gb/mooneye-gb/acceptance/boot_div-dmgABCmgb/xbaseline_0000.png differ diff --git a/cinema/gb/mooneye-gb/acceptance/boot_div2-S/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/boot_div2-S/baseline_0000.png new file mode 100644 index 000000000..a1964c20a Binary files /dev/null and b/cinema/gb/mooneye-gb/acceptance/boot_div2-S/baseline_0000.png differ diff --git a/cinema/gb/mooneye-gb/acceptance/boot_div2-S/config.ini b/cinema/gb/mooneye-gb/acceptance/boot_div2-S/config.ini index be43e382a..cc5f28f4f 100644 --- a/cinema/gb/mooneye-gb/acceptance/boot_div2-S/config.ini +++ b/cinema/gb/mooneye-gb/acceptance/boot_div2-S/config.ini @@ -1,6 +1,3 @@ -[testinfo] -fail=1 - [ports.cinema] gb.model=SGB diff --git a/cinema/gb/mooneye-gb/acceptance/ppu/hblank_ly_scx_timing-GS/xbaseline_0000.png b/cinema/gb/mooneye-gb/acceptance/ppu/hblank_ly_scx_timing-GS/xbaseline_0000.png new file mode 100644 index 000000000..bfd601395 Binary files /dev/null and b/cinema/gb/mooneye-gb/acceptance/ppu/hblank_ly_scx_timing-GS/xbaseline_0000.png differ diff --git a/cinema/gb/mooneye-gb/acceptance/ppu/intr_2_mode0_timing_sprites/xbaseline_0000.png b/cinema/gb/mooneye-gb/acceptance/ppu/intr_2_mode0_timing_sprites/xbaseline_0000.png new file mode 100644 index 000000000..74348ef72 Binary files /dev/null and b/cinema/gb/mooneye-gb/acceptance/ppu/intr_2_mode0_timing_sprites/xbaseline_0000.png differ diff --git a/cinema/gb/mooneye-gb/acceptance/ppu/lcdon_timing-GS/xbaseline_0000.png b/cinema/gb/mooneye-gb/acceptance/ppu/lcdon_timing-GS/xbaseline_0000.png new file mode 100644 index 000000000..cdd28042c Binary files /dev/null and b/cinema/gb/mooneye-gb/acceptance/ppu/lcdon_timing-GS/xbaseline_0000.png differ diff --git a/cinema/gb/mooneye-gb/acceptance/ppu/lcdon_write_timing-GS/xbaseline_0000.png b/cinema/gb/mooneye-gb/acceptance/ppu/lcdon_write_timing-GS/xbaseline_0000.png new file mode 100644 index 000000000..26fab83aa Binary files /dev/null and b/cinema/gb/mooneye-gb/acceptance/ppu/lcdon_write_timing-GS/xbaseline_0000.png differ diff --git a/cinema/gb/mooneye-gb/acceptance/serial/boot_sclk_align-dmgABCmgb/xbaseline_0000.png b/cinema/gb/mooneye-gb/acceptance/serial/boot_sclk_align-dmgABCmgb/xbaseline_0000.png new file mode 100644 index 000000000..ed7bd71b0 Binary files /dev/null and b/cinema/gb/mooneye-gb/acceptance/serial/boot_sclk_align-dmgABCmgb/xbaseline_0000.png differ diff --git a/cinema/gb/mooneye-gb/misc/boot_div-A/xbaseline_0000.png b/cinema/gb/mooneye-gb/misc/boot_div-A/xbaseline_0000.png new file mode 100644 index 000000000..9834e5db4 Binary files /dev/null and b/cinema/gb/mooneye-gb/misc/boot_div-A/xbaseline_0000.png differ diff --git a/cinema/gb/mooneye-gb/misc/boot_div-cgb0/xbaseline_0000.png b/cinema/gb/mooneye-gb/misc/boot_div-cgb0/xbaseline_0000.png new file mode 100644 index 000000000..b9d24cc3a Binary files /dev/null and b/cinema/gb/mooneye-gb/misc/boot_div-cgb0/xbaseline_0000.png differ diff --git a/cinema/gb/mooneye-gb/misc/boot_div-cgbABCDE/xbaseline_0000.png b/cinema/gb/mooneye-gb/misc/boot_div-cgbABCDE/xbaseline_0000.png new file mode 100644 index 000000000..38e760ea8 Binary files /dev/null and b/cinema/gb/mooneye-gb/misc/boot_div-cgbABCDE/xbaseline_0000.png differ diff --git a/cinema/gb/mooneye-gb/misc/boot_hwio-C/config.ini b/cinema/gb/mooneye-gb/misc/boot_hwio-C/config.ini index 20566851b..a7b84195c 100644 --- a/cinema/gb/mooneye-gb/misc/boot_hwio-C/config.ini +++ b/cinema/gb/mooneye-gb/misc/boot_hwio-C/config.ini @@ -1,6 +1,3 @@ -[testinfo] -fail=1 - [ports.cinema] gb.model=CGB diff --git a/cinema/gb/mooneye-gb/misc/boot_hwio-C/xbaseline_0000.png b/cinema/gb/mooneye-gb/misc/boot_hwio-C/xbaseline_0000.png deleted file mode 100644 index 5b043da58..000000000 Binary files a/cinema/gb/mooneye-gb/misc/boot_hwio-C/xbaseline_0000.png and /dev/null differ diff --git a/cinema/gb/mooneye-gb/misc/ppu/vblank_stat_intr-C/xbaseline_0000.png b/cinema/gb/mooneye-gb/misc/ppu/vblank_stat_intr-C/xbaseline_0000.png new file mode 100644 index 000000000..57a392a97 Binary files /dev/null and b/cinema/gb/mooneye-gb/misc/ppu/vblank_stat_intr-C/xbaseline_0000.png differ diff --git a/include/mgba/feature/video-logger.h b/include/mgba/feature/video-logger.h index 2530d3fa2..cf50dd58e 100644 --- a/include/mgba/feature/video-logger.h +++ b/include/mgba/feature/video-logger.h @@ -57,6 +57,7 @@ struct mVideoLogger { void* dataContext; bool block; + bool waitOnFlush; void (*init)(struct mVideoLogger*); void (*deinit)(struct mVideoLogger*); void (*reset)(struct mVideoLogger*); diff --git a/include/mgba/internal/arm/decoder.h b/include/mgba/internal/arm/decoder.h index 71b3a09e4..fc612f306 100644 --- a/include/mgba/internal/arm/decoder.h +++ b/include/mgba/internal/arm/decoder.h @@ -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 diff --git a/src/arm/decoder.c b/src/arm/decoder.c index bbb839003..435a751f9 100644 --- a/src/arm/decoder.c +++ b/src/arm/decoder.c @@ -9,6 +9,7 @@ #include #include +#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; diff --git a/src/core/core.c b/src/core/core.c index 70aac8059..5bcfa2bdf 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -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) { diff --git a/src/core/sync.c b/src/core/sync.c index 492572534..b2a65493b 100644 --- a/src/core/sync.c +++ b/src/core/sync.c @@ -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); } diff --git a/src/feature/video-logger.c b/src/feature/video-logger.c index 5c2a98f5c..508517c65 100644 --- a/src/feature/video-logger.c +++ b/src/feature/video-logger.c @@ -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); } } diff --git a/src/gb/io.c b/src/gb/io.c index c0a2d36cc..0e3a70eca 100644 --- a/src/gb/io.c +++ b/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: diff --git a/src/gba/core.c b/src/gba/core.c index 6c89e3d76..3a93a3648 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -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 diff --git a/src/gba/renderers/gl.c b/src/gba/renderers/gl.c index 661443a0c..704bd8c2e 100644 --- a/src/gba/renderers/gl.c +++ b/src/gba/renderers/gl.c @@ -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]); diff --git a/src/platform/3ds/3ds-vfs.c b/src/platform/3ds/3ds-vfs.c index 508b9fac3..587ac5944 100644 --- a/src/platform/3ds/3ds-vfs.c +++ b/src/platform/3ds/3ds-vfs.c @@ -9,6 +9,8 @@ #include #include +#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; diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index fc97c7158..bac36fc34 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -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) diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 6245b62e0..ee2df8fef 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -18,6 +18,8 @@ #include #include +#include + #include #include #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() { diff --git a/src/platform/qt/DisplayGL.h b/src/platform/qt/DisplayGL.h index be982b006..5af24e478 100644 --- a/src/platform/qt/DisplayGL.h +++ b/src/platform/qt/DisplayGL.h @@ -16,6 +16,7 @@ #endif #endif +#include #include #include #include @@ -130,6 +131,7 @@ private: std::array, 3> m_buffers; QList m_free; QQueue m_queue; + QAtomicInt m_lagging = 0; uint32_t* m_buffer; QPainter m_painter; QMutex m_mutex; diff --git a/src/platform/qt/VideoProxy.cpp b/src/platform/qt/VideoProxy.cpp index aee19deb4..c188f5b78 100644 --- a/src/platform/qt/VideoProxy.cpp +++ b/src/platform/qt/VideoProxy.cpp @@ -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>; diff --git a/src/platform/qt/VideoProxy.h b/src/platform/qt/VideoProxy.h index 0e0f4bd5b..9376515f4 100644 --- a/src/platform/qt/VideoProxy.h +++ b/src/platform/qt/VideoProxy.h @@ -24,6 +24,7 @@ public: void attach(CoreController*); void detach(CoreController*); + void setBlocking(bool block) { m_logger.d.waitOnFlush = block; } signals: void dataAvailable(); diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index ae1b022f7..ffcee47ab 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -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) { diff --git a/src/platform/wii/CMakeLists.txt b/src/platform/wii/CMakeLists.txt index 8d1a60e87..ed27fae90 100644 --- a/src/platform/wii/CMakeLists.txt +++ b/src/platform/wii/CMakeLists.txt @@ -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 diff --git a/src/platform/wii/main.c b/src/platform/wii/main.c index dd9311de9..4a3c8b871 100644 --- a/src/platform/wii/main.c +++ b/src/platform/wii/main.c @@ -28,10 +28,15 @@ #include #include +#ifdef WIIDRC +#include +#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; } diff --git a/src/util/gui/file-select.c b/src/util/gui/file-select.c index 1d75e88b9..b8802d8cc 100644 --- a/src/util/gui/file-select.c +++ b/src/util/gui/file-select.c @@ -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);