From 2969a8bf7a25e00616436b43c7a5e11255f20070 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 27 Jun 2022 21:10:16 -0700 Subject: [PATCH 1/4] Core: Fix cache writes that span multiple tiles --- include/mgba/core/map-cache.h | 1 + src/core/map-cache.c | 17 +++++++++++++---- src/gb/renderers/cache-set.c | 1 + src/gba/renderers/cache-set.c | 2 +- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/include/mgba/core/map-cache.h b/include/mgba/core/map-cache.h index 2bd5934e8..349fc1663 100644 --- a/include/mgba/core/map-cache.h +++ b/include/mgba/core/map-cache.h @@ -23,6 +23,7 @@ DECL_BITS(mMapCacheSystemInfo, TilesWide, 8, 4); DECL_BITS(mMapCacheSystemInfo, TilesHigh, 12, 4); DECL_BITS(mMapCacheSystemInfo, MacroTileSize, 16, 7); DECL_BITS(mMapCacheSystemInfo, MapAlign, 23, 2); +DECL_BITS(mMapCacheSystemInfo, WriteAlign, 25, 2); DECL_BITFIELD(mMapCacheEntryFlags, uint16_t); DECL_BITS(mMapCacheEntryFlags, PaletteId, 0, 4); diff --git a/src/core/map-cache.c b/src/core/map-cache.c index 17df206dd..d51800780 100644 --- a/src/core/map-cache.c +++ b/src/core/map-cache.c @@ -70,11 +70,20 @@ void mMapCacheDeinit(struct mMapCache* cache) { void mMapCacheWriteVRAM(struct mMapCache* cache, uint32_t address) { if (address >= cache->mapStart && address < cache->mapStart + cache->mapSize) { + uint32_t align = 1 << (mMapCacheSystemInfoGetWriteAlign(cache->sysConfig) - mMapCacheSystemInfoGetMapAlign(cache->sysConfig)); address -= cache->mapStart; - struct mMapCacheEntry* status = &cache->status[address >> mMapCacheSystemInfoGetMapAlign(cache->sysConfig)]; - ++status->vramVersion; - status->flags = mMapCacheEntryFlagsClearVramClean(status->flags); - status->tileStatus[mMapCacheEntryFlagsGetPaletteId(status->flags)].vramClean = 0; + address >>= mMapCacheSystemInfoGetMapAlign(cache->sysConfig); + + uint32_t i; + for (i = 0; i < align; ++i) { + if (address + i >= (cache->mapSize >> mMapCacheSystemInfoGetMapAlign(cache->sysConfig))) { + break; + } + struct mMapCacheEntry* status = &cache->status[address + i]; + ++status->vramVersion; + status->flags = mMapCacheEntryFlagsClearVramClean(status->flags); + status->tileStatus[mMapCacheEntryFlagsGetPaletteId(status->flags)].vramClean = 0; + } } } diff --git a/src/gb/renderers/cache-set.c b/src/gb/renderers/cache-set.c index ebd0d0899..bfc56c2a3 100644 --- a/src/gb/renderers/cache-set.c +++ b/src/gb/renderers/cache-set.c @@ -119,6 +119,7 @@ void GBVideoCacheWriteVideoRegister(struct mCacheSet* cache, uint16_t address, u sysconfig = mMapCacheSystemInfoSetMacroTileSize(sysconfig, 5); sysconfig = mMapCacheSystemInfoSetPaletteBPP(sysconfig, 1); sysconfig = mMapCacheSystemInfoSetMapAlign(sysconfig, 0); + sysconfig = mMapCacheSystemInfoSetWriteAlign(sysconfig, 0); sysconfig = mMapCacheSystemInfoSetTilesHigh(sysconfig, 5); sysconfig = mMapCacheSystemInfoSetTilesWide(sysconfig, 5); mMapCacheConfigureSystem(map, sysconfig); diff --git a/src/gba/renderers/cache-set.c b/src/gba/renderers/cache-set.c index 95093ae9a..51512721d 100644 --- a/src/gba/renderers/cache-set.c +++ b/src/gba/renderers/cache-set.c @@ -160,7 +160,7 @@ static void GBAVideoCacheWriteBGCNT(struct mCacheSet* cache, size_t bg, uint16_t int size = GBARegisterBGCNTGetSize(value); int tilesWide = 0; int tilesHigh = 0; - mMapCacheSystemInfo sysconfig = 0; + mMapCacheSystemInfo sysconfig = mMapCacheSystemInfoSetWriteAlign(0, 1); if (map->mapParser == mapParser0) { map->tileCache = mTileCacheSetGetPointer(&cache->tiles, p); sysconfig = mMapCacheSystemInfoSetPaletteBPP(sysconfig, 2 + p); From 1bf1a970238ff99d52860c75546fcc91f161c0da Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 28 Jun 2022 02:23:39 -0700 Subject: [PATCH 2/4] GBA Video: Fix sprite layer priority updating in GL --- CHANGES | 1 + src/gba/renderers/gl.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index aed3e0c11..d536343aa 100644 --- a/CHANGES +++ b/CHANGES @@ -47,6 +47,7 @@ Emulation fixes: - GBA Video: Fix sprites with mid-frame palette changes in GL (fixes mgba.io/i/2476) - GBA Video: Fix OBJ tile wrapping with 2D char mapping (fixes mgba.io/i/2443) - GBA Video: Fix horizontal lines in GL when charbase is changed (fixes mgba.io/i/1631) + - GBA Video: Fix sprite layer priority updating in GL Other fixes: - ARM: Disassemble Thumb mov pseudo-instruction properly - Core: Don't attempt to restore rewind diffs past start of rewind diff --git a/src/gba/renderers/gl.c b/src/gba/renderers/gl.c index acd5dad7f..75b7b7ced 100644 --- a/src/gba/renderers/gl.c +++ b/src/gba/renderers/gl.c @@ -1802,9 +1802,9 @@ 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_STENCIL_TEST); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); glStencilMask(0); @@ -1812,6 +1812,7 @@ void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GB glUniform3i(uniforms[GBA_GL_OBJ_OBJWIN], window, renderer->bldb, renderer->bldy); glDrawBuffers(3, (GLenum[]) { GL_NONE, GL_NONE, GL_COLOR_ATTACHMENT2 }); } else { + glEnable(GL_STENCIL_TEST); glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); glStencilMask(1); @@ -1836,7 +1837,6 @@ 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 }); From 0979380c74569964b9b082a70153a10dde61d927 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 28 Jun 2022 18:43:15 -0700 Subject: [PATCH 3/4] Debugger: Shaving recursive yaks takes a lot of work --- CHANGES | 1 + include/mgba-util/vector.h | 1 + include/mgba/internal/debugger/parser.h | 4 +- src/arm/debugger/debugger.c | 2 - src/debugger/cli-debugger.c | 3 - src/debugger/parser.c | 288 +++++++++++++++++------- src/debugger/test/parser.c | 7 +- src/sm83/debugger/debugger.c | 2 - 8 files changed, 216 insertions(+), 92 deletions(-) diff --git a/CHANGES b/CHANGES index d536343aa..382ce85ad 100644 --- a/CHANGES +++ b/CHANGES @@ -53,6 +53,7 @@ Other fixes: - Core: Don't attempt to restore rewind diffs past start of rewind - Core: Fix the runloop resuming after a game has crashed (fixes mgba.io/i/2451) - Core: Fix crash if library can't be opened + - Debugger: Fix crash with extremely long CLI strings - FFmpeg: Fix crash when encoding audio with some containers - FFmpeg: Fix GIF recording (fixes mgba.io/i/2393) - GB: Fix temporary saves diff --git a/include/mgba-util/vector.h b/include/mgba-util/vector.h index ba412d7da..a4ec71b65 100644 --- a/include/mgba-util/vector.h +++ b/include/mgba-util/vector.h @@ -98,6 +98,7 @@ CXX_GUARD_START } \ DECLARE_VECTOR(StringList, char*); +DECLARE_VECTOR(IntList, int); CXX_GUARD_END diff --git a/include/mgba/internal/debugger/parser.h b/include/mgba/internal/debugger/parser.h index 37f194ddb..0b8f09d4c 100644 --- a/include/mgba/internal/debugger/parser.h +++ b/include/mgba/internal/debugger/parser.h @@ -43,7 +43,7 @@ enum Operation { struct Token { enum TokenType { - TOKEN_ERROR_TYPE, + TOKEN_ERROR_TYPE = 0, TOKEN_UINT_TYPE, TOKEN_IDENTIFIER_TYPE, TOKEN_OPERATOR_TYPE, @@ -60,8 +60,10 @@ struct Token { struct ParseTree { struct Token token; + struct ParseTree* p; struct ParseTree* lhs; struct ParseTree* rhs; + int precedence; }; size_t lexExpression(struct LexVector* lv, const char* string, size_t length, const char* eol); diff --git a/src/arm/debugger/debugger.c b/src/arm/debugger/debugger.c index 99fd4ffb4..afc1e0c49 100644 --- a/src/arm/debugger/debugger.c +++ b/src/arm/debugger/debugger.c @@ -190,14 +190,12 @@ static struct ARMDebugBreakpoint* _lookupBreakpoint(struct ARMDebugBreakpointLis static void _destroyBreakpoint(struct ARMDebugBreakpoint* breakpoint) { if (breakpoint->d.condition) { parseFree(breakpoint->d.condition); - free(breakpoint->d.condition); } } static void _destroyWatchpoint(struct mWatchpoint* watchpoint) { if (watchpoint->condition) { parseFree(watchpoint->condition); - free(watchpoint->condition); } } diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c index a89e6db4d..dbc10fe89 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -226,11 +226,9 @@ static bool _parseExpression(struct mDebugger* debugger, struct CLIDebugVector* } if (!mDebuggerEvaluateParseTree(debugger, tree, intValue, segmentValue)) { parseFree(tree); - free(tree); return false; } parseFree(tree); - free(tree); return true; } @@ -599,7 +597,6 @@ static struct ParseTree* _parseTree(const char** string) { if (error) { if (tree) { parseFree(tree); - free(tree); } return NULL; } else { diff --git a/src/debugger/parser.c b/src/debugger/parser.c index 41197b53e..72eae1ed3 100644 --- a/src/debugger/parser.c +++ b/src/debugger/parser.c @@ -11,6 +11,8 @@ DEFINE_VECTOR(LexVector, struct Token); +DEFINE_VECTOR(IntList, int32_t); + enum LexState { LEX_ERROR = -1, LEX_ROOT = 0, @@ -493,16 +495,21 @@ static const int _operatorPrecedence[] = { [OP_DEREFERENCE] = 2, }; -static struct ParseTree* _parseTreeCreate() { +static struct ParseTree* _parseTreeCreate(void) { struct ParseTree* tree = malloc(sizeof(struct ParseTree)); tree->token.type = TOKEN_ERROR_TYPE; - tree->rhs = 0; - tree->lhs = 0; + tree->p = NULL; + tree->rhs = NULL; + tree->lhs = NULL; + tree->precedence = INT_MAX; return tree; } -static size_t _parseExpression(struct ParseTree* tree, struct LexVector* lv, size_t i, int precedence, int* openParens) { - struct ParseTree* newTree = 0; +static size_t _parseExpression(struct ParseTree* tree, struct LexVector* lv, int* openParens) { + struct ParseTree* newTree = NULL; + bool pop = false; + int precedence = INT_MAX; + size_t i = 0; while (i < LexVectorSize(lv)) { struct Token* token = LexVectorGetPointer(lv, i); int newPrecedence; @@ -517,27 +524,36 @@ static size_t _parseExpression(struct ParseTree* tree, struct LexVector* lv, siz ++i; } else { tree->token.type = TOKEN_ERROR_TYPE; - return i + 1; + ++i; + pop = true; } break; case TOKEN_SEGMENT_TYPE: tree->lhs = _parseTreeCreate(); tree->lhs->token.type = TOKEN_UINT_TYPE; tree->lhs->token.uintValue = token->uintValue; + tree->lhs->p = tree; + tree->lhs->precedence = precedence; tree->rhs = _parseTreeCreate(); + tree->rhs->p = tree; + tree->rhs->precedence = precedence; tree->token.type = TOKEN_SEGMENT_TYPE; - i = _parseExpression(tree->rhs, lv, i + 1, precedence, openParens); + tree = tree->rhs; + ++i; break; case TOKEN_OPEN_PAREN_TYPE: ++*openParens; - i = _parseExpression(tree, lv, i + 1, INT_MAX, openParens); + precedence = INT_MAX; + ++i; break; case TOKEN_CLOSE_PAREN_TYPE: if (*openParens <= 0) { tree->token.type = TOKEN_ERROR_TYPE; } --*openParens; - return i + 1; + ++i; + pop = true; + break; case TOKEN_OPERATOR_TYPE: if (tree->token.type == TOKEN_ERROR_TYPE) { switch (token->operatorValue) { @@ -557,21 +573,44 @@ static size_t _parseExpression(struct ParseTree* tree, struct LexVector* lv, siz newPrecedence = _operatorPrecedence[token->operatorValue]; if (newPrecedence < precedence) { newTree = _parseTreeCreate(); - *newTree = *tree; + memcpy(newTree, tree, sizeof(*tree)); + if (newTree->lhs) { + newTree->lhs->p = newTree; + } + if (newTree->rhs) { + newTree->rhs->p = newTree; + } + newTree->p = tree; tree->lhs = newTree; tree->rhs = _parseTreeCreate(); + tree->rhs->p = tree; + tree->rhs->precedence = newPrecedence; + precedence = newPrecedence; tree->token = *token; - i = _parseExpression(tree->rhs, lv, i + 1, newPrecedence, openParens); - if (tree->token.type == TOKEN_ERROR_TYPE) { - tree->token.type = TOKEN_ERROR_TYPE; - } + tree = tree->rhs; + ++i; } else { - return i; + pop = true; } break; case TOKEN_ERROR_TYPE: tree->token.type = TOKEN_ERROR_TYPE; - return i + 1; + ++i; + pop = true; + break; + } + + if (pop) { + if (tree->token.type == TOKEN_ERROR_TYPE && tree->p) { + tree->p->token.type = TOKEN_ERROR_TYPE; + } + tree = tree->p; + pop = false; + if (!tree) { + break; + } else { + precedence = tree->precedence; + } } } @@ -584,11 +623,13 @@ void parseLexedExpression(struct ParseTree* tree, struct LexVector* lv) { } tree->token.type = TOKEN_ERROR_TYPE; - tree->lhs = 0; - tree->rhs = 0; + tree->lhs = NULL; + tree->rhs = NULL; + tree->p = NULL; + tree->precedence = INT_MAX; int openParens = 0; - _parseExpression(tree, lv, 0, INT_MAX, &openParens); + _parseExpression(tree, lv, &openParens); if (openParens) { if (tree->token.type == TOKEN_IDENTIFIER_TYPE) { free(tree->token.identifierValue); @@ -607,23 +648,40 @@ void lexFree(struct LexVector* lv) { } } -void parseFree(struct ParseTree* tree) { - if (!tree) { - return; - } - - if (tree->lhs) { - parseFree(tree->lhs); - free(tree->lhs); - } - if (tree->rhs) { - parseFree(tree->rhs); - free(tree->rhs); - } - +static void _freeTree(struct ParseTree* tree) { if (tree->token.type == TOKEN_IDENTIFIER_TYPE) { free(tree->token.identifierValue); } + free(tree); +} + +void parseFree(struct ParseTree* tree) { + while (tree) { + if (tree->lhs) { + tree = tree->lhs; + continue; + } + if (tree->rhs) { + tree = tree->rhs; + continue; + } + if (tree->p) { + if (tree->p->lhs == tree) { + tree = tree->p; + _freeTree(tree->lhs); + tree->lhs = NULL; + } else if (tree->p->rhs == tree) { + tree = tree->p; + _freeTree(tree->rhs); + tree->rhs = NULL; + } else { + abort(); + } + } else { + _freeTree(tree); + break; + } + } } static bool _performOperation(struct mDebugger* debugger, enum Operation operation, int32_t current, int32_t next, int32_t* value, int* segment) { @@ -721,56 +779,124 @@ bool mDebuggerEvaluateParseTree(struct mDebugger* debugger, struct ParseTree* tr if (!value) { return false; } - int32_t lhs, rhs; - switch (tree->token.type) { - case TOKEN_UINT_TYPE: - if (segment) { - *segment = -1; - } - *value = tree->token.uintValue; - return true; - case TOKEN_SEGMENT_TYPE: - if (!mDebuggerEvaluateParseTree(debugger, tree->rhs, value, segment)) { - return false; - } - return mDebuggerEvaluateParseTree(debugger, tree->lhs, segment, NULL); - case TOKEN_OPERATOR_TYPE: - switch (tree->token.operatorValue) { - case OP_ASSIGN: - case OP_ADD: - case OP_SUBTRACT: - case OP_MULTIPLY: - case OP_DIVIDE: - case OP_MODULO: - case OP_AND: - case OP_OR: - case OP_XOR: - case OP_LESS: - case OP_GREATER: - case OP_EQUAL: - case OP_NOT_EQUAL: - case OP_LOGICAL_AND: - case OP_LOGICAL_OR: - case OP_LE: - case OP_GE: - case OP_SHIFT_L: - case OP_SHIFT_R: - if (!mDebuggerEvaluateParseTree(debugger, tree->lhs, &lhs, segment)) { - return false; - } - // Fall through - default: - if (!mDebuggerEvaluateParseTree(debugger, tree->rhs, &rhs, segment)) { - return false; + struct IntList stack; + int nextBranch; + bool ok = true; + int32_t tmpVal, tmpSegment; + + IntListInit(&stack, 0); + while (ok) { + switch (tree->token.type) { + case TOKEN_UINT_TYPE: + nextBranch = 2; + tmpSegment = -1; + tmpVal = tree->token.uintValue; + break; + case TOKEN_SEGMENT_TYPE: + nextBranch = 0; + break; + case TOKEN_OPERATOR_TYPE: + switch (tree->token.operatorValue) { + case OP_ASSIGN: + case OP_ADD: + case OP_SUBTRACT: + case OP_MULTIPLY: + case OP_DIVIDE: + case OP_MODULO: + case OP_AND: + case OP_OR: + case OP_XOR: + case OP_LESS: + case OP_GREATER: + case OP_EQUAL: + case OP_NOT_EQUAL: + case OP_LOGICAL_AND: + case OP_LOGICAL_OR: + case OP_LE: + case OP_GE: + case OP_SHIFT_L: + case OP_SHIFT_R: + nextBranch = 0; + break; + default: + nextBranch = 1; + break; } break; + case TOKEN_IDENTIFIER_TYPE: + if (!mDebuggerLookupIdentifier(debugger, tree->token.identifierValue, &tmpVal, &tmpSegment)) { + ok = false; + } + nextBranch = 2; + break; + case TOKEN_ERROR_TYPE: + default: + ok = false; + break; + } + if (!ok) { + break; + } + + bool gotTree = false; + while (!gotTree && tree) { + int32_t lhs, rhs; + + switch (nextBranch) { + case 0: + *IntListAppend(&stack) = tmpVal; + *IntListAppend(&stack) = tmpSegment; + *IntListAppend(&stack) = nextBranch; + tree = tree->lhs; + gotTree = true; + break; + case 1: + *IntListAppend(&stack) = tmpVal; + *IntListAppend(&stack) = tmpSegment; + *IntListAppend(&stack) = nextBranch; + tree = tree->rhs; + gotTree = true; + break; + case 2: + if (!IntListSize(&stack)) { + tree = NULL; + break; + } + nextBranch = *IntListGetPointer(&stack, IntListSize(&stack) - 1); + IntListResize(&stack, -1); + tree = tree->p; + if (nextBranch == 0) { + ++nextBranch; + } else if (tree) { + nextBranch = 2; + switch (tree->token.type) { + case TOKEN_OPERATOR_TYPE: + rhs = tmpVal; + lhs = *IntListGetPointer(&stack, IntListSize(&stack) - 2); + tmpSegment = *IntListGetPointer(&stack, IntListSize(&stack) - 1); + ok = _performOperation(debugger, tree->token.operatorValue, lhs, rhs, &tmpVal, &tmpSegment); + break; + case TOKEN_SEGMENT_TYPE: + tmpSegment = *IntListGetPointer(&stack, IntListSize(&stack) - 2); + break; + default: + break; + } + } + IntListResize(&stack, -2); + break; + } + } + if (!tree) { + break; } - return _performOperation(debugger, tree->token.operatorValue, lhs, rhs, value, segment); - case TOKEN_IDENTIFIER_TYPE: - return mDebuggerLookupIdentifier(debugger, tree->token.identifierValue, value, segment); - case TOKEN_ERROR_TYPE: - default: - break; } - return false; + IntListDeinit(&stack); + if (ok) { + *value = tmpVal; + if (segment) { + *segment = tmpSegment; + } + } + return ok; } diff --git a/src/debugger/test/parser.c b/src/debugger/test/parser.c index c1c2840d2..b25d8a68a 100644 --- a/src/debugger/test/parser.c +++ b/src/debugger/test/parser.c @@ -9,7 +9,7 @@ struct LPTest { struct LexVector lv; - struct ParseTree tree; + struct ParseTree* tree; }; #define PARSE(STR) \ @@ -18,7 +18,8 @@ struct LPTest { LexVectorClear(&lp->lv); \ size_t adjusted = lexExpression(&lp->lv, STR, strlen(STR), ""); \ assert_false(adjusted > strlen(STR)); \ - struct ParseTree* tree = &lp->tree; \ + lp->tree = malloc(sizeof(*lp->tree)); \ + struct ParseTree* tree = lp->tree; \ parseLexedExpression(tree, &lp->lv) static int parseSetup(void** state) { @@ -30,7 +31,7 @@ static int parseSetup(void** state) { static int parseTeardown(void** state) { struct LPTest* lp = *state; - parseFree(&lp->tree); + parseFree(lp->tree); lexFree(&lp->lv); LexVectorDeinit(&lp->lv); free(lp); diff --git a/src/sm83/debugger/debugger.c b/src/sm83/debugger/debugger.c index edf8afc1e..f6a6f690e 100644 --- a/src/sm83/debugger/debugger.c +++ b/src/sm83/debugger/debugger.c @@ -28,14 +28,12 @@ static struct mBreakpoint* _lookupBreakpoint(struct mBreakpointList* breakpoints static void _destroyBreakpoint(struct mBreakpoint* breakpoint) { if (breakpoint->condition) { parseFree(breakpoint->condition); - free(breakpoint->condition); } } static void _destroyWatchpoint(struct mWatchpoint* watchpoint) { if (watchpoint->condition) { parseFree(watchpoint->condition); - free(watchpoint->condition); } } From a851c1e094d748f2ebe16a97d20c7781e65815df Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 29 Jun 2022 01:39:06 -0700 Subject: [PATCH 4/4] Res: Add some stylized icons for future use --- res/gb-icon-128.png | Bin 0 -> 2110 bytes res/gb-icon-256.png | Bin 0 -> 3289 bytes res/gb-icon.svg | 2 ++ res/gba-icon-128.png | Bin 0 -> 3211 bytes res/gba-icon-256.png | Bin 0 -> 5589 bytes res/gba-icon.svg | 2 ++ res/gbc-icon-128.png | Bin 0 -> 1660 bytes res/gbc-icon-256.png | Bin 0 -> 3227 bytes res/gbc-icon.svg | 2 ++ 9 files changed, 6 insertions(+) create mode 100644 res/gb-icon-128.png create mode 100644 res/gb-icon-256.png create mode 100644 res/gb-icon.svg create mode 100644 res/gba-icon-128.png create mode 100644 res/gba-icon-256.png create mode 100644 res/gba-icon.svg create mode 100644 res/gbc-icon-128.png create mode 100644 res/gbc-icon-256.png create mode 100644 res/gbc-icon.svg diff --git a/res/gb-icon-128.png b/res/gb-icon-128.png new file mode 100644 index 0000000000000000000000000000000000000000..4aef63996cae69f4103c7b348d952fdd9fb2e168 GIT binary patch literal 2110 zcmds(*H_aC7RP@fvUHLdLZ2i^fDA+oFiJO}2?Pj85l3aDOJ8aR_oPf5?2%l06@yt z#?tv{)Blzz_{hI5-2VmuLcLD*u2$CJ_D7fh5%gTl)Wisb!I&H$hQr|$3We-!-NdZy zP$p8TR1p!8#Kc6}V~nt{Fpt}3N4A{c^I95eL`6kLhXH#dq^FcU|5tkIo&Fv+ zcd!SEQtIvQD3#Y&l!kH!dd`zbgMFPtgFQW+t&Me+o$ZbH@1~9L`rl6T1=Ay+R|LOa zbpQB%PEk?*LDoIz^G@&PCrM;Wdnc>MIoX#lxCI9Vyy<8|NJH0QIvN@pva+&)Pz*#& z;v@tS1X0yJE}sb3k`@um%YAgg&E*sp>kLJr^pvDonN@Sva|Pfw5P zsuxWSb(sjfCqxA!b==qMs)6>YsqtY51TxI+qeO+bH`NVxw|956uv(aQc6R(x4!eb^ zby8!nmrbL_8=pNL7aN0CL8_@?q@|^Ly4V;s6;m?FjQr0w=cBEG!Xd&iTDdoZ>;%AnekzTpZI-w z;qdVZu1*&!!pI~hJV0Aw z%Fx&{^!9xGUml;@cILOp)zhux5#*YM}}79z?}KEGpckpbdDh0E#`hamN|> z0t)vzwlI{n4xh2(0L|qL)FuL~`$Uuce9^QJp9x@vqf;dVx^mk0ecS-f)6=CoJ{ftr zFNM(F6pQ6wwlg1qbDQeGisrW9U8AVan?Y+v05f3y5Cnn(ASgXk)WBd-@+v#IPIXs^ zj3S_Ooq%J+{|;SjVLnCAbJ?a-uEt;YU5)wTJ~I4=erHU2fWM`C75(bDEmafHnjF>5 zul!+5(S~pFkDD)miw}c#whT>lqiZguj$06DwbxyThdbuF1QP%GT-XI_HPx9{2DKyQ zPbH&(T+cj$`lc-5lF41wg_WFJ5V@Y03;)Q0ZI=F4&4OofB<@k&9ivP=5cIc)`QPZo zmF3P7VO`G-KQ|!qFrHaLe}Ww1-YP)$(f9RTL4AU@@DyX{W^oeau90O$6wP{RcZ9aZ zm%;mRR{8u9pUl9IrKg&oUJo8YWj+LEnPSlmjye9t38{9Y58r0-A1C2!)cm18U58d) zl9e*ZRY(1PV@BsSk5Lr466Zvuj@R0-pMCj8|B_|!H08F#v-Hxt`37l|uhBt&Mz>M- z_}DPH;wqP(3?+Ta0EOyq@x-SOdYkL_Xz6D6HtWm|xTt%F)%Y_404;z58g$qd*I-*B z7A?{>rjeH)Iebh!TTZaq!MTHsc6R1!<5wQmC!@x1je)F(B%)!3j*mRz_maZa4kA1Z zmSZg$c2yutO4`&+|90T_DuR8iR)H_NOTaq~TxR)}mo=k9(;^sOSIunlB2KEP09(ww zB>#iTZXU|d_Tn>7R2Z;(UW*Q5#hnvci`kDfU*uk7uxkB<9!!Ws(IP_jo2z89Q z`cHO}eY2R81Du#wE4BSYGlbwHy8Vvyk#I(JTLpP@&nWady}nhYTn?GWz}u0Lvg+_J z(m+`Sj40mgQXP2u!pB!9y`Fo1<01ezt>9zm{IC4r44N0W-UsF=AcY7!S(PH_z9rC& zfv`Bzf!@5fJnl^v(dsi0oPH`Q>R??Nr|`#QG7Jz*js}XXeh+yMc;i_&5Rr%-)@9-C zFA_*f)ETe4m5!aptxvM0Khesb`5q+ne$P{$_kPW|PZ|6$HA(&b%JjLz?80aVwIwa; zLTylXN=lFWZr`=1GubhuH+#duB`A_x^>aAv8)*a8;W|bv$x_hzy8_r+ku9stJrn*7 D^Bo+{ literal 0 HcmV?d00001 diff --git a/res/gb-icon-256.png b/res/gb-icon-256.png new file mode 100644 index 0000000000000000000000000000000000000000..872eae6f07fd42af487b6d884b198f431543e6ea GIT binary patch literal 3289 zcmcguc{J3G*Z@oM>pR<;r^fovpSM|>pO~2#px4)Q zwKE<)OnLhzJ0v);vZ6#x{O|y~3yDNB>9q~iijvPobZYs~K+o#8$&ta{lP6E)=495> zs&%xrOihe6xrB-e-}8VVG-=d(2|Y$r9ZXQb`aHVeL!OMRRC7ZOtFt-sR>arI!HLm6 zJzbsALDs^|h{r`YCr5`M7*b198FqR<)8EI z?d>2Cu%p9;#`>zdnzF{as_$RN2(#qzVKyP+#^UT~;$8CG)UdI!k%qeZwa^ejFf=-f zSS+P4%fV}DVV0b97XpC1ygamYR8`bY$jZo=pF_DEl6H5)R>KXg&smC!iYO|{>*;H9 zKsivRX9BMJwKmpZU7SxG< zq2mMsp{}+vFyJZzAqR&`Js?DWmC`G6#_vHC@5aTGVj`Ox>;4c``6KC0c?rdXUlxr< zoAQe3a0~nS`C9WyzRrA+k^U%0LMKB^!^hj}=8bC)538wh2{zKH@(OZhRz_AfCYBhJ zDoa-y+PUO{SE-%nppaRvPr#s($+D0-TiGnrH>Bu&_T@`Hcmc#E0Yse0$zgW;O(8{p znEcc*>+IRHPo_edzwc81hP|x=u(&ZSM*njUbh>WEyx^r4XJN?mcLque_wPv^JNfyd z>!D!MAl<+Z7y>9M$v_Y(+;I_*mB-NW+_()T+_f))0O)Vf&XKOT=T|+IeXjb# z4>~b7TIdRKbFSw^Imw>#TEVlX{dMTnP7YcWqhr~}cqSyy0|Q{l3LdBK6{@B3kg$YC zDN+%8wnV_HlPs;xw{)Z-+k*K@d z_s2bEBqlb>dswX4PIX#H?*3b9ojUD#49O_$Q0VIUg@us4xH%+YwSBxcr9loX2mBv_ z>H^k?TzWjMFD^pTj19&CzpRuHlBpd-mzKYh`)E#C%eJY|^V*vaQSVZ5Qs{tVeBb(%pdLZ_K2KpKRf@JzhjpgB0`-2Yu! z+v}+)mq@1+t^2 zU4D0{>83INiz4=pSCxpb21i!Rw|29|bz>XX4aP(EW$jxwnb@szflz_9Uqcdd>&anY zV#j=ElM#&{ zRD7#UpReMM%cZ38;bQ;R-A1QCy|LFJ!A}x?G z$Q>%_TMWY`E}TFG(sn6_0Y%)TZaYzu6U*{|O*;W(&AXHFP#MJ6#4CkD3TVR z9Sn2f+Bez{R+ZE_P{98HMVnUgxnCo_Gmu!*4z$1+<>i_!?%46RX^0Yn*RHZ2qP8~Z zWplSt`9p|`>`#T;a-{rRIBKL|Zgj~@)nW5C_3ECJkMe?e7$9G^3?(c*jSl`xHFj0H zy7H&nhRgjlbgGmOpgsCVWOqR__^pr8?Eto}NDXHXSI(W(S7Q&r5>kAu%x{xR ztKYpBhGYD{YA-%F*k(f3IxjGDT`WP>+(gY0%~u_d43`W}E58fbL?5j+{Hb&_d#=t7 zIDUKU?W=oFH1@=;-5tk~pVM|s@l33n#Jj(Xmaa1gJKr0vlnX+HnPlFdt4dHMm;m1! zWX3k}=A`vldcQ6+21!i2NI==d-jc;kPP);C9~}y^&*JIcP8rF72lA>>=5X6{YN;8* z*e>CzoiO~+uqf`7t2N&q<4mb5j?2kZYuzj9}Ub`y8xhZ=s)#WaxkAe1N&dvL4scux!7T0A9nHbtlXWRP%J? zgyCcSbxFp^`-Xz!?#j8TXuQcO3Aue2mE?5z_3|K~mz}adj|=pRWdB)fDsuS<5S{*U*7!I7%AZk(-Aph9_u!Tv z0&viLz6|pc24_+rVSVRb596&Oee7ppatMzOxXVTVS1OH+RhzhQM6%!*dOIMDoC1;w zgl2sVFVH-OCeAFrIYxIRYB*vvQC}K7fuXB+)e-yFqFkc$JP(_AenG!DQN&5FxY+e% zJdzlF0cw-@Yuu?Q9ljP_7yP@M%OhHlPaufm7!jtj3QERQx}mev33*D*2JhlSqsZ#ub}nEm@d1FXz!(M2Y>_ + diff --git a/res/gba-icon-128.png b/res/gba-icon-128.png new file mode 100644 index 0000000000000000000000000000000000000000..53d17aba2221bffcd03de602de223a7324816809 GIT binary patch literal 3211 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 Mh0xQeSGSG)8>k=17XSbN literal 0 HcmV?d00001 diff --git a/res/gba-icon-256.png b/res/gba-icon-256.png new file mode 100644 index 0000000000000000000000000000000000000000..bd7fc64ad5297e49ce4af6b22bd47029740bafe1 GIT binary patch literal 5589 zcmdsb;XWmSlo{lO3E-fwq00io4N(KM``WJ!#Y{)-yt2HwM00^L` zWu&a?rS&iKe+Jbt3;(o~RWxV24@gSEUEB~KqUQsHqIscW7!b^ySAzwjMhK#u^Y}!F5l!BNp;Z?(jsQFyaArJ^eh)>Ew`?M-@ z&cigmJYuRed{RNguqbrI#W1QWYSz;%k%^j9f={(Fe6}?BTfWZ-2n5!ZwMRTp$9jSj z=~Rw~LzwLKNn7RxKU6aQRb9AaX`0XHK$~n127!X$!LraP1Gtl$QB3fQJY`W+a}_Vx zQ$@ItJ{={isnVKWQ9-e`mOoDIy+5>E|cz6X249&wMk_wCKbMrsQ$|;ePQ>LaD z#kf{wz3yVC=T}fvb8_~MiOujugmH56(9tneznT4zIxHmxFS(ScDy^$doP2HdmYtm| z!M!1`pgP{})B8i^%7(m(rYu7RXN%|dre-$UI>s;T+-#JB@(XLs<$O|I+e51sRTC4pR2)#X%1(O!>*m zJ(!&}w!I1aPa7{pS2*1K(}Gu3{wqq-ZV8HylysK|08%-1C3&NO<->dzGjdhh;jb^q zp-+ohskg&%ajOz&OMl$3@7o~|sqzRoAr1M@MAq#$KHRtwN_yZo8{sUj(1r?npN{Eg!9-SV_c1jt_2w&jK{AUH#IW2jx3CTaI4*R{dVC>m_N$J_Gtn|Oe zK;46=s3;{RAtB-7oUxCuFP*R>N~2Vzy|c5kZK6 zv#qT&g%;-8hZ3}3T{YUNtz+Qjb$7qJzTdSVc$^+R>khOQe)a0rpTonuaBCAeDXHfx zIo9WEO^7?MHsf`$-L~}0Aq-m+?T`6e?sKZv_Zy?uKtdM>(a<_ct;`cpBCW_Wt=oBB zeiz^vIO#WVbyi8RDagAjRH5ix7XmqM8^0|QRdqbhffWA@j9FIIUHpKhGhQuMlY%YVbJPD#@ z$BFJq;_W;fDq5G-1A>B_Pz(PGPp)cd%X|*OfSWd&D=0m~8d|FL3~k%BGvY0?7G74d zpd5Kyd+hXQ;gAd zfflvoSC?-mtyV$y$sbRfnXc-Olx@0iZ%j<{A_n&Y58^e7rj9#&{=7CkD%fSZups)`P|aQ2aiLM~l=?_C>F<;0A!f6m z``Bf4|Fvs|vO;X#{wZMESuZnkY4Y16xH4=BYu&i8?Bj2Tm(e}Ov)`^dr#DuymOARe z0t;W2Fd~g7ku%o1Vh`q~rC*HLezd+jfG&4V*~0PN4uez_t2~h<3IgBXULi%u-dY*< z{xzJ8PLkPu5jc)hO|-9+&=pHIYxX@v<3FPM%u7Tp=cALtFmp-yD?mLk)(7zNwkv^Bcl2F6ff>ey)V8 zU~0aMkNLj5RMgGtnqu_pc>O}4oeL%?*ur(YS#f{6py~>>M#|PDy|RUMl6*I3X_vJJ z;c1!&zQ;K#Y`Zd0I}0Bx&M#XyrQ-qb;Kq43SieujGBFx#&>6$2mQJPj872Ro7PrIB z3yW;z@|a=E8dCiQ?P6{mbF%SswhA=1j9dR*dx_TAsvcWnuqd~%FP4^(>#$rb<&S#N zXCc=`W?ATMwYNRDRO?^DhR`If7kXQ8-4F~? zjEOM;dM4za#I7^@iGT31A<>_a9R)GBIeV)8fB(A^IaijSUBqkst$#tIjcm?KUId+8 z6(`DwmF*@%)D89BS2AK3G50QeFh=LJ#oYcHW8e=*93$`6+WfHgbDZ)&{xdZNTOl_@ zXp#YMLd5yeMluZjqb*SWVlg6t891J{C&+EebMydQ?xr3;l$LHc3{AR~tN!xG08!%} zYb&@S6X#cfGQprHXtcfRuqGiS!?vVEVOcI_ZGIVr!Mu(>JqZcLvwrx){)=223Gs;N zhSTKQSUZFPsx$OHIkukDIBV^+lb{q)Ih3;qp6OQw9Wam>?O(4@^N_h%i|J{(%-K?G z-iAv_%LDWo1vp__T<%yjb*;BG>noo7G`nxR~Vu?9> z<}Fs~?`R-`M7vB?-YGnzZ^=1x_$G%5g&`Pr(bDJA$GCRQZn2Ysx&@&$okhcN@P3?w zv+h4ldaT0_eBtu5^~zP5ceVP|>`BK%xVYs24=-3xqkk=~i{@ zg@9!bpT;w1VS{NqaZG|fd1*j=`^v9+_uc3JjFNQNKTIqQG%RbJf=4_g6M7o(jH<4^$adV3RGy#AEwI?#399^LE=d8XL6J0~&OVP?HlJFnV z7RU>m2<1Bz+uLN@QxEr`6Q4SXa`XmMgN`K{E*~jf$`79u&)-*_lBQn_pyD8{ne$IMF4@ETW68o9N-a|>F<5<~rGOBevuQ*-^?SEnPCw2DSvQIY zz-y7LuRClz;slzf5{4nng*k#2t^7RPR~@^^brez{0Zrzv|F{g+Orjztyfwi>t&nDA zMo54V!++j?cFQSqTN@#BOyfZmJ($4Ne)^vj8YuzZ0l?nSL8%b4OJ~c5Ngnx#=k#fu zf@4}RB8tbjZ?R9hTN_jYI0OLBic7b+(*ZQr!6)=;3buwt!U=Cd+4`hl&HF-OMhCp( zI0X#mK?uAg=iC+U)cty_6RY1}e_k-|^`b!y4Og*~KYrGyop_w~8H$7%pcyO;z@r>( z(glCr|CJTT=nend>w=Utu8up6-=`XEQ9RTr0!pRT4RcWZ&bV&NL&N?V>W1L$5Ns|lH@jNam=s7p(ykOtG;=k=(5Qkqod!i4Vaf_ z>I2{(L@RnZIW;-lM(_nEGH$b%NG8grDHY}001TI&!^*^QB6wl86xS7wXS@S3D@w}} z5Q2HEQw;5J(r=zIuOZYWS*a7SZSnYl3m*Z>8?pWR!gOCY@u4#n%-H?FegzQuHds$f z_xP9m3d7{!osO625Erma#W>U{97IggCu#|Z{N5zfIN1{Ix@yK(lTaxC9r*SA@mHxU zDENCU9_n?^+FOEtS|%HA^dJ!i=Q#&n&r}{qDEQxH@dE@MPj+085cW0pkbsb zc2*n9dG$sVFCBvlz^N`jl^#uJtYBl(7X+vP;z;MG-LS0}oJL zeY<0TEei}K`yCQ~Z~U&>Jfx=4z#b%yvS1~>y?JAFX5J+NpZGj70N!9;N$CBE;1lsv zhEJV$OM^g?it#(PXP9YIKJj;m$#jZ?TxGY=cd@jNEcPHh2isp`lRe|b(c@X})-DQg z+c%A2ElSPJ!%&mM1^RXE0jv#Od^(zjUkX)ex{(hSu_u77giyN~LcSs6w*KBXiwRi2 zUk+u&!vnN04Cp^18aJ^zq-=ew;sCQ?RKo*N3nud+l9c)0xb;smCZOf;y^*pfV2GHn z2MFhdfo)UufyhJ#d7-PWWB2J2cJw~ytG)?sbW2j$|v^}+%%g;l4CX_ZW zQ_8bLYn%O{NB0w(Og?}11H=M;2w?ku)wDx5jOs7fe{?|A~l6 zW#1}4IUdyu4oIGy*7t@YB_bsTEo*^42L-bNy(WKNLx$w_WVq-EA=T<%uI-M&wu$_# zGf22g@LWHY*S=*e5h}DrP8&UB$!gXqqXBj(`T-1Accl zRYbnKL{@A6(FKL!1Y?41AIV@_Rul4$_or4`I7mt4m)V1hL zsZqnckx45kZqbr#ZHibpo5TAu^EYM&6?MEbpSajZ*f7L+V>sBd2SN7fTl2)2O7iN( zb|i!`tfbiQ>}riz{el4QOb!0DpuLt@#-NdMgL2yA!t(QqA>rn`UZ~^6+T&E-^}}QL z<2~}y>E}$}B&il*en*B)@E~X6xn{2yF7;0bth=5?2TK2J{wF^!3l@ay1XzARWs}9p zZQ%EH>x5mwU!3aD0X27$3pC4i-mw`G-z-PJO=HeV65n{Mw{Jmo?fmIi{Lm0qnv3wx_P^3mSXhdrLQ#d!z?=Lxz2w-Pbf5>L z6yv-2f9fE}LuJc_t2F+JlS%T@5(POd^g$S|lMy`r@3p$5X7*2fK3&qxsUDW<3iM($ z^Nn_vE%e5B{nH>K>RfhwlWTI)<()9Xnjr}1^v5+*&%-1`!zxccc-7=D^#b;l8RV_R z2E`X2@py`k$MD_PC%42qM+~3-L_2iVvprD@jzG_DUp|RU+CahJIT##frR8$A(umMa zn0R-_tB=C-(0`p8c`3sGJS&22(BdN&QCXKm1DzO#iTEzybnKI?6G1- zzN1@G-8I+mb!M@m*`i7rH2AUwM5*9>Dw`iaSHgjP+)3_1XttWz(|VLejh)5+#s5t@ z&hh3Edyb6p>@}kXB}4xxd&azLfxy{PVESL1&Y0Ozjw!6ZTvgSdb@WZ6?8uvV0o=C! z9qbjl(ab&zH{jZ8ba|IJ^d*VHgCijh`RCAZOYF=iA?BHMyQccI7s!>=euryOh9C4j}9#hQ<4O!4TKbB)wspsQMt>&%rO5SN1 zdCGj0^H*Q4tAbm*>2rwO%V$fXZWXC{$6^_k4iw+I4^8L`mG_6X`JiW8Qj0au1^i;3 z1y{PrFG!*o*UdE>b zN~KX@sJ^$MkDA7ZMk!YTT<)G30ga=X=d+4wwIjS)s@&>)^>{FR+Lc9hD}9yGL9+pG zf+(d~0RfxarxRJAha)Wbf&iQ%%t5BFt_66h6Gw;}~ zOmfJJrQ-GE+LM(aNco;XNg`DRcDq^{z2yV(+4ww0c@B*pt{S$0-(3bC!M# zsBZ40?U;4Fc`JScLdJza_hgaNisx%q0d2+d%8yxTEwjB?`BYXFH*Gq~uLHiJ>l3W$ zq83z6cNbHf_*n#oudd}N8K|yyu=tWBKEw$Pbl0A$=DFz%HxIgtb + diff --git a/res/gbc-icon-128.png b/res/gbc-icon-128.png new file mode 100644 index 0000000000000000000000000000000000000000..f22938d1601e4a9a1f278fab07fc38d1e1499758 GIT binary patch literal 1660 zcmc&x`#aNn9RGZ`#j>Wkq_NHB7TVazvP&+T?Tj6yxl}qu9E#9FZllc(9vxkfa+_Rg zNK~g*giu5|&T&f$$wPIzlP*q#qvxFe;k=&b{eC^K*Xx(}^Liik@)V#o3^V`$Xg61$ zXoY3p2MJ%w??T#x>R$#gq(3;D z(pi-{5aK0e+dJhQz6pmTRwGfi=8l|AqTOzXFQgCV#N2=&n4S(cHMF86=04NPwIHFz zk;Se0rC(o%m=$?RDerZ4ao_3`Od=SY6K$%}1|)lnb=G2W>x_&HHd>PG!hO=6ZMPOC zw9+(sq?IWXiCnFvsprEDH!?Qc zOy*@rtCE9?v3mGOzoSQjOAT?x#za%`j$<1Psaa8%N&4ngitRS%P@c0ZYqNt1jvVcO za=kWzZ7nQ4++7ym0|DSb^Ki8B4E9Vm#BQLPu}+Gw(QO$UOe}W(a4^t4zBuU)UY}SZ z>4@<^$*^NvlWDe8`WD)c3Q2oWR3{ujK>!y9@N9r71!TWxao>M$87uPi28bz#Z6C8? zP}^?xvUPO7-If{Uc_7KfDG-trkS_qun4mt2PbxVM*XAFL&;@` zvfj|t=8)g&Kv_qolWJ*mrWx*6_)?CAKvOjV?da2u#shRmUZ^^-n|q8(ygxonLAIGL z8_+BfvR&;&=P5lvB37+=fkKHF-Fc0Mn`H7qtqU@M0WfEfruKIW#j0&~x>Q>-(M8ox zL;M#Y0Yn1dg)IA$;<)SfAT`q0)}L35Z&g5#EnSGq!Ys-;OTdw1pm%H0qgfGn@b&b% zntjg8NC;mN-;d_N%&t-w#Ier(d1{O`gjsC~4m=|LK5r5gS zB|=%Ogki-GDh^yPE%Haes{4$FaQ%wTq|gRmQ_r#CtKnE}B+xa|f6P4IgNX=#xS*Vz z#eE6s_$q@pEc;u4cS(^Nv?^lpa`FOGC($8z?C@L$AJL$c-cy7VcvEcj`oa$`T_`Qc zk5L~$5m<}2)*Hiu+bwi$)LvG2O+4W{)!O5yqw*JU<(WJCn7z=;&<3F${gdCAkkPH& zQ`fS)LKLCw>T4Vx@Xmd=%6{J~3Hn6TYWs8F3fb{p@v{pc(ahv~#}W22gCfgg^NrWf zecqUQ*9Jk$yLNDVeRuCNq3&#LhH?ha5-K5vdMjsB(von$JY;~vp~EcrogPS+PbA6@ zmBiZ{?P`K-c$1bOo%9Os8Zgv~IN9)g2+YgYi zuiI(QAF0A*&}T>h!6VH@@Q^EnH)PyKP1%e6SyH;NU99PITMJD7Jz8}F(C~HYJxC)3 z{O%Ps*(oe21Pv9(A)O{vS~O?`_c}nPfc_;VZ)YI4lI(I9d1MHVISG;28nQu5$=aTK z@bVO3pa6>8y}(P~A~S4QZd5~}tbSL3vJNd}WdxUs!~t9M;ye(w{SCJb3igwqwZwNU zzV^OlKUTc!r<#u^4n|7GcGat}E&DDP-8u7i=Dn(wz-7JsIE>h0g+tdT!01N;viIDw zTkLp0Wh=xgtFI15Kb#A>>8I>{KFe&9mk{PZKY5~1Mfhr%)#i%k%>Q~n+8EPBwNI3W zYp*l8e{IrNl6(5lD;&D2%ktD*_2ji@d03ID63-|o$}e|}buG!sPnvNS=E{xd?LVt~ WSJt#YO;0rhD{$j`@-A@$<^KT2XMUgn literal 0 HcmV?d00001 diff --git a/res/gbc-icon-256.png b/res/gbc-icon-256.png new file mode 100644 index 0000000000000000000000000000000000000000..37659abab78cadef7c453638421253d8d5169c81 GIT binary patch literal 3227 zcmdT`X*AT28va>_!7yVmmSiW(*b>>sHVs4evSrU+Ok_8iG7%ADODK|k&q$^$6CwO9 zVUWE811tar0D!~9*vJ|H=zgtq z01Lyf5m0S!3jp*aOEX(E250t*`F}xiX=7zP4U0CXr)MaQ8+LH?U}j?BjEu~7=S-IHG!4Bi2`NF(wG~Za5MGRi zBZPxf04@Sz28B6iJq&M?JcTqkYg2rC;JgAR$o_t`d#)2EI4`R6X>8xK+XF~hC3Sgp zr0YWi9kl3a@nDBkd2tOnv2$2WXHfy!+{iax7O`+Hfy~fXnn;s4pJGq*7!_$$()F5C ze3HCSp=*~Cq=c2Y*x_nt43rhsWF%$n(Scl?(ER95U+X)eS2C0&wcX7kb(PH@MN%&r z1~daIuBPGq2*LD_my+Vr`l{9vVp3o*85pRESsn0glYg$)_lK)oge^W0~q4{%GH!L zjP#7pt7+@2THWz}rVBuw0e%d?{SY{FACN+;+g;Rg|CPo%?~w6dah$$sZEge5tpr<= z*%%+2gOJ1Yz6avm5d6*n*!bn649D5D+ShJXhu`9}jHHFamlB!yk{C8*Kkc3GpuJf? z`CkXH&f=8>04#GRM*6l9Q{QG2?BEz7&?XvEoY=}`wXTquxSFvf|GvvczKq#WBZ&Y{ zmBopSKt*Moxzb8KHZF*tJ8PAZ;|udVh|QR8Z32O$qI%Y6Z^dr>h(Qib|HOyws*}n6 z9GxOuER6KGBkm$pbFGs}dfQLPEl1rjtn&C!K(_SkQ7Z=8r`(t4aS#*%WhG7sjsX&+ zb{B%zz{7^Qpn5KM4HgY-(WS*XE|+@k%zOU#&e zVMRJD<(WCC{*&kH?%x&nN9Z{M7G99eL(W*;ELCbM>PquwLbVgsl@F;+QlqA}>*y=~ zQTJbyI6OhCYF!|F6a8;T{~^@F0(;L^KA#SCm36IiH3AhHtE{nQzNafE4GM@mF1;(O z8^ii#J=(=9;N>vLAN%py_;2Y>-U)JJjpGI1aVx>xeV>~SN{I$0b*h=UHX%FLCZ3ud zdbg>xe-Dd&Gi&+9!8b9$Op}%y+;k3gd}-T!O$&c`)VrO}?&IPoD~_?rm@P;z({j$7 z|JfmGcof&xqvNwSRIn-^af|Yn>ERSsC;Ug=uHbXlC0dz3Y_Y+~iM-h75AIVNipBO( zrenn50V%)l{YMNkaVbP03zM=%sRCQ@4FL!GYRI{6e*mDi^?tWlnON#tD)$1X<$A=h z%s(Yb>K#H2qC6`fI8T`xyObOO$exUVAz~^=dy$w<{tPhYe3fQb%6SWp)?8x7k=B1o zP6}O;_?F)Y6iwuaIkTq)&cz(@(53gjce9EyJt$!kxuxj+a#be${<%sQg%kE7c0LAM z60aW}SHl$iwL$`Ax`mW>LQ)8)tQU;z(0Gb23XI4NqV}PyX##qi^;@w(8^!0Enva5F zcVjxDYKTp}MS3j>*xU0;FAu7-J=IbxF-t40p?!Wu`{G3c)lShj65k>YURSRfT?(9W z)K{tTry7nd76GUV`6b;Wkw~{!U}1YrK3ycqcY@J)KpKR$i&CWc2O=ig8jcoxV4)Lg zbbU43?1R=7$0E`>ngVA2pG7@J-!F+om_Bgl-Dgjo@Uw}-!; zbfu{-KPb$*TC2Ic(;3%FY`j!o9RMM2_lnCAJU<8Z`&#-$#WrPR%)}5{<2%Y~5{?dd zhZTjzx|VAEv|JyHzTp>*o@Ms~jS*amNE7)BXBsft3+D?Z__ygN3rrt4%>KRm?)v5C ze3^noY+>Y_q3-CXmx zKRf@P;w3!0AbNN)Yr@S8=q zMX!2KDxR=fN^#J*m+Jd@XrJ})L8u0Rg3XBh#S|R(%3!x1$92mV&?e&NgZCw=^K9!Z zf*#oQY#f)cE#S>^hzykhP;*~4m=p40ay$?ZW*#8OeSWnc^NDWK(#E5I^lsc{DZSE? zv-=kTM;nhAU!q||IN0zx%UCdk5fO+@4lggUr$=ZUi_RVH))*!G$6c2r;=hfS>PuL~|>N8d(2UcCS% zJc6BdtEj&^+tmq?OUg=6{F)A0$4$ZG{r4Fs?cw!x%@sOhgQJ}$7rXFr6EAF;_mvtu z!TK$t@ay>O3BdA@q28wd`CXsy=<^!nuQ4^FBe~) zpy|BDv~w5cP>rQ#vMHdHQ{E+iJ(&8mnD{*9gwh&q8Q>?%==_+ zhrzY^^7a!$*ZHb|fHSogl*r|)4*DG_f~-tl(nB+56^em=Rj>E{eyXE*C0!ZU_EK7$ zhCqcD^~m(pP&E5IKaFiAk9A%rdAJNu?+*U_Rv4Lt4on^;PB*Wr&bfLN%sgyCc^+s; z_c?20?KNnztLwcXuE;G)hL7Uk1+Rt^ZXD+G#K>CvrjJm?>4o Vi#e*k#r=09n4ry#Y7N}({1YZqn5_T+ literal 0 HcmV?d00001 diff --git a/res/gbc-icon.svg b/res/gbc-icon.svg new file mode 100644 index 000000000..6286714bf --- /dev/null +++ b/res/gbc-icon.svg @@ -0,0 +1,2 @@ + +