mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' (early part) into medusa
This commit is contained in:
commit
4db6d34e12
8
CHANGES
8
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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/core/interface.h>
|
||||
|
||||
enum GBModel {
|
||||
GB_MODEL_AUTODETECT = 0xFF,
|
||||
GB_MODEL_DMG = 0x00,
|
||||
|
|
|
@ -14,16 +14,22 @@ CXX_GUARD_START
|
|||
#include <mgba/internal/gb/gb.h>
|
||||
#include <mgba/internal/gb/video.h>
|
||||
|
||||
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*);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -11,6 +11,15 @@
|
|||
#include <mgba-util/math.h>
|
||||
#include <mgba-util/memory.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<GB*>(m_controller->thread()->core->board)->memory.io;
|
||||
QPointF origin;
|
||||
GB* gb = static_cast<GB*>(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<GB*>(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<GB*>(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);
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -873,21 +873,21 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0" colspan="2">
|
||||
<item row="10" column="0" colspan="2">
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<item row="11" column="0">
|
||||
<widget class="QLabel" name="label_24">
|
||||
<property name="text">
|
||||
<string>Savestate extra data:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<item row="11" column="1">
|
||||
<widget class="QCheckBox" name="saveStateScreenshot">
|
||||
<property name="text">
|
||||
<string>Screenshot</string>
|
||||
|
@ -897,7 +897,7 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="1">
|
||||
<item row="12" column="1">
|
||||
<widget class="QCheckBox" name="saveStateSave">
|
||||
<property name="text">
|
||||
<string>Save data</string>
|
||||
|
@ -907,7 +907,7 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="1">
|
||||
<item row="13" column="1">
|
||||
<widget class="QCheckBox" name="saveStateCheats">
|
||||
<property name="text">
|
||||
<string>Cheat codes</string>
|
||||
|
@ -917,21 +917,21 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="0" colspan="2">
|
||||
<item row="14" column="0" colspan="2">
|
||||
<widget class="Line" name="line_9">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="0">
|
||||
<item row="15" column="0">
|
||||
<widget class="QLabel" name="label_25">
|
||||
<property name="text">
|
||||
<string>Load extra data:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="1">
|
||||
<item row="15" column="1">
|
||||
<widget class="QCheckBox" name="loadStateScreenshot">
|
||||
<property name="text">
|
||||
<string>Screenshot</string>
|
||||
|
@ -941,20 +941,27 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="15" column="1">
|
||||
<item row="16" column="1">
|
||||
<widget class="QCheckBox" name="loadStateSave">
|
||||
<property name="text">
|
||||
<string>Save data</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="16" column="1">
|
||||
<item row="17" column="1">
|
||||
<widget class="QCheckBox" name="loadStateCheats">
|
||||
<property name="text">
|
||||
<string>Cheat codes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QCheckBox" name="forceGbp">
|
||||
<property name="text">
|
||||
<string>Enable Game Boy Player features by default</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="enhancements">
|
||||
|
|
|
@ -1597,7 +1597,6 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
addGameAction(tr("View &tiles..."), "tileWindow", openControllerTView<TileView>(), "tools");
|
||||
addGameAction(tr("View &map..."), "mapWindow", openControllerTView<MapView>(), "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<MemoryView>(), "tools");
|
||||
addGameAction(tr("Search memory..."), "memorySearch", openControllerTView<MemorySearch>(), "tools");
|
||||
|
|
|
@ -306,8 +306,8 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</tran
|
|||
<name>GIFView</name>
|
||||
<message>
|
||||
<location filename="../GIFView.ui" line="14"/>
|
||||
<source>Record GIF/APNG</source>
|
||||
<translation>GIF/APNG aufzeichnen</translation>
|
||||
<source>Record GIF/WebP/APNG</source>
|
||||
<translation>GIF/WebP/APNG aufzeichnen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../GIFView.ui" line="30"/>
|
||||
|
@ -1392,22 +1392,22 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</tran
|
|||
<context>
|
||||
<name>QGBA::CoreController</name>
|
||||
<message>
|
||||
<location filename="../CoreController.cpp" line="601"/>
|
||||
<location filename="../CoreController.cpp" line="605"/>
|
||||
<source>Failed to open save file: %1</source>
|
||||
<translation>Fehler beim Öffnen der Speicherdatei: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../CoreController.cpp" line="630"/>
|
||||
<location filename="../CoreController.cpp" line="634"/>
|
||||
<source>Failed to open game file: %1</source>
|
||||
<translation>Fehler beim Öffnen der Spieldatei: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../CoreController.cpp" line="728"/>
|
||||
<location filename="../CoreController.cpp" line="732"/>
|
||||
<source>Failed to open snapshot file for reading: %1</source>
|
||||
<translation>Konnte Snapshot-Datei %1 nicht zum Lesen öffnen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../CoreController.cpp" line="744"/>
|
||||
<location filename="../CoreController.cpp" line="748"/>
|
||||
<source>Failed to open snapshot file for writing: %1</source>
|
||||
<translation>Konnte Snapshot-Datei %1 nicht zum Schreiben öffnen</translation>
|
||||
</message>
|
||||
|
@ -1433,52 +1433,52 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</tran
|
|||
<context>
|
||||
<name>QGBA::FrameView</name>
|
||||
<message>
|
||||
<location filename="../FrameView.cpp" line="530"/>
|
||||
<location filename="../FrameView.cpp" line="548"/>
|
||||
<source>Export frame</source>
|
||||
<translation>Bild exportieren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../FrameView.cpp" line="531"/>
|
||||
<location filename="../FrameView.cpp" line="549"/>
|
||||
<source>Portable Network Graphics (*.png)</source>
|
||||
<translation>Portable Network Graphics (*.png)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../FrameView.cpp" line="549"/>
|
||||
<location filename="../FrameView.cpp" line="567"/>
|
||||
<source>None</source>
|
||||
<translation>Keine</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../FrameView.cpp" line="551"/>
|
||||
<location filename="../FrameView.cpp" line="569"/>
|
||||
<source>Background</source>
|
||||
<translation>Hintergrund</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../FrameView.cpp" line="554"/>
|
||||
<location filename="../FrameView.cpp" line="572"/>
|
||||
<source>Window</source>
|
||||
<translation>Fenster</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../FrameView.cpp" line="557"/>
|
||||
<location filename="../FrameView.cpp" line="575"/>
|
||||
<source>Objwin</source>
|
||||
<translation>Objwin</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../FrameView.cpp" line="562"/>
|
||||
<location filename="../FrameView.cpp" line="580"/>
|
||||
<source>Sprite</source>
|
||||
<translation>Sprite</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../FrameView.cpp" line="565"/>
|
||||
<location filename="../FrameView.cpp" line="583"/>
|
||||
<source>Backdrop</source>
|
||||
<translation>Hintergrund</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../FrameView.cpp" line="568"/>
|
||||
<location filename="../FrameView.cpp" line="586"/>
|
||||
<source>Frame</source>
|
||||
<translation>Frame</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../FrameView.cpp" line="574"/>
|
||||
<location filename="../FrameView.cpp" line="592"/>
|
||||
<source>%1 %2</source>
|
||||
<translation>%1 %2</translation>
|
||||
</message>
|
||||
|
@ -1494,22 +1494,22 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</tran
|
|||
<context>
|
||||
<name>QGBA::GBAKeyEditor</name>
|
||||
<message>
|
||||
<location filename="../GBAKeyEditor.cpp" line="68"/>
|
||||
<location filename="../GBAKeyEditor.cpp" line="67"/>
|
||||
<source>Clear Button</source>
|
||||
<translation>Button löschen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../GBAKeyEditor.cpp" line="80"/>
|
||||
<location filename="../GBAKeyEditor.cpp" line="79"/>
|
||||
<source>Clear Analog</source>
|
||||
<translation>Analog löschen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../GBAKeyEditor.cpp" line="91"/>
|
||||
<location filename="../GBAKeyEditor.cpp" line="90"/>
|
||||
<source>Refresh</source>
|
||||
<translation>Aktualisieren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../GBAKeyEditor.cpp" line="101"/>
|
||||
<location filename="../GBAKeyEditor.cpp" line="100"/>
|
||||
<source>Set all</source>
|
||||
<translation>Alle belegen</translation>
|
||||
</message>
|
||||
|
@ -3267,12 +3267,12 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</tran
|
|||
<translation>Fehler beim Laden der Eingabedatei: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MemoryModel.cpp" line="343"/>
|
||||
<location filename="../MemoryModel.cpp" line="342"/>
|
||||
<source>TBL</source>
|
||||
<translation>TBL</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MemoryModel.cpp" line="343"/>
|
||||
<location filename="../MemoryModel.cpp" line="342"/>
|
||||
<source>ISO-8859-1</source>
|
||||
<translation>ISO-8859-1</translation>
|
||||
</message>
|
||||
|
@ -3280,22 +3280,22 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</tran
|
|||
<context>
|
||||
<name>QGBA::MemorySearch</name>
|
||||
<message>
|
||||
<location filename="../MemorySearch.cpp" line="222"/>
|
||||
<location filename="../MemorySearch.cpp" line="221"/>
|
||||
<source> (%0/%1×)</source>
|
||||
<translation> (%0/%1×)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MemorySearch.cpp" line="224"/>
|
||||
<location filename="../MemorySearch.cpp" line="223"/>
|
||||
<source> (⅟%0×)</source>
|
||||
<translation> (⅟%0×)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MemorySearch.cpp" line="227"/>
|
||||
<location filename="../MemorySearch.cpp" line="226"/>
|
||||
<source> (%0×)</source>
|
||||
<translation> (%0×)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MemorySearch.cpp" line="231"/>
|
||||
<location filename="../MemorySearch.cpp" line="230"/>
|
||||
<source>%1 byte%2</source>
|
||||
<translation>%1 byte%2</translation>
|
||||
</message>
|
||||
|
@ -3701,12 +3701,12 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</tran
|
|||
<translation>Video-Logs (*.mvl)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Window.cpp" line="851"/>
|
||||
<location filename="../Window.cpp" line="850"/>
|
||||
<source>Crash</source>
|
||||
<translation>Absturz</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Window.cpp" line="852"/>
|
||||
<location filename="../Window.cpp" line="851"/>
|
||||
<source>The game has crashed with the following error:
|
||||
|
||||
%1</source>
|
||||
|
@ -3715,52 +3715,52 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</tran
|
|||
%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Window.cpp" line="874"/>
|
||||
<location filename="../Window.cpp" line="873"/>
|
||||
<source>Unimplemented BIOS call</source>
|
||||
<translation>Nicht implementierter BIOS-Aufruf</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Window.cpp" line="875"/>
|
||||
<location filename="../Window.cpp" line="874"/>
|
||||
<source>This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience.</source>
|
||||
<translation>Dieses Spiel verwendet einen BIOS-Aufruf, der nicht implementiert ist. Bitte verwenden Sie für die beste Spielerfahrung das offizielle BIOS.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Window.cpp" line="974"/>
|
||||
<location filename="../Window.cpp" line="973"/>
|
||||
<source>Really make portable?</source>
|
||||
<translation>Portablen Modus wirklich aktivieren?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Window.cpp" line="975"/>
|
||||
<location filename="../Window.cpp" line="974"/>
|
||||
<source>This will make the emulator load its configuration from the same directory as the executable. Do you want to continue?</source>
|
||||
<translation>Diese Einstellung wird den Emulator so konfigurieren, dass er seine Konfiguration aus dem gleichen Verzeichnis wie die Programmdatei lädt. Möchten Sie fortfahren?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Window.cpp" line="987"/>
|
||||
<location filename="../Window.cpp" line="986"/>
|
||||
<source>Restart needed</source>
|
||||
<translation>Neustart benötigt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Window.cpp" line="988"/>
|
||||
<location filename="../Window.cpp" line="987"/>
|
||||
<source>Some changes will not take effect until the emulator is restarted.</source>
|
||||
<translation>Einige Änderungen werden erst übernommen, wenn der Emulator neu gestartet wurde.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Window.cpp" line="1043"/>
|
||||
<location filename="../Window.cpp" line="1042"/>
|
||||
<source> - Player %1 of %2</source>
|
||||
<translation> - Spieler %1 von %2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Window.cpp" line="1054"/>
|
||||
<location filename="../Window.cpp" line="1053"/>
|
||||
<source>%1 - %2</source>
|
||||
<translation>%1 - %2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Window.cpp" line="1056"/>
|
||||
<location filename="../Window.cpp" line="1055"/>
|
||||
<source>%1 - %2 - %3</source>
|
||||
<translation>%1 - %2 - %3</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Window.cpp" line="1058"/>
|
||||
<location filename="../Window.cpp" line="1057"/>
|
||||
<source>%1 - %2 (%3 fps) - %4</source>
|
||||
<translation>%1 - %2 (%3 Bilder/Sekunde) - %4</translation>
|
||||
</message>
|
||||
|
@ -4151,12 +4151,12 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</tran
|
|||
<translation>e-Reader-Karte (*.raw *.bin *.bmp)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Window.cpp" line="859"/>
|
||||
<location filename="../Window.cpp" line="858"/>
|
||||
<source>Couldn't Start</source>
|
||||
<translation>Konnte nicht gestartet werden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Window.cpp" line="860"/>
|
||||
<location filename="../Window.cpp" line="859"/>
|
||||
<source>Could not start game.</source>
|
||||
<translation>Spiel konnte nicht gestartet werden.</translation>
|
||||
</message>
|
||||
|
@ -4748,7 +4748,13 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</tran
|
|||
<translation>Bildwiederholrate in der Titelleiste anzeigen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1851"/>
|
||||
<location filename="../SettingsView.ui" line="961"/>
|
||||
<source>Enable Game Boy Player features by default</source>
|
||||
<translation>Game Boy Player-Features
|
||||
standardmäßig aktivieren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1858"/>
|
||||
<source>Super Game Boy/Game Boy Color model:</source>
|
||||
<translation>Super Game Boy/Game Boy Color-Modell:</translation>
|
||||
</message>
|
||||
|
@ -4799,107 +4805,107 @@ Titelleiste anzeigen</translation>
|
|||
<translation>Vorlauf-Geschwindigkeit (halten):</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="965"/>
|
||||
<location filename="../SettingsView.ui" line="972"/>
|
||||
<source>Video renderer:</source>
|
||||
<translation>Video-Renderer:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="973"/>
|
||||
<location filename="../SettingsView.ui" line="980"/>
|
||||
<source>Software</source>
|
||||
<translation>Software</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="978"/>
|
||||
<location filename="../SettingsView.ui" line="985"/>
|
||||
<source>OpenGL</source>
|
||||
<translation>OpenGL</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="986"/>
|
||||
<location filename="../SettingsView.ui" line="993"/>
|
||||
<source>OpenGL enhancements</source>
|
||||
<translation>OpenGL-Verbesserungen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="992"/>
|
||||
<location filename="../SettingsView.ui" line="999"/>
|
||||
<source>High-resolution scale:</source>
|
||||
<translation>Hochauflösende Skalierung:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1055"/>
|
||||
<location filename="../SettingsView.ui" line="1062"/>
|
||||
<source>XQ GBA audio (experimental)</source>
|
||||
<translation>XQ GBA-Audio (experimentell)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1394"/>
|
||||
<location filename="../SettingsView.ui" line="1401"/>
|
||||
<source>Cheats</source>
|
||||
<translation>Cheats</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1451"/>
|
||||
<location filename="../SettingsView.ui" line="1458"/>
|
||||
<source>Log to file</source>
|
||||
<translation>In Datei protokollieren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1458"/>
|
||||
<location filename="../SettingsView.ui" line="1465"/>
|
||||
<source>Log to console</source>
|
||||
<translation>Auf die Konsole protokollieren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1472"/>
|
||||
<location filename="../SettingsView.ui" line="1479"/>
|
||||
<source>Select Log File</source>
|
||||
<translation>Protokoll-Datei auswählen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1485"/>
|
||||
<location filename="../SettingsView.ui" line="1492"/>
|
||||
<source>Game Boy-only model:</source>
|
||||
<translation>Game Boy-Modell:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1505"/>
|
||||
<location filename="../SettingsView.ui" line="1512"/>
|
||||
<source>Game Boy Color-only model:</source>
|
||||
<translation>Game Boy Color-Modell:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1515"/>
|
||||
<location filename="../SettingsView.ui" line="1522"/>
|
||||
<source>Game Boy/Game Boy Color model:</source>
|
||||
<translation>Game Boy/Game Boy Color-Modell:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1828"/>
|
||||
<location filename="../SettingsView.ui" line="1835"/>
|
||||
<source>Camera:</source>
|
||||
<translation>Kamera:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1529"/>
|
||||
<location filename="../SettingsView.ui" line="1536"/>
|
||||
<source>Default BG colors:</source>
|
||||
<translation>Standard-Hintergrundfarben:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1616"/>
|
||||
<location filename="../SettingsView.ui" line="1623"/>
|
||||
<source>Default sprite colors 1:</source>
|
||||
<translation>Standard-Sprite-Farben 1:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1703"/>
|
||||
<location filename="../SettingsView.ui" line="1710"/>
|
||||
<source>Default sprite colors 2:</source>
|
||||
<translation>Standard-Sprite-Farben 2:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1790"/>
|
||||
<location filename="../SettingsView.ui" line="1797"/>
|
||||
<source>Use GBC colors in GB games</source>
|
||||
<translation>Verwende GBC-Farben in GB-Spielen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1797"/>
|
||||
<location filename="../SettingsView.ui" line="1804"/>
|
||||
<source>Super Game Boy borders</source>
|
||||
<translation>Super Game Boy-Rahmen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1495"/>
|
||||
<location filename="../SettingsView.ui" line="1502"/>
|
||||
<source>Super Game Boy model:</source>
|
||||
<translation>Super Game Boy-Modell:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1811"/>
|
||||
<location filename="../SettingsView.ui" line="1818"/>
|
||||
<source>Camera driver:</source>
|
||||
<translation>Kamera-Treiber:</translation>
|
||||
</message>
|
||||
|
@ -4930,15 +4936,15 @@ Titelleiste anzeigen</translation>
|
|||
in Arbeitsspeicher vorladen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1085"/>
|
||||
<location filename="../SettingsView.ui" line="1123"/>
|
||||
<location filename="../SettingsView.ui" line="1158"/>
|
||||
<location filename="../SettingsView.ui" line="1186"/>
|
||||
<location filename="../SettingsView.ui" line="1227"/>
|
||||
<location filename="../SettingsView.ui" line="1275"/>
|
||||
<location filename="../SettingsView.ui" line="1323"/>
|
||||
<location filename="../SettingsView.ui" line="1371"/>
|
||||
<location filename="../SettingsView.ui" line="1419"/>
|
||||
<location filename="../SettingsView.ui" line="1092"/>
|
||||
<location filename="../SettingsView.ui" line="1130"/>
|
||||
<location filename="../SettingsView.ui" line="1165"/>
|
||||
<location filename="../SettingsView.ui" line="1193"/>
|
||||
<location filename="../SettingsView.ui" line="1234"/>
|
||||
<location filename="../SettingsView.ui" line="1282"/>
|
||||
<location filename="../SettingsView.ui" line="1330"/>
|
||||
<location filename="../SettingsView.ui" line="1378"/>
|
||||
<location filename="../SettingsView.ui" line="1426"/>
|
||||
<source>Browse</source>
|
||||
<translation>Durchsuchen</translation>
|
||||
</message>
|
||||
|
@ -4953,20 +4959,20 @@ in Arbeitsspeicher vorladen</translation>
|
|||
<translation>DS-BIOS 9:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1077"/>
|
||||
<location filename="../SettingsView.ui" line="1101"/>
|
||||
<source>Use BIOS file if found</source>
|
||||
<translation>BIOS-Datei verwenden,
|
||||
wenn vorhanden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1104"/>
|
||||
<location filename="../SettingsView.ui" line="1111"/>
|
||||
<source>Skip BIOS intro</source>
|
||||
<translation>BIOS-Intro überspringen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="709"/>
|
||||
<location filename="../SettingsView.ui" line="752"/>
|
||||
<location filename="../SettingsView.ui" line="1001"/>
|
||||
<location filename="../SettingsView.ui" line="1008"/>
|
||||
<source>×</source>
|
||||
<translation>×</translation>
|
||||
</message>
|
||||
|
@ -5065,22 +5071,22 @@ wenn vorhanden</translation>
|
|||
<translation>Autofeuer-Intervall:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1030"/>
|
||||
<location filename="../SettingsView.ui" line="1037"/>
|
||||
<source>(240×160)</source>
|
||||
<translation>(240×160)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1066"/>
|
||||
<location filename="../SettingsView.ui" line="1073"/>
|
||||
<source>GB BIOS file:</source>
|
||||
<translation>Datei mit GB-BIOS:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1132"/>
|
||||
<location filename="../SettingsView.ui" line="1139"/>
|
||||
<source>GBA BIOS file:</source>
|
||||
<translation>Datei mit GBA-BIOS:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1139"/>
|
||||
<location filename="../SettingsView.ui" line="1146"/>
|
||||
<source>GBC BIOS file:</source>
|
||||
<translation>Datei mit GBC-BIOS:</translation>
|
||||
</message>
|
||||
|
@ -5090,36 +5096,36 @@ wenn vorhanden</translation>
|
|||
<translation>DS-Firmware:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1150"/>
|
||||
<location filename="../SettingsView.ui" line="1174"/>
|
||||
<source>SGB BIOS file:</source>
|
||||
<translation>Datei mit SGB-BIOS:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1202"/>
|
||||
<location filename="../SettingsView.ui" line="1209"/>
|
||||
<source>Save games</source>
|
||||
<translation>Spielstände</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1236"/>
|
||||
<location filename="../SettingsView.ui" line="1284"/>
|
||||
<location filename="../SettingsView.ui" line="1332"/>
|
||||
<location filename="../SettingsView.ui" line="1380"/>
|
||||
<location filename="../SettingsView.ui" line="1428"/>
|
||||
<location filename="../SettingsView.ui" line="1243"/>
|
||||
<location filename="../SettingsView.ui" line="1291"/>
|
||||
<location filename="../SettingsView.ui" line="1339"/>
|
||||
<location filename="../SettingsView.ui" line="1387"/>
|
||||
<location filename="../SettingsView.ui" line="1435"/>
|
||||
<source>Same directory as the ROM</source>
|
||||
<translation>Verzeichnis der ROM-Datei</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1250"/>
|
||||
<location filename="../SettingsView.ui" line="1257"/>
|
||||
<source>Save states</source>
|
||||
<translation>Savestates</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1298"/>
|
||||
<location filename="../SettingsView.ui" line="1305"/>
|
||||
<source>Screenshots</source>
|
||||
<translation>Screenshots</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1346"/>
|
||||
<location filename="../SettingsView.ui" line="1353"/>
|
||||
<source>Patches</source>
|
||||
<translation>Patches</translation>
|
||||
</message>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue