diff --git a/CHANGES b/CHANGES index cd71fa432..06f30ba0d 100644 --- a/CHANGES +++ b/CHANGES @@ -38,6 +38,8 @@ Features: - e-Reader card scanning - WebP and APNG recording - Separate overrides for GBC games that can also run on SGB or regular GB + - Game Boy Player features can be enabled by default for all compatible games + - Frame viewer support for Game Boy - Mute option in homebrew ports - Status indicators for fast-forward and mute in homebrew ports - Read-only support for MBC6 flash memory @@ -46,7 +48,7 @@ Features: - Command scripts for CLI debugger (by ahigerd) - ARM disassembler now resolves addresses to symbol names - Add Game Boy Player feature support to ports - - Individual window types can now be toggled in GBA debugging views + - Individual window types can now be toggled in debugging views Emulation fixes: - ARM: Fix ALU reading PC after shifting - ARM: Fix STR storing PC after address calculation @@ -72,6 +74,7 @@ Emulation fixes: - GBA Memory: Stall on VRAM access in mode 2 (fixes mgba.io/i/190) - GBA Memory: Improve robustness of Matrix memory support - GBA Memory: Mark Famicom Mini games 22 through 28 as non-mirroring + - GBA Memory: Return correct byte for odd ROM open bus addresses - GBA SIO: Fix copying Normal mode transfer values - GBA SIO: Fix Normal mode being totally broken (fixes mgba.io/i/1800) - GBA SIO: Fix deseralizing SIO registers @@ -79,6 +82,7 @@ Emulation fixes: - GBA Video: Fix Hblank timing - GBA Video: Implement green swap (fixes mgba.io/i/1609) - GBA Video: Emulate sprite cycle limits in OpenGL renderer (fixes mgba.io/i/1635) + - GBA Video: Fix OBJWIN erratic rendering in OpenGL renderer - SM83: Emulate HALT bug Other fixes: - 3DS: Fix thread cleanup @@ -88,8 +92,10 @@ Other fixes: - Core: Fix threading improperly setting paused state while interrupted - 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 - 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) diff --git a/include/mgba/core/interface.h b/include/mgba/core/interface.h index 0ca3cd7ce..596c76433 100644 --- a/include/mgba/core/interface.h +++ b/include/mgba/core/interface.h @@ -41,16 +41,25 @@ typedef uint32_t color_t; #define M_COLOR_GREEN 0x0000FF00 #define M_COLOR_BLUE 0x00FF0000 #define M_COLOR_ALPHA 0xFF000000 +#define M_COLOR_WHITE 0x00FFFFFF + +#define M_RGB8_TO_NATIVE(X) (((X) & 0x00FF00) | (((X) & 0x0000FF) << 16) | (((X) & 0xFF0000) >> 16)) #elif defined(COLOR_5_6_5) #define M_COLOR_RED 0x001F #define M_COLOR_GREEN 0x07E0 #define M_COLOR_BLUE 0xF800 #define M_COLOR_ALPHA 0x0000 +#define M_COLOR_WHITE 0xFFDF + +#define M_RGB8_TO_NATIVE(X) ((((X) & 0xF8) << 8) | (((X) & 0xFC00) >> 5) | (((X) & 0xF80000) >> 19)) #else #define M_COLOR_RED 0x001F #define M_COLOR_GREEN 0x03E0 #define M_COLOR_BLUE 0x7C00 #define M_COLOR_ALPHA 0x1000 +#define M_COLOR_WHITE 0x7FFF + +#define M_RGB8_TO_NATIVE(X) M_RGB8_TO_BGR5(X) #endif #ifndef PYCPARSE @@ -70,6 +79,68 @@ static inline color_t mColorFrom555(uint16_t value) { #endif return color; } + +ATTRIBUTE_UNUSED static unsigned mColorMix5Bit(int weightA, unsigned colorA, int weightB, unsigned colorB) { + unsigned c = 0; + unsigned a, b; +#ifdef COLOR_16_BIT +#ifdef COLOR_5_6_5 + a = colorA & 0xF81F; + b = colorB & 0xF81F; + a |= (colorA & 0x7C0) << 16; + b |= (colorB & 0x7C0) << 16; + c = ((a * weightA + b * weightB) / 16); + if (c & 0x08000000) { + c = (c & ~0x0FC00000) | 0x07C00000; + } + if (c & 0x0020) { + c = (c & ~0x003F) | 0x001F; + } + if (c & 0x10000) { + c = (c & ~0x1F800) | 0xF800; + } + c = (c & 0xF81F) | ((c >> 16) & 0x07C0); +#else + a = colorA & 0x7C1F; + b = colorB & 0x7C1F; + a |= (colorA & 0x3E0) << 16; + b |= (colorB & 0x3E0) << 16; + c = ((a * weightA + b * weightB) / 16); + if (c & 0x04000000) { + c = (c & ~0x07E00000) | 0x03E00000; + } + if (c & 0x0020) { + c = (c & ~0x003F) | 0x001F; + } + if (c & 0x8000) { + c = (c & ~0xF800) | 0x7C00; + } + c = (c & 0x7C1F) | ((c >> 16) & 0x03E0); +#endif +#else + a = colorA & 0xFF; + b = colorB & 0xFF; + c |= ((a * weightA + b * weightB) / 16) & 0x1FF; + if (c & 0x00000100) { + c = 0x000000FF; + } + + a = colorA & 0xFF00; + b = colorB & 0xFF00; + c |= ((a * weightA + b * weightB) / 16) & 0x1FF00; + if (c & 0x00010000) { + c = (c & 0x000000FF) | 0x0000FF00; + } + + a = colorA & 0xFF0000; + b = colorB & 0xFF0000; + c |= ((a * weightA + b * weightB) / 16) & 0x1FF0000; + if (c & 0x01000000) { + c = (c & 0x0000FFFF) | 0x00FF0000; + } +#endif + return c; +} #endif struct blip_t; diff --git a/include/mgba/debugger/debugger.h b/include/mgba/debugger/debugger.h index f3404c02b..2adfa117c 100644 --- a/include/mgba/debugger/debugger.h +++ b/include/mgba/debugger/debugger.h @@ -38,7 +38,8 @@ enum mWatchpointType { WATCHPOINT_WRITE = 1, WATCHPOINT_READ = 2, WATCHPOINT_RW = 3, - WATCHPOINT_WRITE_CHANGE = 4, + WATCHPOINT_CHANGE = 4, + WATCHPOINT_WRITE_CHANGE = 5, }; enum mBreakpointType { diff --git a/include/mgba/gb/interface.h b/include/mgba/gb/interface.h index e963bd563..20eccd38e 100644 --- a/include/mgba/gb/interface.h +++ b/include/mgba/gb/interface.h @@ -10,6 +10,8 @@ CXX_GUARD_START +#include + enum GBModel { GB_MODEL_AUTODETECT = 0xFF, GB_MODEL_DMG = 0x00, diff --git a/include/mgba/internal/gb/renderers/software.h b/include/mgba/internal/gb/renderers/software.h index 11e107f6d..57942a7dc 100644 --- a/include/mgba/internal/gb/renderers/software.h +++ b/include/mgba/internal/gb/renderers/software.h @@ -14,16 +14,22 @@ CXX_GUARD_START #include #include +struct GBVideoRendererSprite { + struct GBObj obj; + int8_t index; +}; + struct GBVideoSoftwareRenderer { struct GBVideoRenderer d; color_t* outputBuffer; int outputBufferStride; - uint8_t row[GB_VIDEO_HORIZONTAL_PIXELS + 8]; + // TODO: Implement the pixel FIFO + uint16_t row[GB_VIDEO_HORIZONTAL_PIXELS + 8]; - color_t palette[128]; - uint8_t lookup[64]; + color_t palette[192]; + uint8_t lookup[192]; uint32_t* temporaryBuffer; @@ -40,7 +46,7 @@ struct GBVideoSoftwareRenderer { GBRegisterLCDC lcdc; enum GBModel model; - struct GBObj obj[10]; + struct GBVideoRendererSprite obj[GB_VIDEO_MAX_LINE_OBJ]; int objMax; int16_t objOffsetX; @@ -54,6 +60,8 @@ struct GBVideoSoftwareRenderer { uint8_t sgbPacket[128]; uint8_t sgbCommandHeader; bool sgbBorders; + + uint8_t lastHighlightAmount; }; void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer*); diff --git a/include/mgba/internal/gb/serialize.h b/include/mgba/internal/gb/serialize.h index c7d1532be..b3cde1661 100644 --- a/include/mgba/internal/gb/serialize.h +++ b/include/mgba/internal/gb/serialize.h @@ -437,6 +437,9 @@ struct GBSerializedState { bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state); void GBSerialize(struct GB* gb, struct GBSerializedState* state); +void GBSGBSerialize(struct GB* gb, struct GBSerializedState* state); +void GBSGBDeserialize(struct GB* gb, const struct GBSerializedState* state); + CXX_GUARD_END #endif diff --git a/include/mgba/internal/gb/video.h b/include/mgba/internal/gb/video.h index cd5a1e8d1..da83ebc2a 100644 --- a/include/mgba/internal/gb/video.h +++ b/include/mgba/internal/gb/video.h @@ -31,6 +31,9 @@ enum { GB_VIDEO_TOTAL_LENGTH = 70224, + GB_VIDEO_MAX_OBJ = 40, + GB_VIDEO_MAX_LINE_OBJ = 10, + GB_BASE_MAP = 0x1800, GB_SIZE_MAP = 0x0400, @@ -63,8 +66,8 @@ struct GBObj { }; union GBOAM { - struct GBObj obj[40]; - uint8_t raw[160]; + struct GBObj obj[GB_VIDEO_MAX_OBJ]; + uint8_t raw[GB_VIDEO_MAX_OBJ * 4]; }; struct mCacheSet; @@ -99,6 +102,12 @@ struct GBVideoRenderer { bool disableBG; bool disableOBJ; bool disableWIN; + + bool highlightBG; + bool highlightOBJ[GB_VIDEO_MAX_OBJ]; + bool highlightWIN; + color_t highlightColor; + uint8_t highlightAmount; }; DECL_BITFIELD(GBRegisterLCDC, uint8_t); diff --git a/include/mgba/internal/gba/renderers/video-software.h b/include/mgba/internal/gba/renderers/video-software.h index ac0fd327f..cdc4f37bd 100644 --- a/include/mgba/internal/gba/renderers/video-software.h +++ b/include/mgba/internal/gba/renderers/video-software.h @@ -52,15 +52,6 @@ struct GBAVideoSoftwareBackground { }; enum { -#ifdef COLOR_16_BIT -#ifdef COLOR_5_6_5 - GBA_COLOR_WHITE = 0xFFDF, -#else - GBA_COLOR_WHITE = 0x7FFF, -#endif -#else - GBA_COLOR_WHITE = 0x00FFFFFF, -#endif OFFSET_PRIORITY = 30, OFFSET_INDEX = 28, }; diff --git a/src/arm/debugger/memory-debugger.c b/src/arm/debugger/memory-debugger.c index dce4b9771..8ccb592e7 100644 --- a/src/arm/debugger/memory-debugger.c +++ b/src/arm/debugger/memory-debugger.c @@ -106,17 +106,24 @@ static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, st } } + uint32_t oldValue; switch (width + 1) { case 1: - info->type.wp.oldValue = debugger->originalMemory.load8(debugger->cpu, address, 0); + oldValue = debugger->originalMemory.load8(debugger->cpu, address, 0); break; case 2: - info->type.wp.oldValue = debugger->originalMemory.load16(debugger->cpu, address, 0); + oldValue = debugger->originalMemory.load16(debugger->cpu, address, 0); break; case 4: - info->type.wp.oldValue = debugger->originalMemory.load32(debugger->cpu, address, 0); + oldValue = debugger->originalMemory.load32(debugger->cpu, address, 0); break; + default: + continue; } + if ((watchpoint->type & WATCHPOINT_CHANGE) && newValue == oldValue) { + continue; + } + info->type.wp.oldValue = oldValue; info->type.wp.newValue = newValue; info->address = address; info->type.wp.watchType = watchpoint->type; diff --git a/src/ds/renderers/software.c b/src/ds/renderers/software.c index 196401671..96b0d32af 100644 --- a/src/ds/renderers/software.c +++ b/src/ds/renderers/software.c @@ -394,7 +394,7 @@ static void DSVideoSoftwareRendererDrawGBAScanline(struct GBAVideoRenderer* rend color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y]; if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) { for (x = 0; x < softwareRenderer->masterEnd; ++x) { - row[x] = GBA_COLOR_WHITE; + row[x] = M_COLOR_WHITE; } return; } @@ -559,7 +559,7 @@ static void _drawScanlineA(struct DSVideoSoftwareRenderer* softwareRenderer, int switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntA)) { case 0: for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) { - row[x] = GBA_COLOR_WHITE; + row[x] = M_COLOR_WHITE; } return; case 1: @@ -631,7 +631,7 @@ static void _drawScanlineB(struct DSVideoSoftwareRenderer* softwareRenderer, int switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntB)) { case 0: for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) { - row[x] = GBA_COLOR_WHITE; + row[x] = M_COLOR_WHITE; } return; case 1: diff --git a/src/gb/core.c b/src/gb/core.c index 2713ce9a1..a9a929688 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -1188,6 +1188,9 @@ static bool _GBVLPLoadState(struct mCore* core, const void* buffer) { GBVideoDeserialize(&gb->video, state); GBIODeserialize(gb, state); GBAudioReset(&gb->audio); + if (gb->model & GB_MODEL_SGB) { + GBSGBDeserialize(gb, state); + } // Make sure CPU loop never spins gb->memory.ie = 0; diff --git a/src/gb/extra/proxy.c b/src/gb/extra/proxy.c index fd5d8d6cf..cea273692 100644 --- a/src/gb/extra/proxy.c +++ b/src/gb/extra/proxy.c @@ -48,6 +48,15 @@ void GBVideoProxyRendererCreate(struct GBVideoProxyRenderer* renderer, struct GB renderer->d.disableWIN = false; renderer->d.disableOBJ = false; + renderer->d.highlightBG = false; + renderer->d.highlightWIN = false; + int i; + for (i = 0; i < GB_VIDEO_MAX_OBJ; ++i) { + renderer->d.highlightOBJ[i] = false; + } + renderer->d.highlightColor = M_COLOR_WHITE; + renderer->d.highlightAmount = 0; + renderer->logger->context = renderer; renderer->logger->parsePacket = _parsePacket; renderer->logger->vramBlock = _vramBlock; @@ -75,6 +84,17 @@ static void _reset(struct GBVideoProxyRenderer* proxyRenderer) { mVideoLoggerRendererReset(proxyRenderer->logger); } +static void _copyExtraState(struct GBVideoProxyRenderer* proxyRenderer) { + proxyRenderer->backend->disableBG = proxyRenderer->d.disableBG; + proxyRenderer->backend->disableWIN = proxyRenderer->d.disableWIN; + proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ; + proxyRenderer->backend->highlightBG = proxyRenderer->d.highlightBG; + proxyRenderer->backend->highlightWIN = proxyRenderer->d.highlightWIN; + memcpy(proxyRenderer->backend->highlightOBJ, proxyRenderer->d.highlightOBJ, sizeof(proxyRenderer->backend->highlightOBJ)); + proxyRenderer->backend->highlightAmount = proxyRenderer->d.highlightAmount; + proxyRenderer->backend->highlightColor = proxyRenderer->d.highlightColor; +} + void GBVideoProxyRendererShim(struct GBVideo* video, struct GBVideoProxyRenderer* renderer) { if ((renderer->backend && video->renderer != renderer->backend) || video->renderer == &renderer->d) { return; @@ -82,6 +102,12 @@ void GBVideoProxyRendererShim(struct GBVideo* video, struct GBVideoProxyRenderer renderer->backend = video->renderer; video->renderer = &renderer->d; renderer->d.cache = renderer->backend->cache; + renderer->d.sgbRenderMode = renderer->backend->sgbRenderMode; + renderer->d.sgbCharRam = renderer->backend->sgbCharRam; + renderer->d.sgbMapRam = renderer->backend->sgbMapRam; + renderer->d.sgbPalRam = renderer->backend->sgbPalRam; + renderer->d.sgbAttributeFiles = renderer->backend->sgbAttributeFiles; + renderer->d.sgbAttributes = renderer->backend->sgbAttributes; renderer->d.vram = video->vram; renderer->d.oam = &video->oam; _init(renderer); @@ -119,7 +145,7 @@ void GBVideoProxyRendererDeinit(struct GBVideoRenderer* renderer) { static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* item) { struct GBVideoProxyRenderer* proxyRenderer = logger->context; uint8_t sgbPacket[16]; - struct GBObj legacyBuffer[40]; + struct GBObj legacyBuffer[GB_VIDEO_MAX_OBJ]; switch (item->type) { case DIRTY_REGISTER: proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value); @@ -142,9 +168,7 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD } break; case DIRTY_SCANLINE: - proxyRenderer->backend->disableBG = proxyRenderer->d.disableBG; - proxyRenderer->backend->disableWIN = proxyRenderer->d.disableWIN; - proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ; + _copyExtraState(proxyRenderer); if (item->address < GB_VIDEO_VERTICAL_PIXELS) { proxyRenderer->backend->finishScanline(proxyRenderer->backend, item->address); } @@ -160,7 +184,7 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD case DIRTY_BUFFER: switch (item->address) { case BUFFER_OAM: - if (item->value2 / sizeof(struct GBObj) > 40) { + if (item->value2 / sizeof(struct GBObj) > GB_VIDEO_MAX_OBJ) { return false; } logger->readData(logger, legacyBuffer, item->value2, true); @@ -235,9 +259,7 @@ void GBVideoProxyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam void GBVideoProxyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y) { struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; if (!proxyRenderer->logger->block) { - proxyRenderer->backend->disableBG = proxyRenderer->d.disableBG; - proxyRenderer->backend->disableWIN = proxyRenderer->d.disableWIN; - proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ; + _copyExtraState(proxyRenderer); proxyRenderer->backend->drawRange(proxyRenderer->backend, startX, endX, y); } mVideoLoggerRendererDrawRange(proxyRenderer->logger, startX, endX, y); diff --git a/src/gb/renderers/software.c b/src/gb/renderers/software.c index f0436ee85..f90d8d674 100644 --- a/src/gb/renderers/software.c +++ b/src/gb/renderers/software.c @@ -11,6 +11,15 @@ #include #include +#define PAL_BG 0 +#define PAL_OBJ 0x20 +#define PAL_HIGHLIGHT 0x80 +#define PAL_HIGHLIGHT_BG (PAL_HIGHLIGHT | PAL_BG) +#define PAL_HIGHLIGHT_OBJ (PAL_HIGHLIGHT | PAL_OBJ) +#define PAL_SGB_BORDER 0x40 +#define OBJ_PRIORITY 0x100 +#define OBJ_PRIO_MASK 0x0FF + static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum GBModel model, bool borders); static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer); static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value); @@ -25,8 +34,8 @@ static void GBVideoSoftwareRendererEnableSGBBorder(struct GBVideoRenderer* rende static void GBVideoSoftwareRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels); static void GBVideoSoftwareRendererPutPixels(struct GBVideoRenderer* renderer, size_t stride, const void* pixels); -static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int startX, int endX, int sx, int sy); -static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int startX, int endX, int y); +static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int startX, int endX, int sx, int sy, bool highlight); +static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBVideoRendererSprite* obj, int startX, int endX, int y); static void _clearScreen(struct GBVideoSoftwareRenderer* renderer) { size_t sgbOffset = 0; @@ -51,7 +60,7 @@ static void _regenerateSGBBorder(struct GBVideoSoftwareRenderer* renderer) { for (i = 0; i < 0x40; ++i) { uint16_t color; LOAD_16LE(color, 0x800 + i * 2, renderer->d.sgbMapRam); - renderer->d.writePalette(&renderer->d, i + 0x40, color); + renderer->d.writePalette(&renderer->d, i + PAL_SGB_BORDER, color); } int x, y; for (y = 0; y < 224; ++y) { @@ -180,6 +189,15 @@ void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer* renderer) { renderer->d.disableOBJ = false; renderer->d.disableWIN = false; + renderer->d.highlightBG = false; + renderer->d.highlightWIN = false; + int i; + for (i = 0; i < GB_VIDEO_MAX_OBJ; ++i) { + renderer->d.highlightOBJ[i] = false; + } + renderer->d.highlightColor = M_COLOR_WHITE; + renderer->d.highlightAmount = 0; + renderer->temporaryBuffer = 0; } @@ -206,8 +224,8 @@ static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum G softwareRenderer->offsetWx = 0; softwareRenderer->offsetWy = 0; - int i; - for (i = 0; i < 64; ++i) { + size_t i; + for (i = 0; i < (sizeof(softwareRenderer->lookup) / sizeof(*softwareRenderer->lookup)); ++i) { softwareRenderer->lookup[i] = i; softwareRenderer->lookup[i] = i; softwareRenderer->lookup[i] = i; @@ -215,6 +233,8 @@ static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum G } memset(softwareRenderer->palette, 0, sizeof(softwareRenderer->palette)); + + softwareRenderer->lastHighlightAmount = 0; } static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer) { @@ -280,18 +300,30 @@ static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* softwareRenderer->lookup[1] = (value >> 2) & 3; softwareRenderer->lookup[2] = (value >> 4) & 3; softwareRenderer->lookup[3] = (value >> 6) & 3; + softwareRenderer->lookup[PAL_HIGHLIGHT_BG + 0] = PAL_HIGHLIGHT + (value & 3); + softwareRenderer->lookup[PAL_HIGHLIGHT_BG + 1] = PAL_HIGHLIGHT + ((value >> 2) & 3); + softwareRenderer->lookup[PAL_HIGHLIGHT_BG + 2] = PAL_HIGHLIGHT + ((value >> 4) & 3); + softwareRenderer->lookup[PAL_HIGHLIGHT_BG + 3] = PAL_HIGHLIGHT + ((value >> 6) & 3); break; case GB_REG_OBP0: - softwareRenderer->lookup[0x20 + 0] = value & 3; - softwareRenderer->lookup[0x20 + 1] = (value >> 2) & 3; - softwareRenderer->lookup[0x20 + 2] = (value >> 4) & 3; - softwareRenderer->lookup[0x20 + 3] = (value >> 6) & 3; + softwareRenderer->lookup[PAL_OBJ + 0] = value & 3; + softwareRenderer->lookup[PAL_OBJ + 1] = (value >> 2) & 3; + softwareRenderer->lookup[PAL_OBJ + 2] = (value >> 4) & 3; + softwareRenderer->lookup[PAL_OBJ + 3] = (value >> 6) & 3; + softwareRenderer->lookup[PAL_HIGHLIGHT_OBJ + 0] = PAL_HIGHLIGHT + (value & 3); + softwareRenderer->lookup[PAL_HIGHLIGHT_OBJ + 1] = PAL_HIGHLIGHT + ((value >> 2) & 3); + softwareRenderer->lookup[PAL_HIGHLIGHT_OBJ + 2] = PAL_HIGHLIGHT + ((value >> 4) & 3); + softwareRenderer->lookup[PAL_HIGHLIGHT_OBJ + 3] = PAL_HIGHLIGHT + ((value >> 6) & 3); break; case GB_REG_OBP1: - softwareRenderer->lookup[0x24 + 0] = value & 3; - softwareRenderer->lookup[0x24 + 1] = (value >> 2) & 3; - softwareRenderer->lookup[0x24 + 2] = (value >> 4) & 3; - softwareRenderer->lookup[0x24 + 3] = (value >> 6) & 3; + softwareRenderer->lookup[PAL_OBJ + 4] = value & 3; + softwareRenderer->lookup[PAL_OBJ + 5] = (value >> 2) & 3; + softwareRenderer->lookup[PAL_OBJ + 6] = (value >> 4) & 3; + softwareRenderer->lookup[PAL_OBJ + 7] = (value >> 6) & 3; + softwareRenderer->lookup[PAL_HIGHLIGHT_OBJ + 4] = PAL_HIGHLIGHT + (value & 3); + softwareRenderer->lookup[PAL_HIGHLIGHT_OBJ + 5] = PAL_HIGHLIGHT + ((value >> 2) & 3); + softwareRenderer->lookup[PAL_HIGHLIGHT_OBJ + 6] = PAL_HIGHLIGHT + ((value >> 4) & 3); + softwareRenderer->lookup[PAL_HIGHLIGHT_OBJ + 7] = PAL_HIGHLIGHT + ((value >> 6) & 3); break; } return value; @@ -442,8 +474,10 @@ static void GBVideoSoftwareRendererWritePalette(struct GBVideoRenderer* renderer if (softwareRenderer->model & GB_MODEL_SGB) { if (index < 0x10 && index && !(index & 3)) { color = softwareRenderer->palette[0]; - } else if (index >= 0x40 && !(index & 0xF)) { + } else if (index >= PAL_SGB_BORDER && !(index & 0xF)) { color = softwareRenderer->palette[0]; + } else if (index > PAL_HIGHLIGHT && index < PAL_HIGHLIGHT_OBJ && !(index & 3)) { + color = softwareRenderer->palette[PAL_HIGHLIGHT_BG]; } } if (renderer->cache) { @@ -472,6 +506,9 @@ static void GBVideoSoftwareRendererWritePalette(struct GBVideoRenderer* renderer #endif } softwareRenderer->palette[index] = color; + if (index < PAL_SGB_BORDER && (index < PAL_OBJ || (index & 3))) { + softwareRenderer->palette[index + PAL_HIGHLIGHT] = mColorMix5Bit(0x10 - softwareRenderer->lastHighlightAmount, color, softwareRenderer->lastHighlightAmount, renderer->highlightColor); + } if (softwareRenderer->model & GB_MODEL_SGB && !index && GBRegisterLCDCIsEnable(softwareRenderer->lcdc)) { renderer->writePalette(renderer, 0x04, value); @@ -508,13 +545,14 @@ static void _cleanOAM(struct GBVideoSoftwareRenderer* renderer, int y) { } int o = 0; int i; - for (i = 0; i < 40 && o < 10; ++i) { + for (i = 0; i < GB_VIDEO_MAX_OBJ && o < GB_VIDEO_MAX_LINE_OBJ; ++i) { uint8_t oy = renderer->d.oam->obj[i].y; if (y < oy - 16 || y >= oy - 16 + spriteHeight) { continue; } // TODO: Sort - renderer->obj[o] = renderer->d.oam->obj[i]; + renderer->obj[o].obj = renderer->d.oam->obj[i]; + renderer->obj[o].index = i; ++o; if (o == 10) { break; @@ -542,16 +580,16 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i } if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->hasWindow && wx <= endX && !softwareRenderer->d.disableWIN) { if (wx > 0 && !softwareRenderer->d.disableBG) { - GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, wx, softwareRenderer->scx - softwareRenderer->offsetScx, softwareRenderer->scy + y - softwareRenderer->offsetScy); + GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, wx, softwareRenderer->scx - softwareRenderer->offsetScx, softwareRenderer->scy + y - softwareRenderer->offsetScy, renderer->highlightBG); } maps = &softwareRenderer->d.vram[GB_BASE_MAP]; if (GBRegisterLCDCIsWindowTileMap(softwareRenderer->lcdc)) { maps += GB_SIZE_MAP; } - GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, wx, endX, -wx - softwareRenderer->offsetWx, y - wy - softwareRenderer->offsetWy); + GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, wx, endX, -wx - softwareRenderer->offsetWx, y - wy - softwareRenderer->offsetWy, renderer->highlightWIN); } else if (!softwareRenderer->d.disableBG) { - GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, endX, softwareRenderer->scx - softwareRenderer->offsetScx, softwareRenderer->scy + y - softwareRenderer->offsetScy); + GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, endX, softwareRenderer->scx - softwareRenderer->offsetScx, softwareRenderer->scy + y - softwareRenderer->offsetScy, renderer->highlightBG); } } else if (!softwareRenderer->d.disableBG) { memset(&softwareRenderer->row[startX], 0, endX - startX); @@ -567,6 +605,18 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i } } + unsigned highlightAmount = (renderer->highlightAmount + 6) >> 4; + if (softwareRenderer->lastHighlightAmount != highlightAmount) { + softwareRenderer->lastHighlightAmount = highlightAmount; + int i; + for (i = 0; i < PAL_SGB_BORDER; ++i) { + if (i >= PAL_OBJ && (i & 3) == 0) { + continue; + } + softwareRenderer->palette[i + PAL_HIGHLIGHT] = mColorMix5Bit(0x10 - highlightAmount, softwareRenderer->palette[i], highlightAmount, renderer->highlightColor); + } + } + size_t sgbOffset = 0; if (softwareRenderer->model & GB_MODEL_SGB && softwareRenderer->sgbBorders) { sgbOffset = softwareRenderer->outputBufferStride * 40 + 48; @@ -583,7 +633,7 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i p <<= 2; } for (; x < ((startX + 7) & ~7) && x < endX; ++x) { - row[x] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x] & 0x7F]]; + row[x] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x] & OBJ_PRIO_MASK]]; } for (; x + 7 < (endX & ~7); x += 8) { if (softwareRenderer->model & GB_MODEL_SGB) { @@ -592,14 +642,14 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i p &= 3; p <<= 2; } - row[x + 0] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x] & 0x7F]]; - row[x + 1] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 1] & 0x7F]]; - row[x + 2] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 2] & 0x7F]]; - row[x + 3] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 3] & 0x7F]]; - row[x + 4] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 4] & 0x7F]]; - row[x + 5] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 5] & 0x7F]]; - row[x + 6] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 6] & 0x7F]]; - row[x + 7] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 7] & 0x7F]]; + row[x + 0] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x] & OBJ_PRIO_MASK]]; + row[x + 1] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 1] & OBJ_PRIO_MASK]]; + row[x + 2] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 2] & OBJ_PRIO_MASK]]; + row[x + 3] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 3] & OBJ_PRIO_MASK]]; + row[x + 4] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 4] & OBJ_PRIO_MASK]]; + row[x + 5] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 5] & OBJ_PRIO_MASK]]; + row[x + 6] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 6] & OBJ_PRIO_MASK]]; + row[x + 7] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 7] & OBJ_PRIO_MASK]]; } if (softwareRenderer->model & GB_MODEL_SGB) { p = softwareRenderer->d.sgbAttributes[(x >> 5) + 5 * (y >> 3)]; @@ -608,7 +658,7 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i p <<= 2; } for (; x < endX; ++x) { - row[x] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x] & 0x7F]]; + row[x] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x] & OBJ_PRIO_MASK]]; } break; case 1: @@ -769,7 +819,7 @@ static void GBVideoSoftwareRendererEnableSGBBorder(struct GBVideoRenderer* rende } } -static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int startX, int endX, int sx, int sy) { +static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int startX, int endX, int sx, int sy, bool highlight) { uint8_t* data = renderer->d.vram; uint8_t* attr = &maps[GB_SIZE_VRAM_BANK0]; if (!GBRegisterLCDCIsTileData(renderer->lcdc)) { @@ -794,12 +844,12 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer } else { bgTile = ((int8_t*) maps)[topX + topY]; } - int p = 0; + int p = highlight ? PAL_HIGHLIGHT_BG : PAL_BG; if (renderer->model >= GB_MODEL_CGB) { GBObjAttributes attrs = attr[topX + topY]; - p = GBObjAttributesGetCGBPalette(attrs) * 4; + p |= GBObjAttributesGetCGBPalette(attrs) * 4; if (GBObjAttributesIsPriority(attrs) && GBRegisterLCDCIsBgEnable(renderer->lcdc)) { - p |= 0x80; + p |= OBJ_PRIORITY; } if (GBObjAttributesIsBank(attrs)) { localData += GB_SIZE_VRAM_BANK0; @@ -829,12 +879,12 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer } else { bgTile = ((int8_t*) maps)[topX + topY]; } - int p = 0; + int p = highlight ? PAL_HIGHLIGHT_BG : PAL_BG; if (renderer->model >= GB_MODEL_CGB) { GBObjAttributes attrs = attr[topX + topY]; - p = GBObjAttributesGetCGBPalette(attrs) * 4; + p |= GBObjAttributesGetCGBPalette(attrs) * 4; if (GBObjAttributesIsPriority(attrs) && GBRegisterLCDCIsBgEnable(renderer->lcdc)) { - p |= 0x80; + p |= OBJ_PRIORITY; } if (GBObjAttributesIsBank(attrs)) { localData += GB_SIZE_VRAM_BANK0; @@ -869,8 +919,8 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer } } -static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int startX, int endX, int y) { - int objX = obj->x + renderer->objOffsetX; +static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBVideoRendererSprite* obj, int startX, int endX, int y) { + int objX = obj->obj.x + renderer->objOffsetX; int ix = objX - 8; if (endX < ix || startX >= ix + 8) { return; @@ -887,8 +937,8 @@ 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)) { + int objY = obj->obj.y + renderer->objOffsetY; + if (GBObjAttributesIsYFlip(obj->obj.attr)) { bottomY = 7 - ((y - objY - 16) & 7); if (GBRegisterLCDCIsObjSize(renderer->lcdc) && y - objY < -8) { ++tileOffset; @@ -899,115 +949,113 @@ static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* rende ++tileOffset; } } - if (GBRegisterLCDCIsObjSize(renderer->lcdc) && obj->tile & 1) { + if (GBRegisterLCDCIsObjSize(renderer->lcdc) && obj->obj.tile & 1) { --tileOffset; } - uint8_t mask = GBObjAttributesIsPriority(obj->attr) ? 0x63 : 0x60; - uint8_t mask2 = GBObjAttributesIsPriority(obj->attr) ? 0 : 0x83; - int p; + unsigned mask = GBObjAttributesIsPriority(obj->obj.attr) ? 0x63 : 0x60; + unsigned mask2 = GBObjAttributesIsPriority(obj->obj.attr) ? 0 : (OBJ_PRIORITY | 3); + int p = renderer->d.highlightOBJ[obj->index] ? PAL_HIGHLIGHT_OBJ : PAL_OBJ; if (renderer->model >= GB_MODEL_CGB) { - p = (GBObjAttributesGetCGBPalette(obj->attr) + 8) * 4; - if (GBObjAttributesIsBank(obj->attr)) { + p |= GBObjAttributesGetCGBPalette(obj->obj.attr) * 4; + if (GBObjAttributesIsBank(obj->obj.attr)) { data += GB_SIZE_VRAM_BANK0; } if (!GBRegisterLCDCIsBgEnable(renderer->lcdc)) { mask = 0x60; - mask2 = 0x83; + mask2 = OBJ_PRIORITY | 3; } } else { - p = (GBObjAttributesGetPalette(obj->attr) + 8) * 4; + p |= (GBObjAttributesGetPalette(obj->obj.attr) + 8) * 4; } int bottomX; int x = startX; + int objTile = obj->obj.tile + tileOffset; if ((x - objX) & 7) { for (; x < endX; ++x) { - if (GBObjAttributesIsXFlip(obj->attr)) { + if (GBObjAttributesIsXFlip(obj->obj.attr)) { bottomX = (x - objX) & 7; } else { bottomX = 7 - ((x - objX) & 7); } - int objTile = obj->tile + tileOffset; uint8_t tileDataLower = data[(objTile * 8 + bottomY) * 2]; uint8_t tileDataUpper = data[(objTile * 8 + bottomY) * 2 + 1]; tileDataUpper >>= bottomX; tileDataLower >>= bottomX; - color_t current = renderer->row[x]; - if (((tileDataUpper | tileDataLower) & 1) && !(current & mask) && (current & mask2) <= 0x80) { + unsigned current = renderer->row[x]; + if (((tileDataUpper | tileDataLower) & 1) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) { renderer->row[x] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1); } } - } else if (GBObjAttributesIsXFlip(obj->attr)) { - int objTile = obj->tile + tileOffset; + } else if (GBObjAttributesIsXFlip(obj->obj.attr)) { uint8_t tileDataLower = data[(objTile * 8 + bottomY) * 2]; uint8_t tileDataUpper = data[(objTile * 8 + bottomY) * 2 + 1]; - color_t current; + unsigned current; current = renderer->row[x]; - if (((tileDataUpper | tileDataLower) & 1) && !(current & mask) && (current & mask2) <= 0x80) { + if (((tileDataUpper | tileDataLower) & 1) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) { renderer->row[x] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1); } current = renderer->row[x + 1]; - if (((tileDataUpper | tileDataLower) & 2) && !(current & mask) && (current & mask2) <= 0x80) { + if (((tileDataUpper | tileDataLower) & 2) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) { renderer->row[x + 1] = p | (tileDataUpper & 2) | ((tileDataLower & 2) >> 1); } current = renderer->row[x + 2]; - if (((tileDataUpper | tileDataLower) & 4) && !(current & mask) && (current & mask2) <= 0x80) { + if (((tileDataUpper | tileDataLower) & 4) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) { renderer->row[x + 2] = p | ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2); } current = renderer->row[x + 3]; - if (((tileDataUpper | tileDataLower) & 8) && !(current & mask) && (current & mask2) <= 0x80) { + if (((tileDataUpper | tileDataLower) & 8) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) { renderer->row[x + 3] = p | ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3); } current = renderer->row[x + 4]; - if (((tileDataUpper | tileDataLower) & 16) && !(current & mask) && (current & mask2) <= 0x80) { + if (((tileDataUpper | tileDataLower) & 16) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) { renderer->row[x + 4] = p | ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4); } current = renderer->row[x + 5]; - if (((tileDataUpper | tileDataLower) & 32) && !(current & mask) && (current & mask2) <= 0x80) { + if (((tileDataUpper | tileDataLower) & 32) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) { renderer->row[x + 5] = p | ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5); } current = renderer->row[x + 6]; - if (((tileDataUpper | tileDataLower) & 64) && !(current & mask) && (current & mask2) <= 0x80) { + if (((tileDataUpper | tileDataLower) & 64) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) { renderer->row[x + 6] = p | ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6); } current = renderer->row[x + 7]; - if (((tileDataUpper | tileDataLower) & 128) && !(current & mask) && (current & mask2) <= 0x80) { + if (((tileDataUpper | tileDataLower) & 128) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) { renderer->row[x + 7] = p | ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7); } } else { - int objTile = obj->tile + tileOffset; uint8_t tileDataLower = data[(objTile * 8 + bottomY) * 2]; uint8_t tileDataUpper = data[(objTile * 8 + bottomY) * 2 + 1]; - color_t current; + unsigned current; current = renderer->row[x + 7]; - if (((tileDataUpper | tileDataLower) & 1) && !(current & mask) && (current & mask2) <= 0x80) { + if (((tileDataUpper | tileDataLower) & 1) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) { renderer->row[x + 7] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1); } current = renderer->row[x + 6]; - if (((tileDataUpper | tileDataLower) & 2) && !(current & mask) && (current & mask2) <= 0x80) { + if (((tileDataUpper | tileDataLower) & 2) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) { renderer->row[x + 6] = p | (tileDataUpper & 2) | ((tileDataLower & 2) >> 1); } current = renderer->row[x + 5]; - if (((tileDataUpper | tileDataLower) & 4) && !(current & mask) && (current & mask2) <= 0x80) { + if (((tileDataUpper | tileDataLower) & 4) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) { renderer->row[x + 5] = p | ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2); } current = renderer->row[x + 4]; - if (((tileDataUpper | tileDataLower) & 8) && !(current & mask) && (current & mask2) <= 0x80) { + if (((tileDataUpper | tileDataLower) & 8) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) { renderer->row[x + 4] = p | ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3); } current = renderer->row[x + 3]; - if (((tileDataUpper | tileDataLower) & 16) && !(current & mask) && (current & mask2) <= 0x80) { + if (((tileDataUpper | tileDataLower) & 16) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) { renderer->row[x + 3] = p | ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4); } current = renderer->row[x + 2]; - if (((tileDataUpper | tileDataLower) & 32) && !(current & mask) && (current & mask2) <= 0x80) { + if (((tileDataUpper | tileDataLower) & 32) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) { renderer->row[x + 2] = p | ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5); } current = renderer->row[x + 1]; - if (((tileDataUpper | tileDataLower) & 64) && !(current & mask) && (current & mask2) <= 0x80) { + if (((tileDataUpper | tileDataLower) & 64) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) { renderer->row[x + 1] = p | ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6); } current = renderer->row[x]; - if (((tileDataUpper | tileDataLower) & 128) && !(current & mask) && (current & mask2) <= 0x80) { + if (((tileDataUpper | tileDataLower) & 128) && !(current & mask) && (current & mask2) <= OBJ_PRIORITY) { renderer->row[x] = p | ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7); } } diff --git a/src/gb/serialize.c b/src/gb/serialize.c index 04060d641..056d1fd77 100644 --- a/src/gb/serialize.c +++ b/src/gb/serialize.c @@ -16,9 +16,6 @@ mLOG_DEFINE_CATEGORY(GB_STATE, "GB Savestate", "gb.serialize"); const uint32_t GB_SAVESTATE_MAGIC = 0x00400000; const uint32_t GB_SAVESTATE_VERSION = 0x00000002; -static void GBSGBSerialize(struct GB* gb, struct GBSerializedState* state); -static void GBSGBDeserialize(struct GB* gb, const struct GBSerializedState* state); - void GBSerialize(struct GB* gb, struct GBSerializedState* state) { STORE_32LE(GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION, 0, &state->versionMagic); STORE_32LE(gb->romCrc32, 0, &state->romCrc32); diff --git a/src/gb/video.c b/src/gb/video.c index 072e9db98..9826b601f 100644 --- a/src/gb/video.c +++ b/src/gb/video.c @@ -409,7 +409,7 @@ static void _cleanOAM(struct GBVideo* video, int y) { } int o = 0; int i; - for (i = 0; i < 40 && o < 10; ++i) { + for (i = 0; i < GB_VIDEO_MAX_OBJ && o < GB_VIDEO_MAX_LINE_OBJ; ++i) { uint8_t oy = video->oam.obj[i].y; if (y < oy - 16 || y >= oy - 16 + spriteHeight) { continue; diff --git a/src/gba/ereader.c b/src/gba/ereader.c index e288f78c6..5e3c64ef3 100644 --- a/src/gba/ereader.c +++ b/src/gba/ereader.c @@ -103,6 +103,16 @@ const uint16_t EREADER_ADDRESS_CODES[] = { 54679 }; +static const uint8_t DUMMY_HEADER_STRIP[2][0x10] = { + { 0x00, 0x30, 0x01, 0x01, 0x00, 0x01, 0x05, 0x10, 0x00, 0x00, 0x10, 0x13, 0x00, 0x00, 0x02, 0x00 }, + { 0x00, 0x30, 0x01, 0x02, 0x00, 0x01, 0x08, 0x10, 0x00, 0x00, 0x10, 0x12, 0x00, 0x00, 0x01, 0x00 } +}; + +static const uint8_t DUMMY_HEADER_FIXED[0x16] = { + 0x00, 0x00, 0x10, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x08, 0x4e, 0x49, 0x4e, 0x54, 0x45, 0x4e, + 0x44, 0x4f, 0x00, 0x22, 0x00, 0x09 +}; + static const uint8_t BLOCK_HEADER[2][0x18] = { { 0x00, 0x02, 0x00, 0x01, 0x40, 0x10, 0x00, 0x1c, 0x10, 0x6f, 0x40, 0xda, 0x39, 0x25, 0x8e, 0xe0, 0x7b, 0xb5, 0x98, 0xb6, 0x5b, 0xcf, 0x7f, 0x72 }, { 0x00, 0x03, 0x00, 0x19, 0x40, 0x10, 0x00, 0x2c, 0x0e, 0x88, 0xed, 0x82, 0x50, 0x67, 0xfb, 0xd1, 0x43, 0xee, 0x03, 0xc6, 0xc6, 0x2b, 0x2c, 0x93 } @@ -327,12 +337,18 @@ void GBAHardwareEReaderScan(struct GBACartridgeHardware* hw, const void* data, s memset(hw->eReaderDots, 0, EREADER_DOTCODE_SIZE); uint8_t blockRS[44][0x10]; + uint8_t block0[0x30]; bool parsed = false; bool bitmap = false; + bool reducedHeader = false; size_t blocks; int base; switch (size) { // Raw sizes + case 2076: + memcpy(block0, DUMMY_HEADER_STRIP[1], sizeof(DUMMY_HEADER_STRIP[1])); + reducedHeader = true; + // Fallthrough case 2112: parsed = true; // Fallthrough @@ -340,6 +356,10 @@ void GBAHardwareEReaderScan(struct GBACartridgeHardware* hw, const void* data, s base = 25; blocks = 28; break; + case 1308: + memcpy(block0, DUMMY_HEADER_STRIP[0], sizeof(DUMMY_HEADER_STRIP[0])); + reducedHeader = true; + // Fallthrough case 1344: parsed = true; // Fallthrough @@ -355,11 +375,12 @@ void GBAHardwareEReaderScan(struct GBACartridgeHardware* hw, const void* data, s return; } + const uint8_t* cdata = data; size_t i; if (bitmap) { size_t x; for (i = 0; i < 40; ++i) { - const uint8_t* line = &((const uint8_t*) data)[(i + 2) * 124]; + const uint8_t* line = &cdata[(i + 2) * 124]; uint8_t* origin = &hw->eReaderDots[EREADER_DOTCODE_STRIDE * i + 200]; for (x = 0; x < 124; ++x) { uint8_t byte = line[x]; @@ -386,8 +407,49 @@ void GBAHardwareEReaderScan(struct GBACartridgeHardware* hw, const void* data, s _eReaderAddress(origin, base + i); } if (parsed) { - for (i = 0; i < size / 48; ++i) { - _eReaderReedSolomon(&((const uint8_t*) data)[i * 48], blockRS[i]); + if (reducedHeader) { + memcpy(&block0[0x10], DUMMY_HEADER_FIXED, sizeof(DUMMY_HEADER_FIXED)); + block0[0x0D] = cdata[0x0]; + block0[0x0C] = cdata[0x1]; + block0[0x10] = cdata[0x2]; + block0[0x11] = cdata[0x3]; + block0[0x26] = cdata[0x4]; + block0[0x27] = cdata[0x5]; + block0[0x28] = cdata[0x6]; + block0[0x29] = cdata[0x7]; + block0[0x2A] = cdata[0x8]; + block0[0x2B] = cdata[0x9]; + block0[0x2C] = cdata[0xA]; + block0[0x2D] = cdata[0xB]; + for (i = 0; i < 12; ++i) { + block0[0x2E] ^= cdata[i]; + } + unsigned dataChecksum = 0; + int j; + for (i = 1; i < (size + 36) / 48; ++i) { + const uint8_t* block = &cdata[i * 48 - 36]; + _eReaderReedSolomon(block, blockRS[i]); + unsigned fragmentChecksum = 0; + for (j = 0; j < 0x30; j += 2) { + uint16_t halfword; + fragmentChecksum ^= block[j]; + fragmentChecksum ^= block[j + 1]; + LOAD_16BE(halfword, j, block); + dataChecksum += halfword; + } + block0[0x2F] += fragmentChecksum; + } + block0[0x13] = (~dataChecksum) >> 8; + block0[0x14] = ~dataChecksum; + for (i = 0; i < 0x2F; ++i) { + block0[0x2F] += block0[i]; + } + block0[0x2F] = ~block0[0x2F]; + _eReaderReedSolomon(block0, blockRS[0]); + } else { + for (i = 0; i < size / 48; ++i) { + _eReaderReedSolomon(&cdata[i * 48], blockRS[i]); + } } } size_t blockId = 0; @@ -407,23 +469,29 @@ void GBAHardwareEReaderScan(struct GBACartridgeHardware* hw, const void* data, s parsedBlockData[1] = header[(2 * i) % 0x18 + 1]; int j; for (j = 2; j < 104; ++j) { - if (byteOffset >= 0x40) { - break; - } - if (byteOffset >= 0x30) { - parsedBlockData[j] = blockRS[blockId][byteOffset - 0x30]; - } else { - parsedBlockData[j] = ((const uint8_t*) data)[blockId * 0x30 + byteOffset]; - } - ++blockId; - if (blockId * 0x30 >= size) { - blockId = 0; - ++byteOffset; - } + if (byteOffset >= 0x40) { + break; + } + if (byteOffset >= 0x30) { + parsedBlockData[j] = blockRS[blockId][byteOffset - 0x30]; + } else if (!reducedHeader) { + parsedBlockData[j] = cdata[blockId * 0x30 + byteOffset]; + } else { + if (blockId > 0) { + parsedBlockData[j] = cdata[blockId * 0x30 + byteOffset - 36]; + } else { + parsedBlockData[j] = block0[byteOffset]; + } + } + ++blockId; + if (blockId * 0x30 >= size) { + blockId = 0; + ++byteOffset; + } } blockData = parsedBlockData; } else { - blockData = &((const uint8_t*) data)[i * 104]; + blockData = &cdata[i * 104]; } int b; for (b = 0; b < 104; ++b) { @@ -646,4 +714,4 @@ void GBAEReaderQueueCard(struct GBA* gba, const void* data, size_t size) { gba->memory.hw.eReaderCards[i].size = size; return; } -} \ No newline at end of file +} diff --git a/src/gba/extra/proxy.c b/src/gba/extra/proxy.c index b7c9e967e..4c71eed60 100644 --- a/src/gba/extra/proxy.c +++ b/src/gba/extra/proxy.c @@ -55,7 +55,7 @@ void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct for (i = 0; i < 128; ++i) { renderer->d.highlightOBJ[i] = false; } - renderer->d.highlightColor = 0xFFFFFF; + renderer->d.highlightColor = M_COLOR_WHITE; renderer->d.highlightAmount = 0; renderer->logger->context = renderer; @@ -100,6 +100,24 @@ static void _reset(struct GBAVideoProxyRenderer* proxyRenderer) { mVideoLoggerRendererReset(proxyRenderer->logger); } +static void _copyExtraState(struct GBAVideoProxyRenderer* proxyRenderer) { + proxyRenderer->backend->disableBG[0] = proxyRenderer->d.disableBG[0]; + proxyRenderer->backend->disableBG[1] = proxyRenderer->d.disableBG[1]; + proxyRenderer->backend->disableBG[2] = proxyRenderer->d.disableBG[2]; + proxyRenderer->backend->disableBG[3] = proxyRenderer->d.disableBG[3]; + proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ; + proxyRenderer->backend->disableWIN[0] = proxyRenderer->d.disableWIN[0]; + proxyRenderer->backend->disableWIN[1] = proxyRenderer->d.disableWIN[1]; + proxyRenderer->backend->disableOBJWIN = proxyRenderer->d.disableOBJWIN; + proxyRenderer->backend->highlightBG[0] = proxyRenderer->d.highlightBG[0]; + proxyRenderer->backend->highlightBG[1] = proxyRenderer->d.highlightBG[1]; + proxyRenderer->backend->highlightBG[2] = proxyRenderer->d.highlightBG[2]; + proxyRenderer->backend->highlightBG[3] = proxyRenderer->d.highlightBG[3]; + memcpy(proxyRenderer->backend->highlightOBJ, proxyRenderer->d.highlightOBJ, sizeof(proxyRenderer->backend->highlightOBJ)); + proxyRenderer->backend->highlightAmount = proxyRenderer->d.highlightAmount; + proxyRenderer->backend->highlightColor = proxyRenderer->d.highlightColor; +} + void GBAVideoProxyRendererShim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer) { if ((renderer->backend && video->renderer != renderer->backend) || video->renderer == &renderer->d) { return; @@ -214,20 +232,7 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD } break; case DIRTY_SCANLINE: - proxyRenderer->backend->disableBG[0] = proxyRenderer->d.disableBG[0]; - proxyRenderer->backend->disableBG[1] = proxyRenderer->d.disableBG[1]; - proxyRenderer->backend->disableBG[2] = proxyRenderer->d.disableBG[2]; - proxyRenderer->backend->disableBG[3] = proxyRenderer->d.disableBG[3]; - proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ; - proxyRenderer->backend->disableWIN[0] = proxyRenderer->d.disableWIN[0]; - proxyRenderer->backend->disableWIN[1] = proxyRenderer->d.disableWIN[1]; - proxyRenderer->backend->disableOBJWIN = proxyRenderer->d.disableOBJWIN; - proxyRenderer->backend->highlightBG[0] = proxyRenderer->d.highlightBG[0]; - proxyRenderer->backend->highlightBG[1] = proxyRenderer->d.highlightBG[1]; - proxyRenderer->backend->highlightBG[2] = proxyRenderer->d.highlightBG[2]; - proxyRenderer->backend->highlightBG[3] = proxyRenderer->d.highlightBG[3]; - memcpy(proxyRenderer->backend->highlightOBJ, proxyRenderer->d.highlightOBJ, sizeof(proxyRenderer->backend->highlightOBJ)); - proxyRenderer->backend->highlightAmount = proxyRenderer->d.highlightAmount; + _copyExtraState(proxyRenderer); if (item->address < GBA_VIDEO_VERTICAL_PIXELS) { proxyRenderer->backend->drawScanline(proxyRenderer->backend, item->address); } @@ -321,18 +326,7 @@ void GBAVideoProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t o void GBAVideoProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; if (!proxyRenderer->logger->block) { - proxyRenderer->backend->disableBG[0] = proxyRenderer->d.disableBG[0]; - proxyRenderer->backend->disableBG[1] = proxyRenderer->d.disableBG[1]; - proxyRenderer->backend->disableBG[2] = proxyRenderer->d.disableBG[2]; - proxyRenderer->backend->disableBG[3] = proxyRenderer->d.disableBG[3]; - proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ; - proxyRenderer->backend->disableWIN[0] = proxyRenderer->d.disableWIN[0]; - proxyRenderer->backend->disableWIN[1] = proxyRenderer->d.disableWIN[1]; - proxyRenderer->backend->disableOBJWIN = proxyRenderer->d.disableOBJWIN; - proxyRenderer->backend->highlightBG[0] = proxyRenderer->d.highlightBG[0]; - proxyRenderer->backend->highlightBG[1] = proxyRenderer->d.highlightBG[1]; - proxyRenderer->backend->highlightBG[2] = proxyRenderer->d.highlightBG[2]; - proxyRenderer->backend->highlightBG[3] = proxyRenderer->d.highlightBG[3]; + _copyExtraState(proxyRenderer); proxyRenderer->backend->drawScanline(proxyRenderer->backend, y); } mVideoLoggerRendererDrawScanline(proxyRenderer->logger, y); diff --git a/src/gba/overrides.c b/src/gba/overrides.c index 551b189b5..bfb52e093 100644 --- a/src/gba/overrides.c +++ b/src/gba/overrides.c @@ -87,12 +87,6 @@ static const struct GBACartridgeOverride _overrides[] = { // Metal Slug Advance { "BSME", SAVEDATA_EEPROM, HW_NONE, 0x8000290, false }, - // Pokemon Pinball: Ruby & Sapphire - { "BPPJ", SAVEDATA_SRAM, HW_GB_PLAYER_DETECTION, IDLE_LOOP_NONE, false }, - { "BPPE", SAVEDATA_SRAM, HW_GB_PLAYER_DETECTION, IDLE_LOOP_NONE, false }, - { "BPPP", SAVEDATA_SRAM, HW_GB_PLAYER_DETECTION, IDLE_LOOP_NONE, false }, - { "BPPU", SAVEDATA_SRAM, HW_GB_PLAYER_DETECTION, IDLE_LOOP_NONE, false }, - // Pokemon Ruby { "AXVJ", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE, false }, { "AXVE", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE, false }, diff --git a/src/gba/renderers/gl.c b/src/gba/renderers/gl.c index 101510461..661443a0c 100644 --- a/src/gba/renderers/gl.c +++ b/src/gba/renderers/gl.c @@ -426,7 +426,7 @@ static const char* const _renderObj = "uniform ivec4 inflags;\n" "uniform mat2x2 transform;\n" "uniform ivec4 dims;\n" - "uniform ivec4 objwin;\n" + "uniform ivec3 objwin;\n" "uniform ivec4 mosaic;\n" "uniform int cyclesRemaining[160];\n" "OUT(0) out vec4 color;\n" @@ -459,7 +459,7 @@ static const char* const _renderObj = " color = pix;\n" " flags = inflags;\n" " gl_FragDepth = float(flags.x) / 16.;\n" - " window = ivec4(objwin.yzw, 0);\n" + " window = ivec4(objwin, 0);\n" "}"; static const struct GBAVideoGLUniform _uniformsObjPriority[] = { @@ -533,20 +533,15 @@ static const char* const _renderWindow = "}\n" "void main() {\n" - " int dispflags = (dispcnt & 0x1F) | 0x20;\n" - " if ((dispcnt & 0xE0) == 0) {\n" - " window = ivec4(dispflags, blend, 0);\n" - " } else {\n" - " ivec4 windowFlags = ivec4(flags.z, blend, 0);\n" - " int top = int(texCoord.y);\n" - " int bottom = max(top - 1, 0);\n" - " if ((dispcnt & 0x20) != 0 && crop(interpolate(vec4(win0[top]), vec4(win0[bottom])))) { \n" - " windowFlags.x = flags.x;\n" - " } else if ((dispcnt & 0x40) != 0 && crop(interpolate(vec4(win1[top]), vec4(win1[bottom])))) {\n" - " windowFlags.x = flags.y;\n" - " }\n" - " window = windowFlags;\n" + " ivec4 windowFlags = ivec4(flags.z, blend, 0);\n" + " int top = int(texCoord.y);\n" + " int bottom = max(top - 1, 0);\n" + " if ((dispcnt & 0x20) != 0 && crop(interpolate(vec4(win0[top]), vec4(win0[bottom])))) { \n" + " windowFlags.x = flags.x;\n" + " } else if ((dispcnt & 0x40) != 0 && crop(interpolate(vec4(win1[top]), vec4(win1[bottom])))) {\n" + " windowFlags.x = flags.y;\n" " }\n" + " window = windowFlags;\n" "}\n"; static const struct GBAVideoGLUniform _uniformsFinalize[] = { @@ -677,7 +672,7 @@ void GBAVideoGLRendererCreate(struct GBAVideoGLRenderer* renderer) { for (i = 0; i < 128; ++i) { renderer->d.highlightOBJ[i] = false; } - renderer->d.highlightColor = 0xFFFFFF; + renderer->d.highlightColor = M_COLOR_WHITE; renderer->d.highlightAmount = 0; renderer->scale = 1; @@ -735,7 +730,7 @@ static void _initFramebufferTextureEx(GLuint tex, GLenum internalFormat, GLenum glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, scale > 0 ? GBA_VIDEO_HORIZONTAL_PIXELS * scale : 8, GBA_VIDEO_VERTICAL_PIXELS * (scale > 0 ? scale : 1), 0, format, type, 0); - glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex, 0); } static void _initFramebufferTexture(GLuint tex, GLenum format, GLenum attachment, int scale) { @@ -754,7 +749,7 @@ static void _initFramebuffers(struct GBAVideoGLRenderer* glRenderer) { _initFramebufferTextureEx(glRenderer->layers[GBA_GL_TEX_BACKDROP_FLAGS], GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, GL_COLOR_ATTACHMENT1, 0); glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_WINDOW]); - _initFramebufferTextureEx(glRenderer->layers[GBA_GL_TEX_WINDOW], GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, GL_COLOR_ATTACHMENT0, glRenderer->scale); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glRenderer->layers[GBA_GL_TEX_WINDOW], 0); glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OUTPUT]); _initFramebufferTexture(glRenderer->outputTex, GL_RGB, GL_COLOR_ATTACHMENT0, glRenderer->scale); @@ -1422,19 +1417,17 @@ void _drawScanlines(struct GBAVideoGLRenderer* glRenderer, int y) { glDrawBuffers(2, (GLenum[]) { GL_NONE, GL_COLOR_ATTACHMENT1 }); int i; for (i = 0; i < 4; ++i) { - glScissor(i + 1, glRenderer->firstY, i + 2, y - glRenderer->firstY + 1); + glScissor(i + 1, glRenderer->firstY, 1, y - glRenderer->firstY + 1); glClearBufferiv(GL_COLOR, 1, (GLint[]) { glRenderer->bg[i].priority, glRenderer->bg[i].target1 | (glRenderer->bg[i].target2 << 1) | (glRenderer->blendEffect << 2), glRenderer->blda, 0 }); } - glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); GBAVideoGLRendererDrawWindow(glRenderer, y); if (GBARegisterDISPCNTIsObjEnable(glRenderer->dispcnt) && !glRenderer->d.disableOBJ) { glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - glEnable(GL_STENCIL_TEST); glDepthFunc(GL_LESS); for (i = 0; i < glRenderer->oamMax; ++i) { struct GBAVideoRendererSprite* sprite = &glRenderer->sprites[i]; @@ -1722,20 +1715,21 @@ void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GB glUniformMatrix2fv(uniforms[GBA_GL_OBJ_TRANSFORM], 1, GL_FALSE, (GLfloat[]) { flipX, 0, 0, flipY }); } glUniform4i(uniforms[GBA_GL_OBJ_DIMS], width, height, totalWidth, totalHeight); + glDisable(GL_STENCIL_TEST); if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_OBJWIN) { // OBJWIN writes do not affect pixel priority glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); - glStencilMask(GL_FALSE); + glStencilMask(0); int window = renderer->objwin & 0x3F; - glUniform4i(uniforms[GBA_GL_OBJ_OBJWIN], 1, window, renderer->bldb, renderer->bldy); + glUniform3i(uniforms[GBA_GL_OBJ_OBJWIN], window, renderer->bldb, renderer->bldy); glDrawBuffers(3, (GLenum[]) { GL_NONE, GL_NONE, GL_COLOR_ATTACHMENT2 }); } else { glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); - glStencilMask(GL_TRUE); + glStencilMask(1); glStencilFunc(GL_ALWAYS, 1, 1); - glUniform4i(uniforms[GBA_GL_OBJ_OBJWIN], 0, 0, 0, 0); + glUniform3i(uniforms[GBA_GL_OBJ_OBJWIN], 0, 0, 0); glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }); } if (GBAObjAttributesAIsMosaic(sprite->a) && GBAObjAttributesAGetMode(sprite->a) != OBJ_MODE_OBJWIN) { @@ -1755,6 +1749,7 @@ void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GB // Update the pixel priority for already-written pixels shader = &renderer->objShader[2]; uniforms = shader->uniforms; + glEnable(GL_STENCIL_TEST); glStencilFunc(GL_EQUAL, 1, 1); glUseProgram(shader->program); glDrawBuffers(2, (GLenum[]) { GL_NONE, GL_COLOR_ATTACHMENT1 }); @@ -1874,17 +1869,29 @@ void GBAVideoGLRendererDrawWindow(struct GBAVideoGLRenderer* renderer, int y) { glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[GBA_GL_FBO_WINDOW]); glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale); glScissor(0, renderer->firstY * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->scale * (y - renderer->firstY + 1)); - glUseProgram(shader->program); - glBindVertexArray(shader->vao); - glUniform2i(uniforms[GBA_GL_VS_LOC], y - renderer->firstY + 1, renderer->firstY); - glUniform2i(uniforms[GBA_GL_VS_MAXPOS], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS); - glUniform1i(uniforms[GBA_GL_WIN_DISPCNT], renderer->dispcnt >> 8); - glUniform2i(uniforms[GBA_GL_WIN_BLEND], renderer->bldb, renderer->bldy); - glUniform3i(uniforms[GBA_GL_WIN_FLAGS], renderer->winN[0].control, renderer->winN[1].control, renderer->winout); - glUniform4iv(uniforms[GBA_GL_WIN_WIN0], GBA_VIDEO_VERTICAL_PIXELS, renderer->winNHistory[0]); - glUniform4iv(uniforms[GBA_GL_WIN_WIN1], GBA_VIDEO_VERTICAL_PIXELS, renderer->winNHistory[1]); glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + switch (renderer->dispcnt & 0xE000) { + case 0x0000: + // No windows are enabled + glClearBufferiv(GL_COLOR, 0, (GLint[]) { ((renderer->dispcnt >> 8) & 0x1F) | 0x20, renderer->bldb, renderer->bldy, 0 }); + break; + case 0x8000: + // Only OBJWIN is enabled + glClearBufferiv(GL_COLOR, 0, (GLint[]) { renderer->winout, renderer->bldb, renderer->bldy, 0 }); + break; + default: + glUseProgram(shader->program); + glBindVertexArray(shader->vao); + glUniform2i(uniforms[GBA_GL_VS_LOC], y - renderer->firstY + 1, renderer->firstY); + glUniform2i(uniforms[GBA_GL_VS_MAXPOS], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS); + glUniform1i(uniforms[GBA_GL_WIN_DISPCNT], renderer->dispcnt >> 8); + glUniform2i(uniforms[GBA_GL_WIN_BLEND], renderer->bldb, renderer->bldy); + glUniform3i(uniforms[GBA_GL_WIN_FLAGS], renderer->winN[0].control, renderer->winN[1].control, renderer->winout); + glUniform4iv(uniforms[GBA_GL_WIN_WIN0], GBA_VIDEO_VERTICAL_PIXELS, renderer->winNHistory[0]); + glUniform4iv(uniforms[GBA_GL_WIN_WIN1], GBA_VIDEO_VERTICAL_PIXELS, renderer->winNHistory[1]); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + break; + } } void GBAVideoGLRendererSetScale(struct GBAVideoGLRenderer* renderer, int scale) { diff --git a/src/gba/renderers/software-private.h b/src/gba/renderers/software-private.h index bdaeffae8..8dfb0dec3 100644 --- a/src/gba/renderers/software-private.h +++ b/src/gba/renderers/software-private.h @@ -35,8 +35,6 @@ int GBAVideoSoftwareRendererPreprocessSpriteLayer(struct GBAVideoSoftwareRendere static inline unsigned _brighten(unsigned color, int y); static inline unsigned _darken(unsigned color, int y); -static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB); - // We stash the priority on the top bits so we can do a one-operator comparison // The lower the number, the higher the priority, and sprites take precedence over backgrounds @@ -45,7 +43,7 @@ static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB) static inline void _compositeBlendObjwin(struct GBAVideoSoftwareRenderer* renderer, int x, uint32_t color, uint32_t current) { if (color >= current) { if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) { - color = _mix(renderer->alphaA[x], current, renderer->alphaB[x], color); + color = mColorMix5Bit(renderer->alphaA[x], current, renderer->alphaB[x], color); } else { color = current & (0x00FFFFFF | FLAG_REBLEND | FLAG_OBJWIN); } @@ -58,7 +56,7 @@ static inline void _compositeBlendObjwin(struct GBAVideoSoftwareRenderer* render static inline void _compositeBlendNoObjwin(struct GBAVideoSoftwareRenderer* renderer, int x, uint32_t color, uint32_t current) { if (color >= current) { if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) { - color = _mix(renderer->alphaA[x], current, renderer->alphaB[x], color); + color = mColorMix5Bit(renderer->alphaA[x], current, renderer->alphaB[x], color); } else { color = current & (0x00FFFFFF | FLAG_REBLEND | FLAG_OBJWIN); } @@ -285,66 +283,4 @@ static inline unsigned _darken(unsigned color, int y) { return c; } -static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB) { - unsigned c = 0; - unsigned a, b; -#ifdef COLOR_16_BIT -#ifdef COLOR_5_6_5 - a = colorA & 0xF81F; - b = colorB & 0xF81F; - a |= (colorA & 0x7C0) << 16; - b |= (colorB & 0x7C0) << 16; - c = ((a * weightA + b * weightB) / 16); - if (c & 0x08000000) { - c = (c & ~0x0FC00000) | 0x07C00000; - } - if (c & 0x0020) { - c = (c & ~0x003F) | 0x001F; - } - if (c & 0x10000) { - c = (c & ~0x1F800) | 0xF800; - } - c = (c & 0xF81F) | ((c >> 16) & 0x07C0); -#else - a = colorA & 0x7C1F; - b = colorB & 0x7C1F; - a |= (colorA & 0x3E0) << 16; - b |= (colorB & 0x3E0) << 16; - c = ((a * weightA + b * weightB) / 16); - if (c & 0x04000000) { - c = (c & ~0x07E00000) | 0x03E00000; - } - if (c & 0x0020) { - c = (c & ~0x003F) | 0x001F; - } - if (c & 0x8000) { - c = (c & ~0xF800) | 0x7C00; - } - c = (c & 0x7C1F) | ((c >> 16) & 0x03E0); -#endif -#else - a = colorA & 0xFF; - b = colorB & 0xFF; - c |= ((a * weightA + b * weightB) / 16) & 0x1FF; - if (c & 0x00000100) { - c = 0x000000FF; - } - - a = colorA & 0xFF00; - b = colorB & 0xFF00; - c |= ((a * weightA + b * weightB) / 16) & 0x1FF00; - if (c & 0x00010000) { - c = (c & 0x000000FF) | 0x0000FF00; - } - - a = colorA & 0xFF0000; - b = colorB & 0xFF0000; - c |= ((a * weightA + b * weightB) / 16) & 0x1FF0000; - if (c & 0x01000000) { - c = (c & 0x0000FFFF) | 0x00FF0000; - } -#endif - return c; -} - #endif diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c index e416c8c6e..8c669a5f6 100644 --- a/src/gba/renderers/video-software.c +++ b/src/gba/renderers/video-software.c @@ -80,7 +80,7 @@ void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) { for (i = 0; i < 128; ++i) { renderer->d.highlightOBJ[i] = false; } - renderer->d.highlightColor = GBA_COLOR_WHITE; + renderer->d.highlightColor = M_COLOR_WHITE; renderer->d.highlightAmount = 0; renderer->temporaryBuffer = 0; @@ -96,7 +96,7 @@ static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) { color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y]; int x; for (x = 0; x < softwareRenderer->masterEnd; ++x) { - row[x] = GBA_COLOR_WHITE; + row[x] = M_COLOR_WHITE; } } } @@ -578,7 +578,7 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) { int x; for (x = 0; x < softwareRenderer->masterEnd; ++x) { - row[x] = GBA_COLOR_WHITE; + row[x] = M_COLOR_WHITE; } return; } @@ -944,7 +944,7 @@ void GBAVideoSoftwareRendererPostprocessBuffer(struct GBAVideoSoftwareRenderer* for (; x < end; ++x) { uint32_t color = softwareRenderer->row[x]; if (color & FLAG_TARGET_1) { - softwareRenderer->row[x] = _mix(softwareRenderer->alphaB[x], backdrop, softwareRenderer->alphaA[x], color); + softwareRenderer->row[x] = mColorMix5Bit(softwareRenderer->alphaB[x], backdrop, softwareRenderer->alphaA[x], color); } } } @@ -1044,8 +1044,8 @@ static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) { if (highlightAmount) { for (i = 0; i < 512; ++i) { - renderer->highlightPalette[i] = _mix(0x10 - highlightAmount, renderer->normalPalette[i], highlightAmount, renderer->d.highlightColor); - renderer->highlightVariantPalette[i] = _mix(0x10 - highlightAmount, renderer->variantPalette[i], highlightAmount, renderer->d.highlightColor); + renderer->highlightPalette[i] = mColorMix5Bit(0x10 - highlightAmount, renderer->normalPalette[i], highlightAmount, renderer->d.highlightColor); + renderer->highlightVariantPalette[i] = mColorMix5Bit(0x10 - highlightAmount, renderer->variantPalette[i], highlightAmount, renderer->d.highlightColor); } } } diff --git a/src/platform/qt/FrameView.cpp b/src/platform/qt/FrameView.cpp index 42091a494..6fbcbc584 100644 --- a/src/platform/qt/FrameView.cpp +++ b/src/platform/qt/FrameView.cpp @@ -275,7 +275,7 @@ void FrameView::injectGBA() { gba->video.renderer->highlightOBJ[i] = false; } QPalette palette; - gba->video.renderer->highlightColor = palette.color(QPalette::HighlightedText).rgb(); + gba->video.renderer->highlightColor = M_RGB8_TO_NATIVE(palette.color(QPalette::Highlight).rgb()); gba->video.renderer->highlightAmount = sin(m_glowFrame * M_PI / 30) * 48 + 64; if (!m_overrideBackdrop.isValid()) { QRgb backdrop = M_RGB5_TO_RGB8(gba->video.palette[0]) | 0xFF000000; @@ -318,7 +318,12 @@ void FrameView::updateTilesGB(bool) { m_queue.clear(); { CoreController::Interrupter interrupter(m_controller); - uint8_t* io = static_cast(m_controller->thread()->core->board)->memory.io; + QPointF origin; + GB* gb = static_cast(m_controller->thread()->core->board); + if (gb->video.sgbBorders && (gb->model & GB_MODEL_SGB)) { + origin = QPointF(48, 40); + } + uint8_t* io = gb->memory.io; GBRegisterLCDC lcdc = io[GB_REG_LCDC]; for (int sprite = 0; sprite < 40; ++sprite) { @@ -338,7 +343,7 @@ void FrameView::updateTilesGB(bool) { { LayerId::SPRITE, sprite }, !m_disabled.contains({ LayerId::SPRITE, sprite }), QPixmap::fromImage(obj), - {}, offset, false, false + {}, offset + origin, false, false }); if (m_queue.back().image.hasAlpha()) { m_queue.back().mask = QRegion(m_queue.back().image.mask()); @@ -352,7 +357,7 @@ void FrameView::updateTilesGB(bool) { { LayerId::WINDOW }, !m_disabled.contains({ LayerId::WINDOW }), {}, - {}, {0, 0}, false, false + {}, origin, false, false }); } @@ -360,7 +365,7 @@ void FrameView::updateTilesGB(bool) { { LayerId::BACKGROUND }, !m_disabled.contains({ LayerId::BACKGROUND }), {}, - {}, {0, 0}, false, false + {}, origin, false, false }); updateRendered(); @@ -370,6 +375,15 @@ void FrameView::updateTilesGB(bool) { void FrameView::injectGB() { mVideoLogger* logger = m_vl->videoLogger; + GB* gb = static_cast(m_vl->board); + gb->video.renderer->highlightBG = false; + gb->video.renderer->highlightWIN = false; + for (int i = 0; i < 40; ++i) { + gb->video.renderer->highlightOBJ[i] = false; + } + QPalette palette; + gb->video.renderer->highlightColor = M_RGB8_TO_NATIVE(palette.color(QPalette::Highlight).rgb()); + gb->video.renderer->highlightAmount = sin(m_glowFrame * M_PI / 30) * 48 + 64; m_vl->reset(m_vl); for (const Layer& layer : m_queue) { @@ -378,12 +392,21 @@ void FrameView::injectGB() { if (!layer.enabled) { mVideoLoggerInjectOAM(logger, layer.id.index << 2, 0); } + if (layer.id == m_active) { + gb->video.renderer->highlightOBJ[layer.id.index] = true; + } break; case LayerId::BACKGROUND: m_vl->enableVideoLayer(m_vl, GB_LAYER_BACKGROUND, layer.enabled); + if (layer.id == m_active) { + gb->video.renderer->highlightBG = true; + } break; case LayerId::WINDOW: m_vl->enableVideoLayer(m_vl, GB_LAYER_WINDOW, layer.enabled); + if (layer.id == m_active) { + gb->video.renderer->highlightWIN = true; + } break; } } @@ -510,6 +533,12 @@ void FrameView::newVl() { m_vl->loadROM(m_vl, m_currentFrame); m_currentFrame = nullptr; mCoreInitConfig(m_vl, nullptr); +#ifdef M_CORE_GB + if (m_controller->platform() == PLATFORM_GB) { + mCoreConfigSetIntValue(&m_vl->config, "sgb.borders", static_cast(m_controller->thread()->core->board)->video.sgbBorders); + m_vl->reloadConfigOption(m_vl, "sgb.borders", nullptr); + } +#endif unsigned width, height; m_vl->desiredVideoDimensions(m_vl, &width, &height); m_framebuffer = QImage(width, height, QImage::Format_RGBX8888); diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index aa9c47ae4..eae0d1012 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -412,6 +412,7 @@ void SettingsView::updateConfig() { saveSetting("gba.audioHle", m_ui.audioHle); saveSetting("dynamicTitle", m_ui.dynamicTitle); saveSetting("videoScale", m_ui.videoScale); + saveSetting("gba.forceGbp", m_ui.forceGbp); if (m_ui.audioBufferSize->currentText().toInt() > 8192) { m_ui.audioBufferSize->setCurrentText("8192"); @@ -606,6 +607,7 @@ void SettingsView::reloadConfig() { loadSetting("useDiscordPresence", m_ui.useDiscordPresence); loadSetting("gba.audioHle", m_ui.audioHle); loadSetting("dynamicTitle", m_ui.dynamicTitle); + loadSetting("gba.forceGbp", m_ui.forceGbp); m_ui.libraryStyle->setCurrentIndex(loadSetting("libraryStyle").toInt()); diff --git a/src/platform/qt/SettingsView.ui b/src/platform/qt/SettingsView.ui index 404a1c930..04d232d59 100644 --- a/src/platform/qt/SettingsView.ui +++ b/src/platform/qt/SettingsView.ui @@ -873,21 +873,21 @@ - + Qt::Horizontal - + Savestate extra data: - + Screenshot @@ -897,7 +897,7 @@ - + Save data @@ -907,7 +907,7 @@ - + Cheat codes @@ -917,21 +917,21 @@ - + Qt::Horizontal - + Load extra data: - + Screenshot @@ -941,20 +941,27 @@ - + Save data - + Cheat codes + + + + Enable Game Boy Player features by default + + + diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index cb2be339e..ae1b022f7 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -1597,7 +1597,6 @@ void Window::setupMenu(QMenuBar* menubar) { addGameAction(tr("View &tiles..."), "tileWindow", openControllerTView(), "tools"); addGameAction(tr("View &map..."), "mapWindow", openControllerTView(), "tools"); -#ifdef M_CORE_GBA Action* frameWindow = addGameAction(tr("&Frame inspector..."), "frameWindow", [this]() { if (!m_frameView) { m_frameView = new FrameView(m_controller); @@ -1613,8 +1612,6 @@ void Window::setupMenu(QMenuBar* menubar) { } m_frameView->show(); }, "tools"); - m_platformActions.insert(PLATFORM_GBA, frameWindow); -#endif addGameAction(tr("View memory..."), "memoryView", openControllerTView(), "tools"); addGameAction(tr("Search memory..."), "memorySearch", openControllerTView(), "tools"); diff --git a/src/platform/qt/ts/medusa-emu-de.ts b/src/platform/qt/ts/medusa-emu-de.ts index 8682e073f..be660854d 100644 --- a/src/platform/qt/ts/medusa-emu-de.ts +++ b/src/platform/qt/ts/medusa-emu-de.ts @@ -306,8 +306,8 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.GIFView - Record GIF/APNG - GIF/APNG aufzeichnen + Record GIF/WebP/APNG + GIF/WebP/APNG aufzeichnen @@ -1392,22 +1392,22 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. QGBA::CoreController - + Failed to open save file: %1 Fehler beim Öffnen der Speicherdatei: %1 - + Failed to open game file: %1 Fehler beim Öffnen der Spieldatei: %1 - + Failed to open snapshot file for reading: %1 Konnte Snapshot-Datei %1 nicht zum Lesen öffnen - + Failed to open snapshot file for writing: %1 Konnte Snapshot-Datei %1 nicht zum Schreiben öffnen @@ -1433,52 +1433,52 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. QGBA::FrameView - + Export frame Bild exportieren - + Portable Network Graphics (*.png) Portable Network Graphics (*.png) - + None Keine - + Background Hintergrund - + Window Fenster - + Objwin Objwin - + Sprite Sprite - + Backdrop Hintergrund - + Frame Frame - + %1 %2 %1 %2 @@ -1494,22 +1494,22 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. QGBA::GBAKeyEditor - + Clear Button Button löschen - + Clear Analog Analog löschen - + Refresh Aktualisieren - + Set all Alle belegen @@ -3267,12 +3267,12 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.Fehler beim Laden der Eingabedatei: %1 - + TBL TBL - + ISO-8859-1 ISO-8859-1 @@ -3280,22 +3280,22 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. QGBA::MemorySearch - + (%0/%1×) (%0/%1×) - + (⅟%0×) (⅟%0×) - + (%0×) (%0×) - + %1 byte%2 %1 byte%2 @@ -3701,12 +3701,12 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.Video-Logs (*.mvl) - + Crash Absturz - + The game has crashed with the following error: %1 @@ -3715,52 +3715,52 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. - + Unimplemented BIOS call Nicht implementierter BIOS-Aufruf - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Dieses Spiel verwendet einen BIOS-Aufruf, der nicht implementiert ist. Bitte verwenden Sie für die beste Spielerfahrung das offizielle BIOS. - + Really make portable? Portablen Modus wirklich aktivieren? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Diese Einstellung wird den Emulator so konfigurieren, dass er seine Konfiguration aus dem gleichen Verzeichnis wie die Programmdatei lädt. Möchten Sie fortfahren? - + Restart needed Neustart benötigt - + Some changes will not take effect until the emulator is restarted. Einige Änderungen werden erst übernommen, wenn der Emulator neu gestartet wurde. - + - Player %1 of %2 - Spieler %1 von %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 Bilder/Sekunde) - %4 @@ -4151,12 +4151,12 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.e-Reader-Karte (*.raw *.bin *.bmp) - + Couldn't Start Konnte nicht gestartet werden - + Could not start game. Spiel konnte nicht gestartet werden. @@ -4748,7 +4748,13 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.Bildwiederholrate in der Titelleiste anzeigen - + + Enable Game Boy Player features by default + Game Boy Player-Features +standardmäßig aktivieren + + + Super Game Boy/Game Boy Color model: Super Game Boy/Game Boy Color-Modell: @@ -4799,107 +4805,107 @@ Titelleiste anzeigen Vorlauf-Geschwindigkeit (halten): - + Video renderer: Video-Renderer: - + Software Software - + OpenGL OpenGL - + OpenGL enhancements OpenGL-Verbesserungen - + High-resolution scale: Hochauflösende Skalierung: - + XQ GBA audio (experimental) XQ GBA-Audio (experimentell) - + Cheats Cheats - + Log to file In Datei protokollieren - + Log to console Auf die Konsole protokollieren - + Select Log File Protokoll-Datei auswählen - + Game Boy-only model: Game Boy-Modell: - + Game Boy Color-only model: Game Boy Color-Modell: - + Game Boy/Game Boy Color model: Game Boy/Game Boy Color-Modell: - + Camera: Kamera: - + Default BG colors: Standard-Hintergrundfarben: - + Default sprite colors 1: Standard-Sprite-Farben 1: - + Default sprite colors 2: Standard-Sprite-Farben 2: - + Use GBC colors in GB games Verwende GBC-Farben in GB-Spielen - + Super Game Boy borders Super Game Boy-Rahmen - + Super Game Boy model: Super Game Boy-Modell: - + Camera driver: Kamera-Treiber: @@ -4930,15 +4936,15 @@ Titelleiste anzeigen in Arbeitsspeicher vorladen - - - - - - - - - + + + + + + + + + Browse Durchsuchen @@ -4953,20 +4959,20 @@ in Arbeitsspeicher vorladen DS-BIOS 9: - + Use BIOS file if found BIOS-Datei verwenden, wenn vorhanden - + Skip BIOS intro BIOS-Intro überspringen - + × × @@ -5065,22 +5071,22 @@ wenn vorhanden Autofeuer-Intervall: - + (240×160) (240×160) - + GB BIOS file: Datei mit GB-BIOS: - + GBA BIOS file: Datei mit GBA-BIOS: - + GBC BIOS file: Datei mit GBC-BIOS: @@ -5090,36 +5096,36 @@ wenn vorhanden DS-Firmware: - + SGB BIOS file: Datei mit SGB-BIOS: - + Save games Spielstände - - - - - + + + + + Same directory as the ROM Verzeichnis der ROM-Datei - + Save states Savestates - + Screenshots Screenshots - + Patches Patches diff --git a/src/platform/qt/ts/mgba-pt_BR.ts b/src/platform/qt/ts/mgba-pt_BR.ts new file mode 100644 index 000000000..b5d3cf545 --- /dev/null +++ b/src/platform/qt/ts/mgba-pt_BR.ts @@ -0,0 +1,5445 @@ + + + + + AboutScreen + + + About + Sobre + + + + <a href="http://mgba.io/">Website</a> • <a href="https://forums.mgba.io/">Forums / Support</a> • <a href="https://patreon.com/mgba">Donate</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">Source</a> + <a href="http://mgba.io/">Site</a> • <a href="https://forums.mgba.io/">Fóruns / Suporte</a> • <a href="https://patreon.com/mgba">Doar</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">Fonte</a> + + + + Branch: <tt>{gitBranch}</tt><br/>Revision: <tt>{gitCommit}</tt> + Branch: <tt>{gitBranch}</tt><br/>Revisão: <tt>{gitCommit}</tt> + + + + {projectName} + {projectName} + + + + {projectName} would like to thank the following patrons from Patreon: + {projectName} gostaria de agradecer aos seguintes patrões do Patreon: + + + + © 2013 – 2020 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0 +Game Boy Advance is a registered trademark of Nintendo Co., Ltd. + © 2013 – 2020 Jeffrey Pfau, licenciado sob a Licença Pública Mozilla, versão 2.0 +Game Boy Advance é uma marca registrada da Nintendo Co., Ltd. + + + + {projectVersion} + {projectVersion} + + + + {logo} + {logo} + + + + {projectName} is an open-source Game Boy Advance emulator + {projectName} é um emulador de Game Boy Advance de Código Aberto + + + + {patrons} + {patrons} + + + + ArchiveInspector + + + Open in archive... + Abrir no arquivo... + + + + Loading... + Carregando... + + + + AssetTile + + + AssetTile + Conjunto de Blocos + + + + Tile # + Bloco # + + + + + 0 + 0 + + + + Palette # + Paleta # + + + + Address + Endereço + + + + 0x06000000 + 0x06000000 + + + + Red + Vermelho + + + + Green + Verde + + + + Blue + Azul + + + + + + 0x00 (00) + 0x00 (00) + + + + BattleChipView + + + BattleChip Gate + BattleChip Gate + + + + Chip name + Nome do chip + + + + Insert + Inserir + + + + Save + Salvar + + + + Load + Carregar + + + + Add + Adicionar + + + + Remove + Excluir + + + + Gate type + Gate type + + + + Ba&ttleChip Gate + Ba&ttleChip Gate + + + + Progress &Gate + Progress &Gate + + + + Beast &Link Gate + Beast &Link Gate + + + + Inserted + Inserido + + + + Chip ID + ID do Chip + + + + Update Chip data + Atualizar dados do Chip + + + + Show advanced + Mostrar opções avançadas + + + + CheatsView + + + Cheats + Cheats + + + + Remove + Excluir + + + + Save + Salvar + + + + Load + Carregar + + + + Add New Set + Adicionar Novo Conjunto + + + + Add + Adicionar + + + + Enter codes here... + Insira os códigos aqui... + + + + DebuggerConsole + + + Debugger + Depurador + + + + Enter command (try `help` for more info) + Digite o comando (ou `help` para mais informações) + + + + Break + Pausar + + + + FrameView + + + Inspect frame + Inspecionar quadro + + + + × + × + + + + Magnification + Ampliação + + + + Freeze frame + Congelar quadro + + + + Backdrop color + Cor de fundo + + + + Disable scanline effects + Desativar efeitos scanline + + + + Export + Exportar + + + + Reset + Resetar + + + + GIFView + + + Record GIF/APNG + Gravar GIF/APNG + + + + Loop + Repetir + + + + Start + Iniciar + + + + Stop + Parar + + + + Select File + Selecionar arquivo + + + + APNG + APNG + + + + GIF + GIF + + + + WebP + WebP + + + + Frameskip + Frameskip + + + + IOViewer + + + I/O Viewer + Visualizador de E/S + + + + 0x0000 + 0x0000 + + + + 2 + 2 + + + + 5 + 5 + + + + 4 + 4 + + + + 7 + 7 + + + + 0 + 0 + + + + 9 + 9 + + + + 1 + 1 + + + + 3 + 3 + + + + 8 + 8 + + + + C + C + + + + E + E + + + + 6 + 6 + + + + D + D + + + + F + F + + + + A + A + + + + B + B + + + + LibraryTree + + + Name + Nome + + + + Location + Localização + + + + Platform + Plataforma + + + + Size + Tamanho + + + + CRC32 + CRC32 + + + + LoadSaveState + + + + %1 State + Estado %1 + + + + + + + + + + + + No Save + Não salvar + + + + 1 + 1 + + + + 2 + 2 + + + + Cancel + Cancelar + + + + 3 + 3 + + + + 4 + 4 + + + + 5 + 5 + + + + 6 + 6 + + + + 7 + 7 + + + + 8 + 8 + + + + 9 + 9 + + + + LogView + + + Logs + Registros + + + + Enabled Levels + Níveis Habilitados + + + + Debug + Depurar + + + + Stub + Stub + + + + Info + Info + + + + Warning + Warning + + + + Error + Error + + + + Fatal + Fatal + + + + Game Error + Erro do Jogo + + + + Clear + Limpar + + + + Max Lines + Máximo de Linhas + + + + MapView + + + Maps + Mapas + + + + × + × + + + + Magnification + Ampliação + + + + Export + Exportar + + + + Copy + Copiar + + + + MemoryDump + + + Save Memory Range + Salvar Faixa de Memória + + + + Start Address: + Endereço Inicial: + + + + : + : + + + + + 0x + 0x + + + + Byte Count: + Número de bytes: + + + + Dump across banks + Salvar múltiplos bancos + + + + MemorySearch + + + Memory Search + Pesquisa de Memória + + + + Address + Endereço + + + + Current Value + Valor Atual + + + + + Type + Tipo + + + + Value + Valor + + + + Numeric + Numérico + + + + Text + Texto + + + + Width + Tamanho + + + + 1 Byte (8-bit) + 1 Byte (8-bit) + + + + 2 Bytes (16-bit) + 2 Bytes (16-bit) + + + + 4 Bytes (32-bit) + 4 Bytes (32-bit) + + + + Number type + Tipo de número + + + + Hexadecimal + Hexadecimal + + + + Search type + Tipo de pesquisa + + + + Equal to value + Igual ao valor + + + + Greater than value + Maior que o valor + + + + Less than value + Menor que o valor + + + + Unknown/changed + Desconhecido/alterado + + + + Changed by value + Alterado por valor + + + + Unchanged + Inalterado + + + + Increased + Aumentado + + + + Decreased + Diminuído + + + + Search ROM + Pesquisar ROM + + + + New Search + Nova Pesquisa + + + + Decimal + Decimal + + + + + Guess + Adivinhar + + + + Search Within + Buscar nos Resultados + + + + Open in Memory Viewer + Abrir no Visualizador de Memória + + + + Refresh + Atualizar + + + + MemoryView + + + Memory + Memória + + + + Inspect Address: + Inspecionar Endereço: + + + + : + : + + + + 0x + 0x + + + + Set Alignment: + Definir Alinhamento: + + + + &1 Byte + &1 Byte + + + + &2 Bytes + &2 Bytes + + + + &4 Bytes + &4 Bytes + + + + Unsigned Integer: + Unsigned Integer: + + + + Signed Integer: + Signed Integer: + + + + String: + String: + + + + Load TBL + Carregar TBL + + + + Copy Selection + Copiar Seleção + + + + Paste + Colar + + + + Save Selection + Salvar Seleção + + + + Save Range + Salvar Intervalo + + + + Load + Carregar + + + + ObjView + + + Sprites + Sprites + + + + + × + × + + + + Magnification + Ampliação + + + + Export + Exportar + + + + Attributes + Atributos + + + + Transform + Transformar + + + + Off + Desligado + + + + Palette + Paleta + + + + + + + 0 + 0 + + + + Copy + Copiar + + + + + +0.00 + +0.00 + + + + + +1.00 + +1.00 + + + + Matrix + Matriz + + + + Double Size + Tamanho Duplo + + + + + + + Return, Ctrl+R + Return, Ctrl+R + + + + Flipped + Invertido + + + + H + H + + + + V + V + + + + Mode + Modo + + + + Normal + Normal + + + + Mosaic + Mosaico + + + + Enabled + Habilitado + + + + Priority + Prioridade + + + + Tile + Bloco + + + + Geometry + Geometria + + + + Position + Posição + + + + , + , + + + + Dimensions + Dimensões + + + + + 8 + 8 + + + + Address + Endereço + + + + 0x07000000 + 0x07000000 + + + + OverrideView + + + Game Overrides + Game Overrides + + + + Game Boy Advance + Game Boy Advance + + + + + + + Autodetect + Autodetectar + + + + Realtime clock + Relógio em tempo real + + + + Gyroscope + Giroscópio + + + + Tilt + Inclinação + + + + Light sensor + Sensor de luz + + + + Rumble + Vibrar + + + + Save type + Tipo de salvamento + + + + + None + Nenhum + + + + SRAM + SRAM + + + + Flash 512kb + Flash 512kb + + + + Flash 1Mb + Flash 1Mb + + + + EEPROM + EEPROM + + + + Idle loop + Loop ocioso + + + + Game Boy Player features + Funções do Game Boy Player + + + + Game Boy + Game Boy + + + + Game Boy model + Modelo do Game Boy + + + + Game Boy (DMG) + Game Boy (DMG) + + + + Super Game Boy (SGB) + Super Game Boy (SGB) + + + + Game Boy Color (CGB) + Game Boy Color (CGB) + + + + Game Boy Advance (AGB) + Game Boy Advance (AGB) + + + + Memory bank controller + Controlador de banco de memória + + + + MBC1 + MBC1 + + + + MBC2 + MBC2 + + + + MBC3 + MBC3 + + + + MBC3 + RTC + MBC3 + RTC + + + + MBC5 + MBC5 + + + + MBC5 + Rumble + MBC5 + Rumble + + + + MBC6 + MBC6 + + + + MBC7 + MBC7 + + + + MMM01 + MMM01 + + + + Pocket Cam + Pocket Cam + + + + TAMA5 + TAMA5 + + + + HuC-1 + HuC-1 + + + + HuC-3 + HuC-3 + + + + Wisdom Tree (Unlicensed) + Wisdom Tree (Não licenciado) + + + + Pokémon Jade/Diamond (Unlicensed) + Pokémon Jade/Diamond (Não licenciado) + + + + Background Colors + Cores do Plano de Fundo + + + + Sprite Colors 1 + Cores de Sprite 1 + + + + Sprite Colors 2 + Cores de Sprite 2 + + + + PaletteView + + + Palette + Paleta + + + + Background + Plano de Fundo + + + + Objects + Objetos + + + + Selection + Seleção + + + + Red + Vermelho + + + + Green + Verde + + + + Blue + Azul + + + + + + 0x00 (00) + 0x00 (00) + + + + 16-bit value + 16-bit value + + + + Hex code + Código hexadecimal + + + + Palette index + Índice da paleta + + + + 0x0000 + 0x0000 + + + + #000000 + #000000 + + + + 0x000 (000) + 0x000 (000) + + + + Export BG + Exportar BG + + + + Export OBJ + Exportar OBJ + + + + PlacementControl + + + Adjust placement + Ajustar posicionamento + + + + All + Todos + + + + Offset + Deslocamento + + + + X + X + + + + Y + Y + + + + PrinterView + + + Game Boy Printer + Game Boy Printer + + + + Hurry up! + Se apresse! + + + + Tear off + Desligar + + + + × + × + + + + Magnification + Ampliação + + + + QGBA::AssetTile + + + %0%1%2 + %0%1%2 + + + + + + 0x%0 (%1) + 0x%0 (%1) + + + + QGBA::CheatsModel + + + (untitled) + (sem título) + + + + Failed to open cheats file: %1 + Falha ao abrir arquivo de cheats: %1 + + + + QGBA::CheatsView + + + + Add GameShark + Adicionar GameShark + + + + Add Pro Action Replay + Adicionar Pro Action Replay + + + + Add CodeBreaker + Adicionar CodeBreaker + + + + Add GameGenie + Adicionar GameGenie + + + + + Select cheats file + Selecionar arquivo de cheats + + + + QGBA::CoreController + + + Failed to open save file: %1 + Falha ao abrir o arquivo de salvamento: %1 + + + + Failed to open game file: %1 + Falha ao abrir o arquivo do jogo: %1 + + + + Failed to open snapshot file for reading: %1 + Falha ao abrir o arquivo de snapshot para leitura: %1 + + + + Failed to open snapshot file for writing: %1 + Falha ao abrir o arquivo de snapshot para escrita: %1 + + + + QGBA::CoreManager + + + Failed to open game file: %1 + Falha ao abrir o arquivo do jogo: %1 + + + + Could not load game. Are you sure it's in the correct format? + Não foi possível carregar o jogo. Tem certeza que está no formato correto? + + + + Failed to open save file. Is the save directory writable? + Falha ao abrir o arquivo de salvamento. O diretório para salvar tem permissão de escrita? + + + + QGBA::FrameView + + + Export frame + Exportar quadro + + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + + None + Nenhum + + + + Background + Plano de Fundo + + + + Window + Janela + + + + Sprite + Sprite + + + + Backdrop + Cor de Fundo + + + + %1 %2 + %1 %2 + + + + QGBA::GBAApp + + + Enable Discord Rich Presence + Habilitar o Discord Rich Presence + + + + QGBA::GBAKeyEditor + + + Clear Button + Limpar Botão + + + + Clear Analog + Limpar Analógico + + + + Refresh + Atualizar + + + + Set all + Definir todos + + + + QGBA::GDBWindow + + + Server settings + Configurações do servidor + + + + Local port + Porta local + + + + Bind address + Vincular endereço + + + + Break + Pausar + + + + Stop + Parar + + + + Start + Iniciar + + + + Crash + Travar + + + + Could not start GDB server + Não foi possível iniciar o servidor GDB + + + + QGBA::GIFView + + + Failed to open output file: %1 + Falha ao abrir arquivo de saída: %1 + + + + Select output file + Selecionar arquivo de saída + + + + Graphics Interchange Format (*.gif);;Animated Portable Network Graphics (*.png *.webp *.apng) + Graphics Interchange Format (*.gif);;Animated Portable Network Graphics (*.png *.webp *.apng) + + + + QGBA::IOViewer + + + Background mode + Modo do plano de fundo + + + + Mode 0: 4 tile layers + Modo 0: 4 camadas de bloco + + + + Mode 1: 2 tile layers + 1 rotated/scaled tile layer + Modo 1: 2 camadas de blocos + 1 camada de bloco rotacionado/escalonado + + + + Mode 2: 2 rotated/scaled tile layers + Modo 2: 2 camadas de blocos rotacionados/escalonados + + + + Mode 3: Full 15-bit bitmap + Modo 3: Bitmap 15-bit completo + + + + Mode 4: Full 8-bit bitmap + Modo 4: Bitmap 8-bits completo + + + + Mode 5: Small 15-bit bitmap + Modo 5: Bitmap 15-bits pequeno + + + + CGB Mode + Modo CGB + + + + Frame select + Selecionar quadro + + + + Unlocked HBlank + HBlank desbloqueado + + + + Linear OBJ tile mapping + Mapeamento linear do OBJ + + + + Force blank screen + Forçar limpeza da tela + + + + Enable background 0 + Habilitar plano de fundo 0 + + + + Enable background 1 + Habilitar plano de fundo 1 + + + + Enable background 2 + Habilitar plano de fundo 2 + + + + Enable background 3 + Habilitar plano de fundo 3 + + + + Enable OBJ + Habilitar OBJ + + + + Enable Window 0 + Habilitar Janela 0 + + + + Enable Window 1 + Habilitar Janela 1 + + + + Enable OBJ Window + Habilitar Janela do OBJ + + + + Currently in VBlank + Atualmente no VBlank + + + + Currently in HBlank + Atualmente no HBlank + + + + Currently in VCounter + Atualmente no VCounter + + + + Enable VBlank IRQ generation + Habilitar geração de VBlank IRQ + + + + Enable HBlank IRQ generation + Habilitar geração de HBlank IRQ + + + + Enable VCounter IRQ generation + Habilitar geração de VCounter IRQ + + + + VCounter scanline + Scanline do VCounter + + + + Current scanline + Scanline atual + + + + + + + Priority + Prioridade + + + + + + + Tile data base (* 16kB) + Base de dados de blocos (* 16kB) + + + + + + + Enable mosaic + Habilitar mosaico + + + + + + + Enable 256-color + Habilitar 256-cores + + + + + + + Tile map base (* 2kB) + Base do mapa de blocos (* 2 kB) + + + + + + + Background dimensions + Dimensões do plano de fundo + + + + + Overflow wraps + Overflow wraps + + + + + + + Horizontal offset + Deslocamento horizontal + + + + + + + Vertical offset + Deslocamento vertical + + + + + + + + + + + + + + + Fractional part + Parte fracionária + + + + + + + + + + + Integer part + Parte inteira + + + + + + + Integer part (bottom) + Parte inteira (inferior) + + + + + + + Integer part (top) + Parte inteira (superior) + + + + + End x + X final + + + + + Start x + X inicial + + + + + End y + Y final + + + + + Start y + Y inicial + + + + Window 0 enable BG 0 + Janela 0 habilitar BG 0 + + + + Window 0 enable BG 1 + Janela 0 habilitar BG 1 + + + + Window 0 enable BG 2 + Janela 0 habilitar BG 2 + + + + Window 0 enable BG 3 + Janela 0 habilitar BG 3 + + + + Window 0 enable OBJ + Janela 0 habilitar OBJ + + + + Window 0 enable blend + Janela 0 habilitar blend + + + + Window 1 enable BG 0 + Janela 1 habilitar BG 0 + + + + Window 1 enable BG 1 + Janela 1 habilitar BG 1 + + + + Window 1 enable BG 2 + Janela 1 habilitar BG 2 + + + + Window 1 enable BG 3 + Janela 1 habilitar BG 3 + + + + Window 1 enable OBJ + Janela 1 habilitar OBJ + + + + Window 1 enable blend + Janela 1 habilitar blend + + + + Outside window enable BG 0 + Fora da janela habilitar BG 0 + + + + Outside window enable BG 1 + Fora da janela habilitar BG 1 + + + + Outside window enable BG 2 + Fora da janela habilitar BG 2 + + + + Outside window enable BG 3 + Fora da janela habilitar BG 3 + + + + Outside window enable OBJ + Fora da janela habilitar OBJ + + + + Outside window enable blend + Fora da janela habilitar blend + + + + OBJ window enable BG 0 + Janela OBJ habilitar BG 0 + + + + OBJ window enable BG 1 + Janela OBJ habilitar BG 1 + + + + OBJ window enable BG 2 + Janela OBJ habilitar BG 2 + + + + OBJ window enable BG 3 + Janela OBJ habilitar BG 3 + + + + OBJ window enable OBJ + Janela OBJ habilitar OBJ + + + + OBJ window enable blend + Janela OBJ habilitar blend + + + + Background mosaic size vertical + Tamanho vertical do mosaico do plano de fundo + + + + Background mosaic size horizontal + Tamanho horizontal do mosaico do plano de fundo + + + + Object mosaic size vertical + Tamanho vertical do mosaico de objeto + + + + Object mosaic size horizontal + Tamanho horizontal do mosaico de objeto + + + + BG 0 target 1 + BG 0 alvo 1 + + + + BG 1 target 1 + BG 1 alvo 1 + + + + BG 2 target 1 + BG 2 alvo 1 + + + + BG 3 target 1 + BG 3 alvo 1 + + + + OBJ target 1 + OBJ alvo 1 + + + + Backdrop target 1 + Backdrop alvo 1 + + + + Blend mode + Blend mode + + + + Disabled + Desabilitado + + + + Additive blending + Blending aditivo + + + + Brighten + Clarear + + + + Darken + Escurecer + + + + BG 0 target 2 + BG 0 alvo 2 + + + + BG 1 target 2 + BG 1 alvo 2 + + + + BG 2 target 2 + BG 2 alvo 2 + + + + BG 3 target 2 + BG 3 alvo 2 + + + + OBJ target 2 + OBJ alvo 2 + + + + Backdrop target 2 + Backdrop alvo 2 + + + + Blend A (target 1) + Blend A (alvo 1) + + + + Blend B (target 2) + Blend B (alvo 2) + + + + Blend Y + Blend Y + + + + Sweep shifts + Sweep shifts + + + + Sweep subtract + Sweep subtract + + + + Sweep time (in 1/128s) + Sweep time (em 1/128s) + + + + + + + Sound length + Comprimento do som + + + + + Duty cycle + Ciclo de trabalho + + + + + + Envelope step time + Envelope step time + + + + + + Envelope increase + Envelope increase + + + + + + Initial volume + Volume inicial + + + + + + Sound frequency + Frequência de som + + + + + + + Timed + Programado + + + + + + + Reset + Resetar + + + + Double-size wave table + Double-size wave table + + + + Active wave table + Active wave table + + + + Enable channel 3 + Habilitar canal 3 + + + + Volume + Volume + + + + 0% + 0% + + + + + 100% + 100% + + + + + 50% + 50% + + + + + 25% + 25% + + + + + + + 75% + 75% + + + + Clock divider + Divisor de relógio + + + + Register stages + Register stages + + + + 15 + 15 + + + + 7 + 7 + + + + Shifter frequency + Frequência de turno + + + + PSG volume right + Volume PSG direito + + + + PSG volume left + Volume PSG esquerdo + + + + Enable channel 1 right + Habilitar canal 1 à direita + + + + Enable channel 2 right + Habilitar canal 2 à direita + + + + Enable channel 3 right + Habilitar canal 3 à direita + + + + Enable channel 4 right + Habilitar canal 4 à direita + + + + Enable channel 1 left + Habilitar canal 1 à esquerda + + + + Enable channel 2 left + Habilitar canal 2 esquerdo + + + + Enable channel 3 left + Habilitar canal 3 esquerdo + + + + Enable channel 4 left + Habilitar canal 4 esquerdo + + + + PSG master volume + Volume principal do PSG + + + + Loud channel A + Canal A alto + + + + Loud channel B + Canal B alto + + + + Enable channel A right + Habilitar canal A direito + + + + Enable channel A left + Ativar o canal A esquerdo + + + + Channel A timer + Temporizador do canal A + + + + + 0 + 0 + + + + + + + + + + + + 1 + 1 + + + + Channel A reset + Redefinir canal A + + + + Enable channel B right + Habilitar canal B direito + + + + Enable channel B left + Habilitar canal B esquerdo + + + + Channel B timer + Temporizador do canal B + + + + Channel B reset + Redefinir canal B + + + + Active channel 1 + Canal ativo 1 + + + + Active channel 2 + Canal ativo 2 + + + + Active channel 3 + Canal ativo 3 + + + + Active channel 4 + Canal ativo 4 + + + + Enable audio + Habilitar áudio + + + + Bias + Viés + + + + Resolution + Resolução + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sample + Amostra + + + + + + + + + + + Address (bottom) + Endereço (inferior) + + + + + + + + + + + Address (top) + Endereço (superior) + + + + + + + Word count + Contagem de palavras + + + + + + + Destination offset + Deslocamento de destino + + + + + + + + + + + Increment + Incrementar + + + + + + + + + + + Decrement + Decrementar + + + + + + + + + + + Fixed + Corrigido + + + + + + + Increment and reload + Incrementar e recarregar + + + + + + + Source offset + Deslocamento de origem + + + + + + + Repeat + Repetir + + + + + + + 32-bit + 32-bit + + + + + + + Start timing + Iniciar timing + + + + + + + Immediate + Imediato + + + + + + + + + VBlank + VBlank + + + + + + + + + HBlank + HBlank + + + + + + + + + + + + IRQ + IRQ + + + + + + + + + + + Enable + Habilitar + + + + + + Audio FIFO + Áudio FIFO + + + + Video Capture + Captura de Vídeo + + + + DRQ + DRQ + + + + + + + Value + Valor + + + + + + + Scale + Escala + + + + + + + 1/64 + 1/64 + + + + + + + 1/256 + 1/256 + + + + + + + 1/1024 + 1/1024 + + + + + + Cascade + Em cascata + + + + + A + A + + + + + B + B + + + + + Select + Select + + + + + Start + Start + + + + + Right + Direita + + + + + Left + Esquerda + + + + + Up + Cima + + + + + Down + Baixo + + + + + R + R + + + + + L + L + + + + Condition + Condição + + + + SC + SC + + + + SD + SD + + + + SI + SI + + + + SO + SO + + + + + VCounter + VCounter + + + + + Timer 0 + Cronômetro 0 + + + + + Timer 1 + Cronômetro 1 + + + + + Timer 2 + Cronômetro 2 + + + + + Timer 3 + Cronômetro 3 + + + + + SIO + SIO + + + + + DMA 0 + DMA 0 + + + + + DMA 1 + DMA 1 + + + + + DMA 2 + DMA 2 + + + + + DMA 3 + DMA 3 + + + + + Keypad + Keypad + + + + + Gamepak + Gamepak + + + + SRAM wait + Aguardar SRAM + + + + + + + + 4 + 4 + + + + + + + 3 + 3 + + + + + + + + 2 + 2 + + + + + + + + 8 + 8 + + + + Cart 0 non-sequential + Cart 0 non-sequential + + + + Cart 0 sequential + Cart 0 sequential + + + + Cart 1 non-sequential + Cart 1 non-sequential + + + + Cart 1 sequential + Cart 1 sequential + + + + Cart 2 non-sequential + Cart 2 non-sequential + + + + Cart 2 sequential + Cart 2 sequential + + + + PHI terminal + Terminal PHI + + + + Disable + Desabilitado + + + + 4.19MHz + 4.19MHz + + + + 8.38MHz + 8.38MHz + + + + 16.78MHz + 16.78MHz + + + + Gamepak prefetch + Pré-carregamento de Gamepak + + + + Enable IRQs + Habilitar IRQs + + + + QGBA::KeyEditor + + + + --- + --- + + + + Super (L) + Super (L) + + + + Super (R) + Super (R) + + + + Menu + Menu + + + + QGBA::LoadSaveState + + + Load State + Carregar Estado + + + + Save State + Salvar Estado + + + + Empty + Vazio + + + + Corrupted + Corrompido + + + + Slot %1 + Espaço %1 + + + + QGBA::LogConfigModel + + + + Default + Padrão + + + + Fatal + Fatal + + + + Error + Error + + + + Warning + Warning + + + + Info + Info + + + + Debug + Debug + + + + Stub + Stub + + + + Game Error + Erro do Jogo + + + + QGBA::LogController + + + [%1] %2: %3 + [%1] %2: %3 + + + + An error occurred + Ocorreu um erro + + + + DEBUG + DEBUG + + + + STUB + STUB + + + + INFO + INFO + + + + WARN + WARN + + + + ERROR + ERROR + + + + FATAL + FATAL + + + + GAME ERROR + GAME ERROR + + + + QGBA::MapView + + + Priority + Prioridade + + + + + Map base + Base do mapa + + + + + Tile base + Base do bloco + + + + Size + Tamanho + + + + + Offset + Deslocamento + + + + Xform + Xform + + + + Map Addr. + Endereço do Mapa + + + + Mirror + Espelhamento + + + + None + Nenhum + + + + Both + Ambos + + + + Horizontal + Horizontal + + + + Vertical + Vertical + + + + + + N/A + N/D + + + + Export map + Exportar mapa + + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + + QGBA::MemoryDump + + + Save memory region + Salvar região de memória + + + + Failed to open output file: %1 + Falha ao abrir arquivo de saída: %1 + + + + QGBA::MemoryModel + + + Copy selection + Copiar seleção + + + + Save selection + Salvar seleção + + + + Paste + Colar + + + + Load + Carregar + + + + All + Todos + + + + Load TBL + Carregar TBL + + + + Save selected memory + Salvar memória selecionada + + + + Failed to open output file: %1 + Falha ao abrir arquivo de saída: %1 + + + + Load memory + Carregar memória + + + + Failed to open input file: %1 + Falha ao abrir arquivo de entrada: %1 + + + + TBL + TBL + + + + ISO-8859-1 + ISO-8859-1 + + + + QGBA::MemorySearch + + + (%0/%1×) + (%0/%1×) + + + + (⅟%0×) + (⅟%0×) + + + + (%0×) + (%0×) + + + + %1 byte%2 + %1 byte%2 + + + + QGBA::ObjView + + + + 0x%0 + 0x%0 + + + + Off + Desligado + + + + + + + + + + + --- + --- + + + + Normal + Normal + + + + Trans + Trans + + + + OBJWIN + OBJWIN + + + + Invalid + Inválido + + + + + N/A + N/D + + + + Export sprite + Exportar sprite + + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + + QGBA::PaletteView + + + #%0 + #%0 + + + + 0x%0 + 0x%0 + + + + + + + 0x%0 (%1) + 0x%0 (%1) + + + + Export palette + Exportar paleta + + + + Windows PAL (*.pal);;Adobe Color Table (*.act) + Windows PAL (*.pal);;Adobe Color Table (*.act) + + + + Failed to open output palette file: %1 + Falha ao abrir arquivo de paleta de saída: %1 + + + + QGBA::ROMInfo + + + + + + + (unknown) + (desconhecido) + + + + + bytes + bytes + + + + (no database present) + (nenhum banco de dados) + + + + QGBA::SettingsView + + + + Qt Multimedia + Qt multimídia + + + + SDL + SDL + + + + Software (Qt) + Software (Qt) + + + + OpenGL + OpenGL + + + + OpenGL (force version 1.x) + OpenGL (forçar versão 1.x) + + + + None (Still Image) + Nenhum (Still Image) + + + + Keyboard + Teclado + + + + Controllers + Controles + + + + Shortcuts + Atalhos + + + + + Shaders + Shaders + + + + Select BIOS + Selecionar BIOS + + + + (%1×%2) + (%1×%2) + + + + QGBA::ShaderSelector + + + No shader active + Nenhum shader ativo + + + + Load shader + Carregar shader + + + + No shader loaded + Nenhum shader carregado + + + + by %1 + por %1 + + + + Preprocessing + Pré-processamento + + + + Pass %1 + Passar %1 + + + + QGBA::ShortcutModel + + + Action + Acão + + + + Keyboard + Teclado + + + + Gamepad + Controle + + + + QGBA::TileView + + + Export tiles + Exportar blocos + + + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + + Export tile + Exportar bloco + + + + QGBA::VideoView + + + Failed to open output video file: %1 + Falha ao abrir arquivo de vídeo de saída: %1 + + + + Native (%0x%1) + Nativo (%0x%1) + + + + Select output file + Selecione o arquivo de saída + + + + QGBA::Window + + + Game Boy Advance ROMs (%1) + ROMs de Game Boy Advance (%1) + + + + Game Boy ROMs (%1) + ROMs de Game Boy (%1) + + + + All ROMs (%1) + Todas as ROMs (%1) + + + + %1 Video Logs (*.mvl) + %1 Logs de Vídeo (*.mvl) + + + + Archives (%1) + Arquivos (%1) + + + + + + Select ROM + Selecionar ROM + + + + Select folder + Selecionar pasta + + + + Game Boy Advance save files (%1) + Arquivos salvos de Game Boy Advance (%1) + + + + + + Select save + Selecionar salvamento + + + + mGBA savestate files (%1) + Arquivos de savestate do mGBA (%1) + + + + + Select savestate + Selecionar savestate + + + + Select patch + Selecione correção + + + + Patches (*.ips *.ups *.bps) + Patches (*.ips *.ups *.bps) + + + + Select e-Reader dotcode + Selecione dotcode do e-Reader + + + + e-Reader card (*.raw *.bin *.bmp) + e-Reader card (*.raw *.bin *.bmp) + + + + Select image + Selecionar imagem + + + + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) + Arquivo de imagem (*.png *.gif *.jpg *.jpeg);;Todos os arquivos (*) + + + + + GameShark saves (*.sps *.xps) + GameShark saves (*.sps *.xps) + + + + Select video log + Selecionar registro de vídeo + + + + Video logs (*.mvl) + Video logs (*.mvl) + + + + Crash + Travamento + + + + The game has crashed with the following error: + +%1 + O jogo travou com o seguinte erro: + +%1 + + + + Unimplemented BIOS call + Chamada de BIOS não implementada + + + + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. + Este jogo usa uma chamada de BIOS que não está implementada. Por favor, use a BIOS oficial para uma melhor experiência. + + + + Really make portable? + Quer mesmo tornar portátil? + + + + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? + Isto fará com que o emulador carregue sua configuração a partir do mesmo diretório que o executável. Você quer continuar? + + + + Restart needed + É necessário reiniciar + + + + Some changes will not take effect until the emulator is restarted. + Algumas alterações não terão efeito até que o emulador seja reiniciado. + + + + - Player %1 of %2 + - Jogador %1 de %2 + + + + %1 - %2 + %1 - %2 + + + + %1 - %2 - %3 + %1 - %2 - %3 + + + + %1 - %2 (%3 fps) - %4 + %1 - %2 (%3 fps) - %4 + + + + &File + &Arquivo + + + + Load &ROM... + Carregar &ROM... + + + + Load ROM in archive... + Carregar ROM em arquivo... + + + + Add folder to library... + Adicionar pasta à biblioteca... + + + + Load alternate save... + Carregar salvamento alternativo... + + + + Load temporary save... + Carregar salvamento temporário... + + + + Load &patch... + Carregar &patch... + + + + Boot BIOS + Rodar BIOS + + + + Replace ROM... + Substituir ROM... + + + + ROM &info... + &Informações da ROM... + + + + Recent + Recente + + + + Make portable + Tornar portátil + + + + &Load state + &Carregar Estado + + + + About... + Sobre... + + + + Game Pak sensors... + Sensores de Game Pak... + + + + Clear + Limpar + + + + Load state file... + Carregar arquivo de estado... + + + + &Save state + &Salvar Estado + + + + Save state file... + Salvar arquivo de estado... + + + + Quick load + Carregamento rápido + + + + Quick save + Salvamento rápido + + + + Load recent + Carregar recente + + + + Save recent + Salvar recente + + + + Undo load state + Desfazer carregar estado + + + + Undo save state + Desfazer salvar estado + + + + + State &%1 + Estado &%1 + + + + Load camera image... + Carregar imagem da câmera... + + + + New multiplayer window + Nova janela multijogador + + + + E&xit + &Sair + + + + &Emulation + &Emulação + + + + &Reset + &Resetar + + + + Sh&utdown + &Desligar + + + + Yank game pak + Remover game pak + + + + &Pause + &Pausar + + + + &Next frame + &Próximo quadro + + + + Fast forward (held) + Avançar rápido (segurado) + + + + &Fast forward + Avanço &Rápido + + + + Fast forward speed + Velocidade de avanço + + + + Unbounded + Ilimitado + + + + %0x + %0x + + + + Rewind (held) + Retroceder (segurado) + + + + Re&wind + Re&troceder + + + + Step backwards + Voltar um passo + + + + Sync to &video + Sincronizar para &vídeo + + + + Sync to &audio + Sincronizar para &áudio + + + + Solar sensor + Sensor solar + + + + Increase solar level + Aumentar nível solar + + + + Decrease solar level + Diminuir nível solar + + + + Brightest solar level + Nível solar mais brilhante + + + + Darkest solar level + Nível solar mais escuro + + + + Brightness %1 + Brilho %1 + + + + Audio/&Video + Áudio/&Vídeo + + + + Frame size + Tamanho do quadro + + + + Toggle fullscreen + Alternar tela cheia + + + + Lock aspect ratio + Fixar proporção + + + + Force integer scaling + Forçar dimensionamento inteiro + + + + Bilinear filtering + Filtragem bilinear + + + + Frame&skip + &Salto de quadro + + + + Mute + Mudo + + + + FPS target + Meta de FPS + + + + Native (59.7275) + Nativo (59,7275) + + + + Take &screenshot + Capturar &tela + + + + F12 + F12 + + + + Game Boy Printer... + Game Boy Printer... + + + + BattleChip Gate... + BattleChip Gate... + + + + %1× + %1× + + + + Interframe blending + Interframe blending + + + + Record A/V... + Gravar A/V... + + + + Video layers + Camadas de vídeo + + + + Audio channels + Canais de áudio + + + + Adjust layer placement... + Ajustar posicionamento da camada... + + + + &Tools + &Ferramentas + + + + View &logs... + Visualizar &registros... + + + + Game &overrides... + Game &overrides... + + + + Couldn't Start + Não foi possível Iniciar + + + + Could not start game. + Não foi possível iniciar o jogo. + + + + Scan e-Reader dotcodes... + Escanear dotcode do e-Reader... + + + + Import GameShark Save... + Importar salvamento do GameShark... + + + + Export GameShark Save... + Exportar salvamento do GameShark... + + + + Record GIF/WebP/APNG... + Gravar GIF/WebP/APNG... + + + + &Cheats... + &Cheats... + + + + Settings... + Configurações... + + + + Open debugger console... + Abrir console de depuração... + + + + Start &GDB server... + Iniciar servidor &GDB... + + + + View &palette... + Visualizar &paleta... + + + + View &sprites... + Visualizar &sprites... + + + + View &tiles... + Visualizar &blocos... + + + + View &map... + Visualizar &mapa... + + + + &Frame inspector... + Inspetor de &quadro... + + + + View memory... + Visualizar memória... + + + + Search memory... + Pesquisar memória... + + + + View &I/O registers... + Visualizar registros de &E/S... + + + + Record debug video log... + Gravar log de vídeo de depuração... + + + + Stop debug video log + Parar log de vídeo de depuração + + + + Exit fullscreen + Sair da tela cheia + + + + GameShark Button (held) + Botão de GameShark (segurado) + + + + Autofire + Disparo automático + + + + Autofire A + Disparo automático A + + + + Autofire B + Disparo automático B + + + + Autofire L + Disparo automático L + + + + Autofire R + Disparo automático R + + + + Autofire Start + Disparo automático Start + + + + Autofire Select + Disparo automático Select + + + + Autofire Up + Disparo automático Cima + + + + Autofire Right + Disparo automático Direita + + + + Autofire Down + Disparo automático Baixo + + + + Autofire Left + Disparo automático Esquerda + + + + QObject + + + GBA + GBA + + + + GB + GB + + + + ? + ? + + + + QShortcut + + + Shift + Shift + + + + Control + Control + + + + Alt + Alt + + + + Meta + Meta + + + + ROMInfo + + + ROM Info + Informações da ROM + + + + Game name: + Nome do jogo: + + + + {NAME} + {NAME} + + + + Internal name: + Nome interno: + + + + {TITLE} + {TITLE} + + + + Game ID: + ID do jogo: + + + + {ID} + {ID} + + + + File size: + Tamanho do arquivo: + + + + {SIZE} + {SIZE} + + + + CRC32: + CRC32: + + + + {CRC} + {CRC} + + + + SensorView + + + Sensors + Sensores + + + + Realtime clock + Relógio em tempo real + + + + Fixed time + Tempo fixo + + + + System time + Horário do sistema + + + + Start time at + Horário iniciando em + + + + Now + Agora + + + + MM/dd/yy hh:mm:ss AP + dd/MM/yy hh:mm:ss + + + + Light sensor + Sensor de luz + + + + Brightness + Brilho + + + + Tilt sensor + Sensor de inclinação + + + + + Set Y + Definir Y + + + + + Set X + Definir X + + + + Gyroscope + Giroscópio + + + + Sensitivity + Sensibilidade + + + + SettingsView + + + Settings + Configurações + + + + Audio/Video + Áudio/Vídeo + + + + Interface + Interface + + + + Emulation + Emulação + + + + Enhancements + Melhorias + + + + BIOS + BIOS + + + + Paths + Caminhos + + + + Logging + Registros + + + + Game Boy + Game Boy + + + + Audio driver: + Driver de áudio: + + + + Audio buffer: + Buffer de áudio: + + + + + 1536 + 1536 + + + + 512 + 512 + + + + 768 + 768 + + + + 1024 + 1024 + + + + 2048 + 2048 + + + + 3072 + 3072 + + + + 4096 + 4096 + + + + samples + amostras + + + + Sample rate: + Taxa de amostragem: + + + + + 44100 + 44100 + + + + 22050 + 22050 + + + + 32000 + 32000 + + + + 48000 + 48000 + + + + Hz + Hz + + + + Volume: + Volume: + + + + + Mute + Mudo + + + + Fast forward volume: + Volume durante avanço rápido: + + + + Display driver: + Driver de exibição: + + + + Frameskip: + Salto de quadro: + + + + Skip every + Ignorar a cada + + + + + frames + quadros + + + + FPS target: + Meta de FPS: + + + + frames per second + quadros por segundo + + + + Sync: + Sincronizar: + + + + Video + Vídeo + + + + Audio + Áudio + + + + Lock aspect ratio + Fixar proporção + + + + Bilinear filtering + Filtragem bilinear + + + + Native (59.7275) + Nativo (59,7275) + + + + Interframe blending + Interframe blending + + + + Pause when minimized + Pausar quando minimizado + + + + Show OSD messages + Exibir mensagens OSD + + + + Show filename instead of ROM name in title bar + Mostrar nome do arquivo em vez do nome da ROM na barra de título + + + + Fast forward (held) speed: + Velocidade de avanço (segurado): + + + + (240×160) + (240×160) + + + + Log to file + Registrar para arquivo + + + + Log to console + Registrar no console + + + + Select Log File + Selecionar Arquivo de Registro + + + + Game Boy model: + Modelo do Game Boy: + + + + Super Game Boy model: + Modelo do Super Game Boy: + + + + Game Boy Color model: + Modelo do Game Boy Color: + + + + Use GBC colors in GB games + Usar cores de GBC em jogos de GB + + + + Camera: + Câmera: + + + + Force integer scaling + Forçar dimensionamento inteiro + + + + Language + Idioma + + + + English + Português do Brasil + + + + Library: + Biblioteca: + + + + List view + Visualização em lista + + + + Tree view + Visualização em árvore + + + + Show when no game open + Exibir quando nenhum jogo estiver aberto + + + + Clear cache + Limpar cache + + + + Allow opposing input directions + Permitir direções de entrada opostas + + + + Suspend screensaver + Suspender protetor de tela + + + + Pause when inactive + Pausar quando inativo + + + + Show FPS in title bar + Mostrar FPS na barra de título + + + + Automatically save cheats + Salvar cheats automaticamente + + + + Automatically load cheats + Carregar cheats automaticamente + + + + Automatically save state + Salvar estado automaticamente + + + + Automatically load state + Carregar estado automaticamente + + + + Enable Discord Rich Presence + Habilitar Discord Rich Presence + + + + Fast forward speed: + Velocidade de avanço: + + + + + + × + × + + + + + Unbounded + Ilimitado + + + + Enable rewind + Ativar retrocesso + + + + Rewind history: + Histórico de retrocesso: + + + + Idle loops: + Loops ociosos: + + + + Run all + Executar todos + + + + Remove known + Remover conhecidos + + + + Detect and remove + Detectar e remover + + + + Savestate extra data: + Savestate extra data: + + + + + Screenshot + Captura de tela + + + + + Save data + Salvar dados + + + + + Cheat codes + Códigos de cheat + + + + Load extra data: + Carregar dados extras: + + + + Preload entire ROM into memory + Pré-carregar toda a ROM na memória + + + + Autofire interval: + Intervalo do Disparo Automático: + + + + Video renderer: + Renderizador de vídeo: + + + + Software + Software + + + + OpenGL + OpenGL + + + + OpenGL enhancements + Melhorias do OpenGL + + + + High-resolution scale: + Escala de alta resolução: + + + + XQ GBA audio (experimental) + XQ GBA audio (experimental) + + + + GB BIOS file: + Arquivo GB BIOS: + + + + + + + + + + + + Browse + Navegar + + + + Use BIOS file if found + Usar o arquivo BIOS se encontrado + + + + Skip BIOS intro + Pular introdução da BIOS + + + + GBA BIOS file: + Arquivo GBA BIOS: + + + + GBC BIOS file: + Arquivo GBC BIOS: + + + + SGB BIOS file: + Arquivo SGB BIOS: + + + + Save games + Arquivos de salvamento + + + + + + + + Same directory as the ROM + Mesmo diretório que a ROM + + + + Save states + Estados salvos + + + + Screenshots + Capturas de tela + + + + Patches + Patches + + + + Cheats + Cheats + + + + + + Autodetect + Autodetectar + + + + + + Game Boy (DMG) + Game Boy (DMG) + + + + + + Super Game Boy (SGB) + Super Game Boy (SGB) + + + + + + Game Boy Color (CGB) + Game Boy Color (CGB) + + + + + + Game Boy Advance (AGB) + Game Boy Advance (AGB) + + + + Default BG colors: + Cores padrão do BG: + + + + Super Game Boy borders + Bordas do Super Game Boy + + + + Camera driver: + Driver da Câmera: + + + + Default sprite colors 1: + Cores padrão do sprite 1: + + + + Default sprite colors 2: + Cores padrão do sprite 2: + + + + ShaderSelector + + + Shaders + Shaders + + + + Active Shader: + Shader Ativo: + + + + Name + Nome + + + + Author + Autor + + + + Description + Descrição + + + + Unload Shader + Remover Shader + + + + Load New Shader + Carregar Novo Shader + + + + ShortcutView + + + Edit Shortcuts + Editar Atalhos + + + + Keyboard + Teclado + + + + Gamepad + Controle + + + + Clear + Limpar + + + + TileView + + + Tiles + Blocos + + + + Export Selected + Exportar Selecionado + + + + Export All + Exportar Tudo + + + + 256 colors + 256 cores + + + + × + × + + + + Magnification + Ampliação + + + + Tiles per row + Blocos por linha + + + + Fit to window + Ajustar à janela + + + + Copy Selected + Copiar selecionado + + + + Copy All + Copiar Tudo + + + + VideoView + + + Record Video + Gravar Vídeo + + + + Start + Iniciar + + + + Stop + Parar + + + + Select File + Selecionar arquivo + + + + Presets + Predefinições + + + + + WebM + WebM + + + + Format + Formato + + + + MKV + MKV + + + + AVI + AVI + + + + + MP4 + MP4 + + + + High &Quality + Qualidade &Alta + + + + &YouTube + &YouTube + + + + &Lossless + &Sem perdas + + + + 4K + 4K + + + + &1080p + &1080p + + + + &720p + &720p + + + + &480p + &480p + + + + &Native + &Nativo + + + + h.264 + h.264 + + + + h.264 (NVENC) + h.264 (NVENC) + + + + HEVC + HEVC + + + + HEVC (NVENC) + HEVC (NVENC) + + + + VP8 + VP8 + + + + VP9 + VP9 + + + + FFV1 + FFV1 + + + + + None + Nenhum + + + + FLAC + FLAC + + + + Opus + Opus + + + + Vorbis + Vorbis + + + + MP3 + MP3 + + + + AAC + AAC + + + + Uncompressed + Sem compressão + + + + Bitrate (kbps) + Bitrate (kbps) + + + + VBR + VBR + + + + ABR + ABR + + + + Dimensions + Dimensões + + + + : + : + + + + × + × + + + + Lock aspect ratio + Fixar proporção + + + + Show advanced + Mostrar opções avançadas + + + diff --git a/src/sm83/debugger/memory-debugger.c b/src/sm83/debugger/memory-debugger.c index c41350c67..2cd81669e 100644 --- a/src/sm83/debugger/memory-debugger.c +++ b/src/sm83/debugger/memory-debugger.c @@ -55,7 +55,11 @@ static bool _checkWatchpoints(struct SM83Debugger* debugger, uint16_t address, s return false; } } - info->type.wp.oldValue = debugger->originalMemory.load8(debugger->cpu, address); + uint8_t oldValue = debugger->originalMemory.load8(debugger->cpu, address); + if ((watchpoint->type & WATCHPOINT_CHANGE) && newValue == oldValue) { + continue; + } + info->type.wp.oldValue = oldValue; info->type.wp.newValue = newValue; info->address = address; info->type.wp.watchType = watchpoint->type;