diff --git a/include/arm11/config.h b/include/arm11/config.h index 4bc379b..ef928df 100644 --- a/include/arm11/config.h +++ b/include/arm11/config.h @@ -2,7 +2,7 @@ /* * This file is part of open_agb_firm - * Copyright (C) 2023 profi200 + * Copyright (C) 2024 profi200 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,6 +27,11 @@ extern "C" { #endif +#define OAF_WORK_DIR "sdmc:/3ds/open_agb_firm" +#define OAF_SAVE_DIR "saves" // Relative to work dir. +#define OAF_SCREENSHOT_DIR "screenshots" // Relative to work dir. + + typedef struct { // [general] @@ -60,9 +65,11 @@ typedef struct } OafConfig; //static_assert(sizeof(OafConfig) == 76, "nope"); +extern OafConfig g_oafConfig; -Result parseOafConfig(const char *const path, OafConfig *const cfg, const bool newCfgOnError); + +Result parseOafConfig(const char *const path, OafConfig *cfg, const bool newCfgOnError); #ifdef __cplusplus } // extern "C" diff --git a/include/arm11/oaf_video.h b/include/arm11/oaf_video.h new file mode 100644 index 0000000..cfc755d --- /dev/null +++ b/include/arm11/oaf_video.h @@ -0,0 +1,25 @@ +/* + * This file is part of open_agb_firm + * Copyright (C) 2024 profi200 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "types.h" +#include "kernel.h" + + + +KHandle OAF_videoInit(void); +void OAF_videoExit(void); \ No newline at end of file diff --git a/source/arm11/config.c b/source/arm11/config.c index 46d1143..e8925ff 100644 --- a/source/arm11/config.c +++ b/source/arm11/config.c @@ -1,6 +1,6 @@ /* * This file is part of open_agb_firm - * Copyright (C) 2023 profi200 + * Copyright (C) 2024 profi200 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,7 +26,6 @@ #define INI_BUF_SIZE (1024u) -// Note: Keep this synchronized with g_oafConfig in open_agb_firm.c. #define DEFAULT_CONFIG "[general]\n" \ "backlight=64\n" \ "backlightSteps=5\n" \ @@ -47,6 +46,51 @@ +// Default config. +OafConfig g_oafConfig = +{ + // [general] + 64, // backlight + 5, // backlightSteps + false, // directBoot + true, // useGbaDb + + // [video] + 2, // scaler + 2.2f, // gbaGamma + 1.54f, // lcdGamma + 1.f, // contrast + 0.f, // brightness + + // [audio] + 0, // Automatic audio output. + 127, // Control via volume slider. + + // [input] + { // buttonMaps + 0, // A + 0, // B + 0, // Select + 0, // Start + 0, // Right + 0, // Left + 0, // Up + 0, // Down + 0, // R + 0 // L + }, + + // [game] + 0, // saveSlot + 0xFF, // saveType + + // [advanced] + false, // saveOverride + 14 // defaultSave +}; + + + static u32 parseButtons(const char *str) { if(str == NULL || *str == '\0') return 0; @@ -149,11 +193,12 @@ static int cfgIniCallback(void* user, const char* section, const char* name, con } // TODO: Instead of writing a hardcoded string turn default config into a string. -Result parseOafConfig(const char *const path, OafConfig *const cfg, const bool newCfgOnError) +Result parseOafConfig(const char *const path, OafConfig *cfg, const bool newCfgOnError) { char *iniBuf = (char*)calloc(INI_BUF_SIZE, 1); if(iniBuf == NULL) return RES_OUT_OF_MEM; + cfg = (cfg != NULL ? cfg : &g_oafConfig); Result res = fsQuickRead(path, iniBuf, INI_BUF_SIZE - 1); if(res == RES_OK) ini_parse_string(iniBuf, cfgIniCallback, cfg); else if(newCfgOnError) diff --git a/source/arm11/oaf_video.c b/source/arm11/oaf_video.c new file mode 100644 index 0000000..d3446f3 --- /dev/null +++ b/source/arm11/oaf_video.c @@ -0,0 +1,268 @@ +/* + * This file is part of open_agb_firm + * Copyright (C) 2024 profi200 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "types.h" +#include "arm11/config.h" +#include "arm11/drivers/gx.h" +#include "util.h" +#include "oaf_error_codes.h" +#include "arm11/drivers/lgycap.h" +#include "arm11/bitmap.h" +#include "drivers/gfx.h" +#include "arm11/drivers/mcu.h" +#include "arm11/fmt.h" +#include "fsutil.h" +#include "kernel.h" +#include "kevent.h" +#include "arm11/gpu_cmd_lists.h" +#include "arm11/drivers/hid.h" + + + +static void adjustGammaTableForGba(void) +{ + // Credits for this algo go to Extrems. + const float targetGamma = g_oafConfig.gbaGamma; + const float lcdGamma = 1.f / g_oafConfig.lcdGamma; + const float contrast = g_oafConfig.contrast; + const float brightness = g_oafConfig.brightness / contrast; + const float contrastInTargetGamma = powf(contrast, targetGamma); + vu32 *const color_lut_data = &getGxRegs()->pdc0.color_lut_data; + for(u32 i = 0; i < 256; i++) + { + // Adjust i with brightness and convert to target gamma. + const float adjusted = powf((float)i / 255 + brightness, targetGamma); + + // Apply contrast, convert to LCD gamma, round to nearest and clamp. + const u32 res = clamp_s32(lroundf(powf(contrastInTargetGamma * adjusted, lcdGamma) * 255), 0, 255); + + // Same adjustment for red/green/blue. + *color_lut_data = res<<16 | res<<8 | res; + } +} + +static Result dumpFrameTex(void) +{ + // Stop LgyCap before dumping the frame to prevent glitches. + LGYCAP_stop(LGYCAP_DEV_TOP); + + // A1BGR5 format (alpha ignored). + constexpr u32 alignment = 0x80; // Make PPF happy. + alignas(4) static BmpV1WithMasks bmpHeaders = + { + { + .magic = 0x4D42, + .fileSize = alignment + 240 * 160 * 2, + .reserved = 0, + .reserved2 = 0, + .pixelOffset = alignment + }, + { + .headerSize = sizeof(Bitmapinfoheader), + .width = 240, + .height = -160, + .colorPlanes = 1, + .bitsPerPixel = 16, + .compression = BI_BITFIELDS, + .imageSize = 240 * 160 * 2, + .xPixelsPerMeter = 0, + .yPixelsPerMeter = 0, + .colorsUsed = 0, + .colorsImportant = 0 + }, + .rMask = 0xF800, + .gMask = 0x07C0, + .bMask = 0x003E + }; + + u32 outDim = PPF_DIM(240, 160); + u32 fileSize = alignment + 240 * 160 * 2; + if(g_oafConfig.scaler > 1) + { + outDim = PPF_DIM(360, 240); + fileSize = alignment + 360 * 240 * 2; + + bmpHeaders.header.fileSize = fileSize; + bmpHeaders.dib.width = 360; + bmpHeaders.dib.height = -240; + bmpHeaders.dib.imageSize = 360 * 240 * 2; + } + + // Transfer frame data out of the 512x512 texture. + // We will use the currently hidden frame buffer as temporary buffer. + // Note: This is a race with the currently displaying frame buffer + // because we just swapped buffers in the gfx handler function. + u32 *const tmpBuf = GFX_getBuffer(GFX_LCD_TOP, GFX_SIDE_LEFT); + GX_displayTransfer((u32*)0x18200000, PPF_DIM(512, 240), tmpBuf + (alignment / 4), outDim, + PPF_O_FMT(GX_A1BGR5) | PPF_I_FMT(GX_A1BGR5) | PPF_CROP_EN); + memcpy(tmpBuf, &bmpHeaders, sizeof(bmpHeaders)); + GFX_waitForPPF(); + + // Get current date & time. + RtcTimeDate td; + MCU_getRtcTimeDate(&td); + + // Construct file path from date & time. Then write the file. + char fn[36]; + ee_sprintf(fn, OAF_SCREENSHOT_DIR "/%04X_%02X_%02X_%02X_%02X_%02X.bmp", + td.y + 0x2000, td.mon, td.d, td.h, td.min, td.s); + const Result res = fsQuickWrite(fn, tmpBuf, fileSize); + + // Restart LgyCap. + LGYCAP_start(LGYCAP_DEV_TOP); + + return res; +} + +static void gbaGfxHandler(void *args) +{ + const KHandle event = (KHandle)args; + + while(1) + { + if(waitForEvent(event) != KRES_OK) break; + clearEvent(event); + + // All measurements are the worst timings in ~30 seconds of runtime. + // Measured with timer prescaler 1. + // BGR8: + // 240x160 no scaling: ~184 µs + // 240x160 bilinear x1.5: ~408 µs + // 360x240 no scaling: ~437 µs + // + // A1BGR5: + // 240x160 no scaling: ~188 µs (25300 ticks) + // 240x160 bilinear x1.5: ~407 µs (54619 ticks) + // 360x240 no scaling: ~400 µs (53725 ticks) + static bool inited = false; + u32 listSize; + const u32 *list; + if(inited == false) + { + inited = true; + + listSize = sizeof(gbaGpuInitList); + list = (u32*)gbaGpuInitList; + } + else + { + listSize = sizeof(gbaGpuList2); + list = (u32*)gbaGpuList2; + } + GX_processCommandList(listSize, list); + GFX_waitForP3D(); + GX_displayTransfer((u32*)GPU_RENDER_BUF_ADDR, PPF_DIM(240, 400), GFX_getBuffer(GFX_LCD_TOP, GFX_SIDE_LEFT), + PPF_DIM(240, 400), PPF_O_FMT(GX_BGR8) | PPF_I_FMT(GX_BGR8)); + GFX_waitForPPF(); + GFX_swapBuffers(); + + // Trigger only if both are held and at least one is detected as newly pressed down. + if(hidKeysHeld() == (KEY_Y | KEY_SELECT) && hidKeysDown() != 0) + dumpFrameTex(); + } + + taskExit(); +} + +static KHandle setupFrameCapture(const u8 scaler) +{ + const bool is240x160 = scaler < 2; + static s16 matrix[12 * 8] = + { + // Vertical. + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0x24B0, 0x4000, 0, 0x24B0, 0x4000, 0, 0, + 0x4000, 0x2000, 0, 0x4000, 0x2000, 0, 0, 0, + 0, -0x4B0, 0, 0, -0x4B0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + // Horizontal. + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0x24B0, 0, 0, 0x24B0, 0, 0, + 0x4000, 0x4000, 0x2000, 0x4000, 0x4000, 0x2000, 0, 0, + 0, 0, -0x4B0, 0, 0, -0x4B0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }; + + const Result res = fsQuickRead("gba_scaler_matrix.bin", matrix, sizeof(matrix)); + if(res != RES_OK && res != RES_FR_NO_FILE) + { + ee_printf("Failed to load hardware scaling matrix: %s\n", result2String(res)); + } + + LgyCapCfg gbaCfg; + gbaCfg.cnt = LGYCAP_SWIZZLE | LGYCAP_ROT_NONE | LGYCAP_FMT_A1BGR5 | (is240x160 ? 0 : LGYCAP_HSCALE_EN | LGYCAP_VSCALE_EN); + gbaCfg.w = (is240x160 ? 240 : 360); + gbaCfg.h = (is240x160 ? 160 : 240); + gbaCfg.irq = 0; + gbaCfg.vLen = 6; + gbaCfg.vPatt = 0b00011011; + memcpy(gbaCfg.vMatrix, matrix, 6 * 8 * 2); + gbaCfg.hLen = 6; + gbaCfg.hPatt = 0b00011011; + memcpy(gbaCfg.hMatrix, &matrix[6 * 8], 6 * 8 * 2); + + return LGYCAP_init(LGYCAP_DEV_TOP, &gbaCfg); +} + +KHandle OAF_videoInit(void) +{ +#ifdef NDEBUG + // Force black and turn the backlight off on the bottom screen. + // Don't turn the backlight off on 2DS (1 panel). + GFX_setForceBlack(false, true); + if(MCU_getSystemModel() != SYS_MODEL_2DS) + GFX_powerOffBacklight(GFX_BL_BOT); +#endif + + // Initialize frame capture and frame handler. + const u8 scaler = g_oafConfig.scaler; + const KHandle frameReadyEvent = setupFrameCapture(scaler); + patchGbaGpuCmdList(scaler); + createTask(0x800, 3, gbaGfxHandler, (void*)frameReadyEvent); + + // Adjust gamma table and setup button overrides. + adjustGammaTableForGba(); + + // Load border if any exists. + if(scaler == 0) // No borders for scaled modes. + { + // Abuse currently invisible frame buffer as temporary buffer. + void *const borderBuf = GFX_getBuffer(GFX_LCD_TOP, GFX_SIDE_LEFT); + if(fsQuickRead("border.bgr", borderBuf, 400 * 240 * 3) == RES_OK) + { + // Copy border in swizzled form to GPU render buffer. + GX_displayTransfer(borderBuf, PPF_DIM(240, 400), (u32*)GPU_RENDER_BUF_ADDR, + PPF_DIM(240, 400), PPF_O_FMT(GX_BGR8) | PPF_I_FMT(GX_BGR8) | PPF_OUT_TILED); + GFX_waitForPPF(); + } + } + + return frameReadyEvent; +} + +void OAF_videoExit(void) +{ + // frameReadyEvent deleted by this function. + // gbaGfxHandler() will automatically terminate. + LGYCAP_deinit(LGYCAP_DEV_TOP); +} \ No newline at end of file diff --git a/source/arm11/open_agb_firm.c b/source/arm11/open_agb_firm.c index 6144da7..af92636 100644 --- a/source/arm11/open_agb_firm.c +++ b/source/arm11/open_agb_firm.c @@ -1,6 +1,6 @@ /* * This file is part of open_agb_firm - * Copyright (C) 2021 derrek, profi200 + * Copyright (C) 2024 derrek, profi200 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,83 +16,30 @@ * along with this program. If not, see . */ -#include #include #include #include "types.h" -#include "arm11/config.h" #include "util.h" -#include "arm11/drivers/lgy11.h" -#include "drivers/lgy_common.h" #include "arm_intrinsic.h" #include "oaf_error_codes.h" #include "fs.h" -#include "fsutil.h" #include "arm11/fmt.h" -#include "arm11/drivers/gx.h" -#include "arm11/drivers/lgycap.h" -#include "drivers/gfx.h" #include "arm11/drivers/mcu.h" -#include "kernel.h" -#include "kevent.h" -#include "arm11/gpu_cmd_lists.h" +#include "drivers/gfx.h" #include "arm11/drivers/hid.h" +#include "fsutil.h" #include "arm11/filebrowser.h" -#include "arm11/drivers/codec.h" +#include "arm11/config.h" #include "arm11/save_type.h" #include "arm11/patch.h" -#include "arm11/bitmap.h" +#include "arm11/drivers/codec.h" +#include "drivers/lgy_common.h" +#include "arm11/oaf_video.h" +#include "arm11/drivers/lgy11.h" +#include "kernel.h" +#include "kevent.h" -#define OAF_WORK_DIR "sdmc:/3ds/open_agb_firm" -#define OAF_SAVE_DIR "saves" // Relative to work dir. -#define OAF_SCREENSHOT_DIR "screenshots" // Relative to work dir. - - - -// Default config. -// Note: Keep this synchronized with DEFAULT_CONFIG in config.c. -static OafConfig g_oafConfig = -{ - // [general] - 64, // backlight - 5, // backlightSteps - false, // directBoot - true, // useGbaDb - - // [video] - 2, // scaler - 2.2f, // gbaGamma - 1.54f, // lcdGamma - 1.f, // contrast - 0.f, // brightness - - // [audio] - 0, // Automatic audio output. - 127, // Control via volume slider. - - // [input] - { // buttonMaps - 0, // A - 0, // B - 0, // Select - 0, // Start - 0, // Right - 0, // Left - 0, // Up - 0, // Down - 0, // R - 0 // L - }, - - // [game] - 0, // saveSlot - 0xFF, // saveType - - // [advanced] - false, // saveOverride - 14 // defaultSave -}; static KHandle g_frameReadyEvent = 0; @@ -158,151 +105,6 @@ static Result loadGbaRom(const char *const path, u32 *const romSizeOut) return res; } -static void adjustGammaTableForGba(void) -{ - // Credits for this algo go to Extrems. - const float targetGamma = g_oafConfig.gbaGamma; - const float lcdGamma = 1.f / g_oafConfig.lcdGamma; - const float contrast = g_oafConfig.contrast; - const float brightness = g_oafConfig.brightness / contrast; - const float contrastInTargetGamma = powf(contrast, targetGamma); - vu32 *const color_lut_data = &getGxRegs()->pdc0.color_lut_data; - for(u32 i = 0; i < 256; i++) - { - // Adjust i with brightness and convert to target gamma. - const float adjusted = powf((float)i / 255 + brightness, targetGamma); - - // Apply contrast, convert to LCD gamma, round to nearest and clamp. - const u32 res = clamp_s32(lroundf(powf(contrastInTargetGamma * adjusted, lcdGamma) * 255), 0, 255); - - // Same adjustment for red/green/blue. - *color_lut_data = res<<16 | res<<8 | res; - } -} - -static Result dumpFrameTex(void) -{ - // Stop LgyCap before dumping the frame to prevent glitches. - LGYCAP_stop(LGYCAP_DEV_TOP); - - // A1BGR5 format (alpha ignored). - constexpr u32 alignment = 0x80; // Make PPF happy. - alignas(4) static BmpV1WithMasks bmpHeaders = - { - { - .magic = 0x4D42, - .fileSize = alignment + 240 * 160 * 2, - .reserved = 0, - .reserved2 = 0, - .pixelOffset = alignment - }, - { - .headerSize = sizeof(Bitmapinfoheader), - .width = 240, - .height = -160, - .colorPlanes = 1, - .bitsPerPixel = 16, - .compression = BI_BITFIELDS, - .imageSize = 240 * 160 * 2, - .xPixelsPerMeter = 0, - .yPixelsPerMeter = 0, - .colorsUsed = 0, - .colorsImportant = 0 - }, - .rMask = 0xF800, - .gMask = 0x07C0, - .bMask = 0x003E - }; - - u32 outDim = PPF_DIM(240, 160); - u32 fileSize = alignment + 240 * 160 * 2; - if(g_oafConfig.scaler > 1) - { - outDim = PPF_DIM(360, 240); - fileSize = alignment + 360 * 240 * 2; - - bmpHeaders.header.fileSize = fileSize; - bmpHeaders.dib.width = 360; - bmpHeaders.dib.height = -240; - bmpHeaders.dib.imageSize = 360 * 240 * 2; - } - - // Transfer frame data out of the 512x512 texture. - // We will use the currently hidden frame buffer as temporary buffer. - // Note: This is a race with the currently displaying frame buffer - // because we just swapped buffers in the gfx handler function. - u32 *const tmpBuf = GFX_getBuffer(GFX_LCD_TOP, GFX_SIDE_LEFT); - GX_displayTransfer((u32*)0x18200000, PPF_DIM(512, 240), tmpBuf + (alignment / 4), outDim, - PPF_O_FMT(GX_RGB5A1) | PPF_I_FMT(GX_RGB5A1) | PPF_CROP_EN); - memcpy(tmpBuf, &bmpHeaders, sizeof(bmpHeaders)); - GFX_waitForPPF(); - - // Get current date & time. - RtcTimeDate td; - MCU_getRtcTimeDate(&td); - - // Construct file path from date & time. Then write the file. - char fn[36]; - ee_sprintf(fn, OAF_SCREENSHOT_DIR "/%04X_%02X_%02X_%02X_%02X_%02X.bmp", - td.y + 0x2000, td.mon, td.d, td.h, td.min, td.s); - const Result res = fsQuickWrite(fn, tmpBuf, fileSize); - - // Restart LgyCap. - LGYCAP_start(LGYCAP_DEV_TOP); - - return res; -} - -static void gbaGfxHandler(void *args) -{ - const KHandle event = (KHandle)args; - - while(1) - { - if(waitForEvent(event) != KRES_OK) break; - clearEvent(event); - - // All measurements are the worst timings in ~30 seconds of runtime. - // Measured with timer prescaler 1. - // BGR8: - // 240x160 no scaling: ~184 µs - // 240x160 bilinear x1.5: ~408 µs - // 360x240 no scaling: ~437 µs - // - // A1BGR5: - // 240x160 no scaling: ~188 µs (25300 ticks) - // 240x160 bilinear x1.5: ~407 µs (54619 ticks) - // 360x240 no scaling: ~400 µs (53725 ticks) - static bool inited = false; - u32 listSize; - const u32 *list; - if(inited == false) - { - inited = true; - - listSize = sizeof(gbaGpuInitList); - list = (u32*)gbaGpuInitList; - } - else - { - listSize = sizeof(gbaGpuList2); - list = (u32*)gbaGpuList2; - } - GX_processCommandList(listSize, list); - GFX_waitForP3D(); - GX_displayTransfer((u32*)GPU_RENDER_BUF_ADDR, PPF_DIM(240, 400), GFX_getBuffer(GFX_LCD_TOP, GFX_SIDE_LEFT), - PPF_DIM(240, 400), PPF_O_FMT(GX_BGR8) | PPF_I_FMT(GX_BGR8)); - GFX_waitForPPF(); - GFX_swapBuffers(); - - // Trigger only if both are held and at least one is detected as newly pressed down. - if(hidKeysHeld() == (KEY_Y | KEY_SELECT) && hidKeysDown() != 0) - dumpFrameTex(); - } - - taskExit(); -} - void changeBacklight(s16 amount) { u8 min, max; @@ -468,48 +270,6 @@ Result oafParseConfigEarly(void) return res; } -static KHandle setupFrameCapture(const u8 scaler) -{ - const bool is240x160 = scaler < 2; - static s16 matrix[12 * 8] = - { - // Vertical. - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0x24B0, 0x4000, 0, 0x24B0, 0x4000, 0, 0, - 0x4000, 0x2000, 0, 0x4000, 0x2000, 0, 0, 0, - 0, -0x4B0, 0, 0, -0x4B0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - - // Horizontal. - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0x24B0, 0, 0, 0x24B0, 0, 0, - 0x4000, 0x4000, 0x2000, 0x4000, 0x4000, 0x2000, 0, 0, - 0, 0, -0x4B0, 0, 0, -0x4B0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 - }; - - const Result res = fsQuickRead("gba_scaler_matrix.bin", matrix, sizeof(matrix)); - if(res != RES_OK && res != RES_FR_NO_FILE) - { - ee_printf("Failed to load hardware scaling matrix: %s\n", result2String(res)); - } - - LgyCapCfg gbaCfg; - gbaCfg.cnt = LGYCAP_OUT_SWIZZLE | LGYCAP_ROT_NONE | LGYCAP_OUT_FMT_A1BGR5 | (is240x160 ? 0 : LGYCAP_HSCALE_EN | LGYCAP_VSCALE_EN); - gbaCfg.w = (is240x160 ? 240 : 360); - gbaCfg.h = (is240x160 ? 160 : 240); - gbaCfg.vLen = 6; - gbaCfg.vPatt = 0b00011011; - memcpy(gbaCfg.vMatrix, matrix, 6 * 8 * 2); - gbaCfg.hLen = 6; - gbaCfg.hPatt = 0b00011011; - memcpy(gbaCfg.hMatrix, &matrix[6 * 8], 6 * 8 * 2); - - return LGYCAP_init(LGYCAP_DEV_TOP, &gbaCfg); -} - Result oafInitAndRun(void) { Result res; @@ -565,42 +325,16 @@ Result oafInitAndRun(void) res = LGY_prepareGbaMode(g_oafConfig.directBoot, saveType, filePath); if(res == RES_OK) { -#ifdef NDEBUG - // Force black and turn the backlight off on the bottom screen. - // Don't turn the backlight off on 2DS (1 panel). - GFX_setForceBlack(false, true); - if(MCU_getSystemModel() != SYS_MODEL_2DS) - GFX_powerOffBacklight(GFX_BL_BOT); -#endif + // Initialize video output (frame capture, post processing ect.). + g_frameReadyEvent = OAF_videoInit(); - // Initialize frame capture and frame handler. - const KHandle frameReadyEvent = setupFrameCapture(g_oafConfig.scaler); - patchGbaGpuCmdList(g_oafConfig.scaler); - createTask(0x800, 3, gbaGfxHandler, (void*)frameReadyEvent); - g_frameReadyEvent = frameReadyEvent; - - // Adjust gamma table and setup button overrides. - adjustGammaTableForGba(); + // Setup button overrides. const u32 *const maps = g_oafConfig.buttonMaps; u16 overrides = 0; for(unsigned i = 0; i < 10; i++) if(maps[i] != 0) overrides |= 1u<