From 16c0132e8fd44f8e5d189540927a6a98cdde7aa7 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 23 Apr 2018 22:38:21 -0700 Subject: [PATCH 01/10] Feature: Increase GIF frame resolution if needed --- src/feature/imagemagick/imagemagick-gif-encoder.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/feature/imagemagick/imagemagick-gif-encoder.c b/src/feature/imagemagick/imagemagick-gif-encoder.c index faa44d9d9..1e0ddb6e6 100644 --- a/src/feature/imagemagick/imagemagick-gif-encoder.c +++ b/src/feature/imagemagick/imagemagick-gif-encoder.c @@ -98,6 +98,10 @@ static void _magickPostVideoFrame(struct mAVStream* stream, const color_t* pixel static void _magickVideoDimensionsChanged(struct mAVStream* stream, unsigned width, unsigned height) { struct ImageMagickGIFEncoder* encoder = (struct ImageMagickGIFEncoder*) stream; + if (width * height > encoder->iwidth * encoder->iheight) { + free(encoder->frame); + encoder->frame = malloc(width * height * 4); + } encoder->iwidth = width; encoder->iheight = height; } From 420a15a84151065982c1170b0983f136e9b1cddb Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 23 Apr 2018 22:49:36 -0700 Subject: [PATCH 02/10] GB Video: Don't blank screen on SGB (fixes #1063) --- src/gb/renderers/software.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gb/renderers/software.c b/src/gb/renderers/software.c index 7fe4ad458..b707a02cc 100644 --- a/src/gb/renderers/software.c +++ b/src/gb/renderers/software.c @@ -29,8 +29,8 @@ static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* rende static void _clearScreen(struct GBVideoSoftwareRenderer* renderer) { size_t sgbOffset = 0; - if (renderer->model == GB_MODEL_SGB && renderer->sgbBorders) { - sgbOffset = renderer->outputBufferStride * 40 + 48; + if (renderer->model == GB_MODEL_SGB) { + return; } int y; for (y = 0; y < GB_VIDEO_VERTICAL_PIXELS; ++y) { From 279862261b36565995894eca7cc08061e808978f Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 23 Apr 2018 22:57:26 -0700 Subject: [PATCH 03/10] GB Video: Mark OAM dirty on reset (fixes #10620) --- src/gba/renderers/video-software.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c index 0b3df681a..8458d2e51 100644 --- a/src/gba/renderers/video-software.c +++ b/src/gba/renderers/video-software.c @@ -108,6 +108,7 @@ static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer) { softwareRenderer->winN[1] = (struct WindowN) { .control = { .priority = 1 } }; softwareRenderer->objwin = (struct WindowControl) { .priority = 2 }; softwareRenderer->winout = (struct WindowControl) { .priority = 3 }; + softwareRenderer->oamDirty = 1; softwareRenderer->oamMax = 0; softwareRenderer->mosaic = 0; From 5d0bbcd51277ff6c45192e555d644daa416ca397 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 24 Apr 2018 11:21:28 -0700 Subject: [PATCH 04/10] GB Video: Fix input iteration on SGB (fixes #1064) --- src/gb/io.c | 22 +++++++++++++++++----- src/gb/video.c | 1 + 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/gb/io.c b/src/gb/io.c index af2bba3a9..d66df3030 100644 --- a/src/gb/io.c +++ b/src/gb/io.c @@ -113,13 +113,26 @@ static void _writeSGBBits(struct GB* gb, int bits) { return; } gb->currentSgbBits = bits; - if (bits == 3) { - gb->sgbCurrentController = (gb->sgbCurrentController + 1) & gb->sgbControllers; - } if (gb->sgbBit == 128 && bits == 2) { GBVideoWriteSGBPacket(&gb->video, gb->sgbPacket); ++gb->sgbBit; } + if (gb->sgbBit > 128) { + switch (bits) { + case 1: + gb->sgbBit |= 2; + break; + case 2: + gb->sgbBit |= 4; + break; + case 3: + if (gb->sgbBit == 135 && bits == 3 && !(gb->video.sgbCommandHeader & 7)) { + gb->sgbBit &= ~6; + gb->sgbCurrentController = (gb->sgbCurrentController + 1) & gb->sgbControllers; + } + break; + } + } if (gb->sgbBit >= 128) { return; } @@ -511,8 +524,7 @@ static uint8_t _readKeys(struct GB* gb) { } switch (gb->memory.io[REG_JOYP] & 0x30) { case 0x30: - // TODO: Increment - keys = (gb->video.sgbCommandHeader >> 3) == SGB_MLT_REQ ? 0xF - gb->sgbCurrentController : 0; + keys = 0xF - gb->sgbCurrentController; break; case 0x20: keys >>= 4; diff --git a/src/gb/video.c b/src/gb/video.c index be92537f4..93fcf1767 100644 --- a/src/gb/video.c +++ b/src/gb/video.c @@ -703,6 +703,7 @@ void GBVideoWriteSGBPacket(struct GBVideo* video, uint8_t* data) { break; case SGB_MLT_REQ: video->p->sgbControllers = video->sgbPacketBuffer[1] & 0x3; + video->p->sgbCurrentController = 0; return; case SGB_MASK_EN: video->renderer->sgbRenderMode = video->sgbPacketBuffer[1] & 0x3; From 773151638842e8a91cf0f4b2661322719fc3abb9 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 24 Apr 2018 21:31:53 -0700 Subject: [PATCH 05/10] GB I/O: SGB multiplayer cleanup (fixes #1067) --- src/gb/io.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/gb/io.c b/src/gb/io.c index d66df3030..62f8e5b32 100644 --- a/src/gb/io.c +++ b/src/gb/io.c @@ -113,10 +113,6 @@ static void _writeSGBBits(struct GB* gb, int bits) { return; } gb->currentSgbBits = bits; - if (gb->sgbBit == 128 && bits == 2) { - GBVideoWriteSGBPacket(&gb->video, gb->sgbPacket); - ++gb->sgbBit; - } if (gb->sgbBit > 128) { switch (bits) { case 1: @@ -126,13 +122,17 @@ static void _writeSGBBits(struct GB* gb, int bits) { gb->sgbBit |= 4; break; case 3: - if (gb->sgbBit == 135 && bits == 3 && !(gb->video.sgbCommandHeader & 7)) { + if (gb->sgbBit == 135) { gb->sgbBit &= ~6; gb->sgbCurrentController = (gb->sgbCurrentController + 1) & gb->sgbControllers; } break; } } + if (gb->sgbBit == 128 && bits == 2) { + GBVideoWriteSGBPacket(&gb->video, gb->sgbPacket); + ++gb->sgbBit; + } if (gb->sgbBit >= 128) { return; } @@ -524,7 +524,7 @@ static uint8_t _readKeys(struct GB* gb) { } switch (gb->memory.io[REG_JOYP] & 0x30) { case 0x30: - keys = 0xF - gb->sgbCurrentController; + keys = gb->sgbCurrentController; break; case 0x20: keys >>= 4; From 88ef2e21693afac38e44222bff9931d8ea575785 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 25 Apr 2018 09:09:17 -0700 Subject: [PATCH 06/10] Qt: Fix launching with -g (fixes #1018) --- src/platform/qt/DebuggerController.cpp | 11 +++++++++-- src/platform/qt/DebuggerController.h | 2 +- src/platform/qt/GDBController.cpp | 1 - src/platform/qt/Window.cpp | 3 +++ 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/platform/qt/DebuggerController.cpp b/src/platform/qt/DebuggerController.cpp index eb27789a7..f6c7fdd36 100644 --- a/src/platform/qt/DebuggerController.cpp +++ b/src/platform/qt/DebuggerController.cpp @@ -32,6 +32,10 @@ void DebuggerController::setController(std::shared_ptr controlle connect(m_gameController.get(), &CoreController::stopping, [this]() { setController(nullptr); }); + if (m_autoattach) { + m_autoattach = false; + attach(); + } } } @@ -43,11 +47,12 @@ void DebuggerController::attach() { attachInternal(); m_gameController->setDebugger(m_debugger); mDebuggerEnter(m_debugger, DEBUGGER_ENTER_ATTACHED, 0); + } else { + m_autoattach = true; } } void DebuggerController::detach() { - QObject::disconnect(m_autoattach); if (!isAttached()) { return; } @@ -55,6 +60,8 @@ void DebuggerController::detach() { CoreController::Interrupter interrupter(m_gameController); shutdownInternal(); m_gameController->setDebugger(nullptr); + } else { + m_autoattach = false; } } @@ -67,7 +74,7 @@ void DebuggerController::breakInto() { } void DebuggerController::shutdown() { - QObject::disconnect(m_autoattach); + m_autoattach = false; if (!isAttached()) { return; } diff --git a/src/platform/qt/DebuggerController.h b/src/platform/qt/DebuggerController.h index 650d533d3..45ac434da 100644 --- a/src/platform/qt/DebuggerController.h +++ b/src/platform/qt/DebuggerController.h @@ -39,7 +39,7 @@ protected: std::shared_ptr m_gameController; private: - QMetaObject::Connection m_autoattach; + bool m_autoattach = false; }; } diff --git a/src/platform/qt/GDBController.cpp b/src/platform/qt/GDBController.cpp index ade67e08c..d2e9523b7 100644 --- a/src/platform/qt/GDBController.cpp +++ b/src/platform/qt/GDBController.cpp @@ -34,7 +34,6 @@ void GDBController::setBindAddress(uint32_t bindAddress) { } void GDBController::listen() { - CoreController::Interrupter interrupter(m_gameController); if (!isAttached()) { attach(); } diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index c5d6adf4b..a6012b173 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -175,6 +175,9 @@ void Window::argumentsPassed(mArguments* args) { if (args->debuggerType == DEBUGGER_GDB) { if (!m_gdbController) { m_gdbController = new GDBController(this); + if (m_controller) { + m_gdbController->setController(m_controller); + } m_gdbController->listen(); } } From b89b3b6d138383d235729f728149eaf3f69f01eb Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 25 Apr 2018 21:19:04 -0700 Subject: [PATCH 07/10] GB Video: Implement SGB ATTR_LINE --- src/gb/renderers/software.c | 31 +++++++++++++++++++++++++++++++ src/gb/video.c | 1 + 2 files changed, 32 insertions(+) diff --git a/src/gb/renderers/software.c b/src/gb/renderers/software.c index b707a02cc..02fa0a330 100644 --- a/src/gb/renderers/software.c +++ b/src/gb/renderers/software.c @@ -135,6 +135,30 @@ static void _parseAttrBlock(struct GBVideoSoftwareRenderer* renderer, int start) } } +static void _parseAttrLine(struct GBVideoSoftwareRenderer* renderer, int start) { + uint8_t byte = renderer->sgbPacket[start]; + unsigned line = byte & 0x1F; + int pal = (byte >> 5) & 3; + + if (byte & 0x80) { + if (line > GB_VIDEO_VERTICAL_PIXELS / 8) { + return; + } + int x; + for (x = 0; x < GB_VIDEO_HORIZONTAL_PIXELS / 8; ++x) { + _setAttribute(renderer->d.sgbAttributes, x, line, pal); + } + } else { + if (line > GB_VIDEO_HORIZONTAL_PIXELS / 8) { + return; + } + int y; + for (y = 0; y < GB_VIDEO_VERTICAL_PIXELS / 8; ++y) { + _setAttribute(renderer->d.sgbAttributes, line, y, pal); + } + } +} + static bool _inWindow(struct GBVideoSoftwareRenderer* renderer) { return GBRegisterLCDCIsWindow(renderer->lcdc) && GB_VIDEO_HORIZONTAL_PIXELS + 7 > renderer->wx; } @@ -287,6 +311,13 @@ static void GBVideoSoftwareRendererWriteSGBPacket(struct GBVideoRenderer* render _parseAttrBlock(softwareRenderer, i); } break; + case SGB_ATTR_LIN: + sets = softwareRenderer->sgbPacket[1]; + i = 2; + for (; i < (softwareRenderer->sgbCommandHeader & 7) << 4 && sets; ++i, --sets) { + _parseAttrLine(softwareRenderer, i); + } + break; case SGB_ATTR_DIV: pAfter = softwareRenderer->sgbPacket[1] & 3; pBefore = (softwareRenderer->sgbPacket[1] >> 2) & 3; diff --git a/src/gb/video.c b/src/gb/video.c index 93fcf1767..bdffc7923 100644 --- a/src/gb/video.c +++ b/src/gb/video.c @@ -694,6 +694,7 @@ void GBVideoWriteSGBPacket(struct GBVideo* video, uint8_t* data) { case SGB_ATTR_BLK: case SGB_ATTR_DIV: case SGB_ATTR_CHR: + case SGB_ATTR_LIN: case SGB_PAL_TRN: case SGB_ATRC_EN: case SGB_CHR_TRN: From 1807b66bd60c4dadd91190833ca0a81e8d4d49bd Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 26 Apr 2018 06:47:09 -0700 Subject: [PATCH 08/10] Revert "GBA: Disable cheat hooks" This reverts commit 2e55bd098ac97fcb203634f5c65651695de6eb38. --- src/gba/cheats.c | 4 ++-- src/gba/gba.c | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/gba/cheats.c b/src/gba/cheats.c index ec32924f3..19e80b0b1 100644 --- a/src/gba/cheats.c +++ b/src/gba/cheats.c @@ -21,7 +21,7 @@ static void _addBreakpoint(struct mCheatDevice* device, struct GBACheatSet* chea if (cheats->hook->reentries > 1) { return; } - // TODO: Put back hooks + GBASetBreakpoint(device->p->board, &device->d, cheats->hook->address, cheats->hook->mode, &cheats->hook->patchedOpcode); } static void _removeBreakpoint(struct mCheatDevice* device, struct GBACheatSet* cheats) { @@ -32,7 +32,7 @@ static void _removeBreakpoint(struct mCheatDevice* device, struct GBACheatSet* c if (cheats->hook->reentries > 0) { return; } - // TODO: Put back hooks + GBAClearBreakpoint(device->p->board, cheats->hook->address, cheats->hook->mode, cheats->hook->patchedOpcode); } static void _patchROM(struct mCheatDevice* device, struct GBACheatSet* cheats) { diff --git a/src/gba/gba.c b/src/gba/gba.c index 1390fd31c..f14c639c9 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -805,7 +805,9 @@ void GBAFrameEnded(struct GBA* gba) { size_t i; for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) { struct GBACheatSet* cheats = (struct GBACheatSet*) *mCheatSetsGetPointer(&device->cheats, i); - mCheatRefresh(device, &cheats->d); + if (!cheats->hook) { + mCheatRefresh(device, &cheats->d); + } } } From 8ea524d9e61334d7d94dd12b9f8a1cd84f0b67ec Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 26 Apr 2018 09:07:57 -0700 Subject: [PATCH 09/10] CMake: Fix debian libelf package name (fixes #1070) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d854567e..9a92afc6b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -661,7 +661,7 @@ if(USE_ELF) list(APPEND FEATURES ELF) include_directories(AFTER ${LIBELF_INCLUDE_DIRS}) list(APPEND DEPENDENCY_LIB ${LIBELF_LIBRARIES}) - set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libelfg0") + set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libelf1") endif() if(ENABLE_SCRIPTING) From 67a135e5e7a5d6366f516972f25e7c71bff91447 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 26 Apr 2018 18:38:02 -0700 Subject: [PATCH 10/10] Qt: Add layer placement features --- CHANGES | 1 + include/mgba/core/core.h | 1 + include/mgba/internal/gb/renderers/software.h | 7 ++ .../internal/gba/renderers/video-software.h | 4 + src/gb/core.c | 29 ++++++- src/gb/renderers/software.c | 39 +++++---- src/gba/core.c | 30 ++++++- src/gba/renderers/software-mode0.c | 4 +- src/gba/renderers/software-obj.c | 3 +- src/gba/renderers/video-software.c | 10 ++- src/platform/qt/CMakeLists.txt | 2 + src/platform/qt/PlacementControl.cpp | 76 ++++++++++++++++ src/platform/qt/PlacementControl.h | 34 ++++++++ src/platform/qt/PlacementControl.ui | 87 +++++++++++++++++++ src/platform/qt/Window.cpp | 6 ++ 15 files changed, 309 insertions(+), 24 deletions(-) create mode 100644 src/platform/qt/PlacementControl.cpp create mode 100644 src/platform/qt/PlacementControl.h create mode 100644 src/platform/qt/PlacementControl.ui diff --git a/CHANGES b/CHANGES index c02eac1e2..cca0ff244 100644 --- a/CHANGES +++ b/CHANGES @@ -49,6 +49,7 @@ Misc: - GBA Memory: 64 MiB GBA Video cartridge support - PSP2: Use system enter key by default - 3DS: Remove deprecated CSND interface + - Qt: Options to mess around with layer placement 0.6.3: (2017-04-14) Bugfixes: diff --git a/include/mgba/core/core.h b/include/mgba/core/core.h index 607a15a4e..96a9324f2 100644 --- a/include/mgba/core/core.h +++ b/include/mgba/core/core.h @@ -153,6 +153,7 @@ struct mCore { size_t (*listAudioChannels)(const struct mCore*, const struct mCoreChannelInfo**); void (*enableVideoLayer)(struct mCore*, size_t id, bool enable); void (*enableAudioChannel)(struct mCore*, size_t id, bool enable); + void (*adjustVideoLayer)(struct mCore*, size_t id, int32_t x, int32_t y); #ifndef MINIMAL_CORE void (*startVideoLog)(struct mCore*, struct mVideoLogContext*); diff --git a/include/mgba/internal/gb/renderers/software.h b/include/mgba/internal/gb/renderers/software.h index 159d643b8..3b8f7a210 100644 --- a/include/mgba/internal/gb/renderers/software.h +++ b/include/mgba/internal/gb/renderers/software.h @@ -38,6 +38,13 @@ struct GBVideoSoftwareRenderer { GBRegisterLCDC lcdc; enum GBModel model; + int16_t objOffsetX; + int16_t objOffsetY; + int16_t offsetScx; + int16_t offsetScy; + int16_t offsetWx; + int16_t offsetWy; + int sgbTransfer; uint8_t sgbPacket[128]; uint8_t sgbCommandHeader; diff --git a/include/mgba/internal/gba/renderers/video-software.h b/include/mgba/internal/gba/renderers/video-software.h index 77aaf63fb..d7831c5ac 100644 --- a/include/mgba/internal/gba/renderers/video-software.h +++ b/include/mgba/internal/gba/renderers/video-software.h @@ -44,6 +44,8 @@ struct GBAVideoSoftwareBackground { int32_t sy; int yCache; uint16_t mapCache[64]; + int32_t offsetX; + int32_t offsetY; }; enum BlendEffect { @@ -159,6 +161,8 @@ struct GBAVideoSoftwareRenderer { int oamDirty; int oamMax; struct GBAVideoSoftwareSprite sprites[128]; + int16_t objOffsetX; + int16_t objOffsetY; uint32_t scanlineDirty[5]; uint16_t nextIo[REG_SOUND1CNT_LO]; diff --git a/src/gb/core.c b/src/gb/core.c index fe3e423d1..deb73ea99 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -791,13 +791,17 @@ static bool _GBCoreSavedataRestore(struct mCore* core, const void* sram, size_t static size_t _GBCoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) { UNUSED(core); - *info = _GBVideoLayers; + if (info) { + *info = _GBVideoLayers; + } return sizeof(_GBVideoLayers) / sizeof(*_GBVideoLayers); } static size_t _GBCoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) { UNUSED(core); - *info = _GBAudioChannels; + if (info) { + *info = _GBAudioChannels; + } return sizeof(_GBAudioChannels) / sizeof(*_GBAudioChannels); } @@ -832,6 +836,26 @@ static void _GBCoreEnableAudioChannel(struct mCore* core, size_t id, bool enable } } +static void _GBCoreAdjustVideoLayer(struct mCore* core, size_t id, int32_t x, int32_t y) { + struct GBCore* gbcore = (struct GBCore*) core; + switch (id) { + case 0: + gbcore->renderer.offsetScx = x; + gbcore->renderer.offsetScy = y; + break; + case 1: + gbcore->renderer.offsetWx = x; + gbcore->renderer.offsetWy = y; + break; + case 2: + gbcore->renderer.objOffsetX = x; + gbcore->renderer.objOffsetY = y; + break; + default: + return; + } +} + #ifndef MINIMAL_CORE static void _GBCoreStartVideoLog(struct mCore* core, struct mVideoLogContext* context) { struct GBCore* gbcore = (struct GBCore*) core; @@ -934,6 +958,7 @@ struct mCore* GBCoreCreate(void) { core->listAudioChannels = _GBCoreListAudioChannels; core->enableVideoLayer = _GBCoreEnableVideoLayer; core->enableAudioChannel = _GBCoreEnableAudioChannel; + core->adjustVideoLayer = _GBCoreAdjustVideoLayer; #ifndef MINIMAL_CORE core->startVideoLog = _GBCoreStartVideoLog; core->endVideoLog = _GBCoreEndVideoLog; diff --git a/src/gb/renderers/software.c b/src/gb/renderers/software.c index 02fa0a330..f63939113 100644 --- a/src/gb/renderers/software.c +++ b/src/gb/renderers/software.c @@ -198,6 +198,13 @@ static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum G softwareRenderer->sgbTransfer = 0; softwareRenderer->sgbCommandHeader = 0; softwareRenderer->sgbBorders = sgbBorders; + softwareRenderer->objOffsetX = 0; + softwareRenderer->objOffsetY = 0; + softwareRenderer->offsetScx = 0; + softwareRenderer->offsetScy = 0; + softwareRenderer->offsetWx = 0; + softwareRenderer->offsetWy = 0; + int i; for (i = 0; i < 64; ++i) { softwareRenderer->lookup[i] = i; @@ -471,7 +478,7 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i int wy = softwareRenderer->wy + softwareRenderer->currentWy; if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && wy <= y && endX >= softwareRenderer->wx - 7) { if (softwareRenderer->wx - 7 > 0 && !softwareRenderer->d.disableBG) { - GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, softwareRenderer->wx - 7, softwareRenderer->scx, softwareRenderer->scy + y); + GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, softwareRenderer->wx - 7, softwareRenderer->scx - softwareRenderer->offsetScx, softwareRenderer->scy + y - softwareRenderer->offsetScy); } maps = &softwareRenderer->d.vram[GB_BASE_MAP]; @@ -479,10 +486,10 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i maps += GB_SIZE_MAP; } if (!softwareRenderer->d.disableWIN) { - GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, softwareRenderer->wx - 7, endX, 7 - softwareRenderer->wx, y - wy); + GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, softwareRenderer->wx - 7, endX, 7 - softwareRenderer->wx - softwareRenderer->offsetWx, y - wy - softwareRenderer->offsetWy); } } else if (!softwareRenderer->d.disableBG) { - GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, endX, softwareRenderer->scx, softwareRenderer->scy + y); + GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, endX, softwareRenderer->scx - softwareRenderer->offsetScx, softwareRenderer->scy + y - softwareRenderer->offsetScy); } } else if (!softwareRenderer->d.disableBG) { memset(&softwareRenderer->row[startX], 0, endX - startX); @@ -778,15 +785,16 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer } static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int startX, int endX, int y) { - int ix = obj->x - 8; + int objX = obj->x + renderer->objOffsetX; + int ix = objX - 8; if (endX < ix || startX >= ix + 8) { return; } - if (obj->x < endX) { - endX = obj->x; + if (objX < endX) { + endX = objX; } - if (obj->x - 8 > startX) { - startX = obj->x - 8; + if (objX - 8 > startX) { + startX = objX - 8; } if (startX < 0) { startX = 0; @@ -794,14 +802,15 @@ static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* rende uint8_t* data = renderer->d.vram; int tileOffset = 0; int bottomY; + int objY = obj->y + renderer->objOffsetY; if (GBObjAttributesIsYFlip(obj->attr)) { - bottomY = 7 - ((y - obj->y - 16) & 7); - if (GBRegisterLCDCIsObjSize(renderer->lcdc) && y - obj->y < -8) { + bottomY = 7 - ((y - objY - 16) & 7); + if (GBRegisterLCDCIsObjSize(renderer->lcdc) && y - objY < -8) { ++tileOffset; } } else { - bottomY = (y - obj->y - 16) & 7; - if (GBRegisterLCDCIsObjSize(renderer->lcdc) && y - obj->y >= -8) { + bottomY = (y - objY - 16) & 7; + if (GBRegisterLCDCIsObjSize(renderer->lcdc) && y - objY >= -8) { ++tileOffset; } } @@ -825,12 +834,12 @@ static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* rende } int bottomX; int x = startX; - if ((x - obj->x) & 7) { + if ((x - objX) & 7) { for (; x < endX; ++x) { if (GBObjAttributesIsXFlip(obj->attr)) { - bottomX = (x - obj->x) & 7; + bottomX = (x - objX) & 7; } else { - bottomX = 7 - ((x - obj->x) & 7); + bottomX = 7 - ((x - objX) & 7); } int objTile = obj->tile + tileOffset; uint8_t tileDataLower = data[(objTile * 8 + bottomY) * 2]; diff --git a/src/gba/core.c b/src/gba/core.c index 0abd5ec6e..263399a12 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -795,13 +795,17 @@ static bool _GBACoreSavedataRestore(struct mCore* core, const void* sram, size_t static size_t _GBACoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) { UNUSED(core); - *info = _GBAVideoLayers; + if (info) { + *info = _GBAVideoLayers; + } return sizeof(_GBAVideoLayers) / sizeof(*_GBAVideoLayers); } static size_t _GBACoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) { UNUSED(core); - *info = _GBAAudioChannels; + if (info) { + *info = _GBAAudioChannels; + } return sizeof(_GBAAudioChannels) / sizeof(*_GBAAudioChannels); } @@ -841,6 +845,27 @@ static void _GBACoreEnableAudioChannel(struct mCore* core, size_t id, bool enabl } } +static void _GBACoreAdjustVideoLayer(struct mCore* core, size_t id, int32_t x, int32_t y) { + struct GBACore* gbacore = (struct GBACore*) core; + switch (id) { + case 0: + case 1: + case 2: + case 3: + gbacore->renderer.bg[id].offsetX = x; + gbacore->renderer.bg[id].offsetY = y; + break; + case 4: + gbacore->renderer.objOffsetX = x; + gbacore->renderer.objOffsetY = y; + gbacore->renderer.oamDirty = 1; + break; + default: + return; + } + memset(gbacore->renderer.scanlineDirty, 0xFFFFFFFF, sizeof(gbacore->renderer.scanlineDirty)); +} + #ifndef MINIMAL_CORE static void _GBACoreStartVideoLog(struct mCore* core, struct mVideoLogContext* context) { struct GBACore* gbacore = (struct GBACore*) core; @@ -946,6 +971,7 @@ struct mCore* GBACoreCreate(void) { core->listAudioChannels = _GBACoreListAudioChannels; core->enableVideoLayer = _GBACoreEnableVideoLayer; core->enableAudioChannel = _GBACoreEnableAudioChannel; + core->adjustVideoLayer = _GBACoreAdjustVideoLayer; #ifndef MINIMAL_CORE core->startVideoLog = _GBACoreStartVideoLog; core->endVideoLog = _GBACoreEndVideoLog; diff --git a/src/gba/renderers/software-mode0.c b/src/gba/renderers/software-mode0.c index f0925528a..944232068 100644 --- a/src/gba/renderers/software-mode0.c +++ b/src/gba/renderers/software-mode0.c @@ -446,13 +446,13 @@ } void GBAVideoSoftwareRendererDrawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) { - int inX = (renderer->start + background->x) & 0x1FF; + int inX = (renderer->start + background->x - background->offsetX) & 0x1FF; int length = renderer->end - renderer->start; if (background->mosaic) { int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1; y -= y % mosaicV; } - int inY = y + background->y; + int inY = y + background->y - background->offsetY; uint16_t mapData; unsigned yBase = inY & 0xF8; diff --git a/src/gba/renderers/software-obj.c b/src/gba/renderers/software-obj.c index 397e84f48..c7080bf5f 100644 --- a/src/gba/renderers/software-obj.c +++ b/src/gba/renderers/software-obj.c @@ -147,6 +147,7 @@ int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* re } int32_t x = (uint32_t) GBAObjAttributesBGetX(sprite->b) << 23; x >>= 23; + x += renderer->objOffsetX; uint16_t* vramBase = &renderer->d.vram[BASE_TILE >> 1]; bool align = GBAObjAttributesAIs256Color(sprite->a) && !GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt); unsigned charBase = (GBAObjAttributesCGetTile(sprite->c) & ~align) * 0x20; @@ -185,7 +186,7 @@ int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* re } } - int inY = y - (int) GBAObjAttributesAGetY(sprite->a); + int inY = y - ((int) GBAObjAttributesAGetY(sprite->a) + renderer->objOffsetY); int stride = GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? (width >> !GBAObjAttributesAIs256Color(sprite->a)) : 0x80; uint32_t current; diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c index 8458d2e51..c0387ccb4 100644 --- a/src/gba/renderers/video-software.c +++ b/src/gba/renderers/video-software.c @@ -114,6 +114,9 @@ static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer) { softwareRenderer->mosaic = 0; softwareRenderer->nextY = 0; + softwareRenderer->objOffsetX = 0; + softwareRenderer->objOffsetY = 0; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); memset(softwareRenderer->cache, 0, sizeof(softwareRenderer->cache)); memset(softwareRenderer->nextIo, 0, sizeof(softwareRenderer->nextIo)); @@ -142,6 +145,8 @@ static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer) { bg->sx = 0; bg->sy = 0; bg->yCache = -1; + bg->offsetX = 0; + bg->offsetY = 0; } } @@ -507,8 +512,9 @@ static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer) { height <<= GBAObjAttributesAGetDoubleSize(obj.a); } if (GBAObjAttributesAGetY(obj.a) < VIDEO_VERTICAL_PIXELS || GBAObjAttributesAGetY(obj.a) + height >= VIDEO_VERTICAL_TOTAL_PIXELS) { - renderer->sprites[oamMax].y = GBAObjAttributesAGetY(obj.a); - renderer->sprites[oamMax].endY = GBAObjAttributesAGetY(obj.a) + height; + int y = GBAObjAttributesAGetY(obj.a) + renderer->objOffsetY; + renderer->sprites[oamMax].y = y; + renderer->sprites[oamMax].endY = y + height; renderer->sprites[oamMax].obj = obj; ++oamMax; } diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 1c2cc919c..eccce100c 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -102,6 +102,7 @@ set(SOURCE_FILES ObjView.cpp OverrideView.cpp PaletteView.cpp + PlacementControl.cpp PrinterView.cpp RegisterView.cpp ROMInfo.cpp @@ -135,6 +136,7 @@ set(UI_FILES ObjView.ui OverrideView.ui PaletteView.ui + PlacementControl.ui PrinterView.ui ROMInfo.ui SensorView.ui diff --git a/src/platform/qt/PlacementControl.cpp b/src/platform/qt/PlacementControl.cpp new file mode 100644 index 000000000..90f1f7b32 --- /dev/null +++ b/src/platform/qt/PlacementControl.cpp @@ -0,0 +1,76 @@ +/* Copyright (c) 2013-2018 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 "PlacementControl.h" + +#include "CoreController.h" + +#include + +#include + +using namespace QGBA; + +PlacementControl::PlacementControl(std::shared_ptr controller, QWidget* parent) + : QDialog(parent) + , m_controller(controller) +{ + m_ui.setupUi(this); + + connect(m_ui.offsetX, static_cast(&QSpinBox::valueChanged), [this](int x) { + adjustLayer(-1, x, m_ui.offsetY->value()); + }); + + connect(m_ui.offsetY, static_cast(&QSpinBox::valueChanged), [this](int y) { + adjustLayer(-1, m_ui.offsetX->value(), y); + }); + + QGridLayout* grid = static_cast(layout()); + CoreController::Interrupter interrupter(m_controller); + const mCoreChannelInfo* info; + size_t nVideo = m_controller->thread()->core->listVideoLayers(m_controller->thread()->core, &info); + for (size_t i = 0; i < nVideo; ++i) { + QSpinBox* offsetX = new QSpinBox; + QSpinBox* offsetY = new QSpinBox; + + offsetX->setWrapping(true); + offsetX->setMaximum(127); + offsetX->setMinimum(-128); + offsetX->setAccelerated(true); + + offsetY->setWrapping(true); + offsetY->setMaximum(127); + offsetY->setMinimum(-128); + offsetY->setAccelerated(true); + + m_layers.append(qMakePair(offsetX, offsetY)); + int row = grid->rowCount(); + grid->addWidget(new QLabel(QString(info[i].visibleName)), row, 0, Qt::AlignRight); + grid->addWidget(offsetX, row, 1); + grid->addWidget(offsetY, row, 2); + + connect(offsetX, static_cast(&QSpinBox::valueChanged), [this, i, offsetY](int x) { + adjustLayer(i, x, offsetY->value()); + }); + + connect(offsetY, static_cast(&QSpinBox::valueChanged), [this, i, offsetX](int y) { + adjustLayer(i, offsetX->value(), y); + }); + } +} + +void PlacementControl::adjustLayer(int layer, int32_t x, int32_t y) { + CoreController::Interrupter interrupter(m_controller); + mCore* core = m_controller->thread()->core; + size_t nVideo = core->listVideoLayers(core, nullptr); + + if (layer < 0) { + for (size_t i = 0; i < nVideo; ++i) { + core->adjustVideoLayer(core, i, x + m_layers[i].first->value(), y + m_layers[i].second->value()); + } + } else if ((size_t) layer < nVideo) { + core->adjustVideoLayer(core, layer, x + m_ui.offsetX->value(), y + m_ui.offsetY->value()); + } +} diff --git a/src/platform/qt/PlacementControl.h b/src/platform/qt/PlacementControl.h new file mode 100644 index 000000000..06abf0e9a --- /dev/null +++ b/src/platform/qt/PlacementControl.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2013-2018 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/. */ +#pragma once + +#include +#include + +#include + +#include "ui_PlacementControl.h" + +namespace QGBA { + +class CoreController; + +class PlacementControl : public QDialog { +Q_OBJECT + +public: + PlacementControl(std::shared_ptr, QWidget* parent = nullptr); + +private: + void adjustLayer(int layer, int32_t x, int32_t y); + + std::shared_ptr m_controller; + QList> m_layers; + + Ui::PlacementControl m_ui; +}; + +} diff --git a/src/platform/qt/PlacementControl.ui b/src/platform/qt/PlacementControl.ui new file mode 100644 index 000000000..1460e7208 --- /dev/null +++ b/src/platform/qt/PlacementControl.ui @@ -0,0 +1,87 @@ + + + PlacementControl + + + + 0 + 0 + 202 + 72 + + + + + 0 + 0 + + + + Adjust placement + + + + + + All + + + + + + + true + + + true + + + -128 + + + 127 + + + + + + + true + + + true + + + -128 + + + 127 + + + + + + + Offset + + + + + + + X + + + + + + + Y + + + + + + + + diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index a6012b173..824166f73 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -42,6 +42,7 @@ #include "OverrideView.h" #include "ObjView.h" #include "PaletteView.h" +#include "PlacementControl.h" #include "PrinterView.h" #include "ROMInfo.h" #include "SensorView.h" @@ -1445,6 +1446,11 @@ void Window::setupMenu(QMenuBar* menubar) { m_audioChannels = avMenu->addMenu(tr("Audio channels")); m_shortcutController->addMenu(m_audioChannels, avMenu); + QAction* placementControl = new QAction(tr("Adjust layer placement..."), avMenu); + connect(placementControl, &QAction::triggered, openControllerTView()); + m_gameActions.append(placementControl); + addControlledAction(avMenu, placementControl, "placementControl"); + QMenu* toolsMenu = menubar->addMenu(tr("&Tools")); m_shortcutController->addMenu(toolsMenu); QAction* viewLogs = new QAction(tr("View &logs..."), toolsMenu);