Compare commits

...

11 Commits

Author SHA1 Message Date
profi200 6b882e9395
Don't run actions on pull requests. 2024-12-06 22:37:25 +01:00
profi200 4e4c2aa2b5
Updated all libretro based color profiles.
Added GB micro, GBA SP (AGS-101), DS lite and VBA/No$GBA profiles.
2024-12-04 00:44:14 +01:00
profi200 dd90d498c4
!Breaking change! Changed the config file format to use strings in many places instead of values.
Added a new saturation setting for color profiles other than none.
Removed lcdGamma and displayGamma temporarily.
Updated README.md with all the changes.
2024-12-03 20:34:53 +01:00
Elouan Martinet 877f7c61d0 Add an option to use current rom directory for config and saves 2024-10-28 09:30:36 +01:00
profi200 7a7190baea
[README] Mention that oaf is not affected by the screen wrap bug because people are talking shit. 2024-09-22 15:14:03 +02:00
profi200 edd43a6fcc
Updated libn3ds.
Fixed hang on power off when all backlights are off (libn3ds).
Screenshots are now always in native resolution.
2024-08-30 18:45:24 +02:00
profi200 50e2f30e0d
[README] Mention new features and oaf is no longer in alpha. 2024-08-02 19:35:52 +02:00
profi200 79b2457e8b
[File browser] Filter out all entries starting with a dot. F.....g Mac OS. 2024-07-31 16:19:46 +02:00
profi200 5356e5c89a
Implemented full gamma correction for top LCDs which improves colors noticeably without crushing details in shadows.
The disadvantage is that the gbaGamma, lcdGamma, brightness and contrast settings don't work anymore for now. They will be reimplemented with color profile settings later.
2024-07-30 16:37:05 +02:00
profi200 67ce019b36
Forgot to check the ROM size before adding the padding. 2024-07-26 21:22:40 +02:00
profi200 41db8760a6
Made ROM padding code ~42% faster. 2024-07-26 20:37:49 +02:00
10 changed files with 474 additions and 157 deletions

View File

@ -2,7 +2,6 @@ name: C/C++ CI
on:
push:
pull_request:
workflow_dispatch:
jobs:

View File

@ -8,10 +8,14 @@ open_agb_firm is also a complete and better alternative to GBA VC injects (AGB_F
* User configuration, such as gamma settings
* Button remapping
* Border support for 1:1 scaling mode
* Gamma correction to fix the washed out look of games
* Color correction to mimic the look of the GBA/DS phat LCD
* And more to come!
Unlike AGB_FIRM open_agb_firm is not affected by the famous bug where the video output wraps around leaving garbled lines at the bottom of the screen. SD cluster size doesn't matter.
## Disclaimer
open_agb_firm is currently in alpha. While open_agb_firm is relatively stable and safe to use, some quirks that have not been fixed. See [Known Issues](#known-issues) for more information.
open_agb_firm is currently in beta. While open_agb_firm is relatively stable and safe to use, some quirks that have not been fixed. See [Known Issues](#known-issues) for more information.
Additionally, we are not responsible for any damage that may occur to your system as a direct or indirect result of you using open_agb_firm.
@ -45,7 +49,7 @@ Settings are stored in `/3ds/open_agb_firm/config.ini`.
### General
General settings.
`u8 backlight` - Backlight brightness in luminance (cd/m²)
`u8 backlight` - Backlight brightness in luminance (cd/m²).
* Default: `64`
* Possible values:
* Old 3DS: `20`-`117`
@ -53,44 +57,56 @@ General settings.
* Values ≤`64` are recommended.
* Hardware calibration from your CTRNAND is required to get the correct brightness for both LCDs.
`u8 backlightSteps` - How much to adjust backlight brightness by
`u8 backlightSteps` - How much to adjust backlight brightness by.
* Default: `5`
`bool directBoot` - Skip GBA BIOS intro at game startup
`bool directBoot` - Skip GBA BIOS intro at game startup.
* Default: `false`
`bool useGbaDb` - Use `gba_db.bin` to get save types
`bool useGbaDb` - Use `gba_db.bin` to get save types.
* Default: `true`
`bool useSavesFolder` - Use `/3ds/open_agb_firm/saves` for save files instead of the ROM directory.
* Default: `true`
### Video
Video-related settings.
`u8 scaler` - Video scaler. 0 = none, 1 = bilinear, 2 = hardware.
* Default: `2`
`string scaler` - Video scaler.
* Default: `matrix`
* Options: `none`, `bilinear`, `matrix`
`float gbaGamma` - GBA input gamma
* Default: `2.2`
`string colorProfile` - Color correction profile.
* Default: `none`
* Options:
* `none` Disable all color correction options.
* `gba` Game Boy Advance.
* `gb_micro` Game Boy micro.
* `gba_sp101` Game Boy Advance SP (AGS-101).
* `nds` Nintendo DS (DS phat).
* `ds_lite` Nintendo DS lite.
* `nso` Nintendo Switch Online.
* `vba` Visual Boy Advance/No$GBA full.
* `identity` No color space conversion.
* If you just want less saturated colors or to change other basic settings like contrast or brightness then set this to `identity`.
* Due to most 2/3DS LCDs not being calibrated correctly from factory the look may not match exactly what you see on real hardware.
* Due to a lot of extra RAM access and extra CPU processing per frame, battery runtime is affected with color profiles other than `none`.
`float lcdGamma` - Output LCD gamma
* Default : `1.54`
`float contrast` - Screen gain
`float contrast` - Screen gain. No effect when `colorProfile=none`.
* Default: `1.0`
`float brightness` - Screen lift
`float brightness` - Screen lift. No effect when `colorProfile=none`.
* Default: `0.0`
`string colorProfile` - Color correction profile. `none`, `gba`, `nds` or `nds_white`.
* Default: `none`
* For the gba profile it's recommended to adjust lcdGamma to match a GBA. For New 3DS XL with IPS LCD roughly 1.8 is good.
* Due to most 2/3DS LCDs not being calibrated correctly from factory the look may not match exactly what you see on a real GBA.
* Due to a lot of extra RAM access and up to 6.3 ms (worst case for scaler=2) of extra CPU processing time per frame, battery run time is affected with color profiles other than none.
`float saturation` - Screen saturation. No effect when `colorProfile=none`.
* Default: `1.0`
### Audio
Audio settings.
`u8 audioOut` - Audio output. 0 = auto, 1 = speakers, 2 = headphones.
* Default: `0`
`string audioOut` - Audio output.
* Default: `auto`
* Options: `auto`, `speakers`, `headphones`
`s8 volume` - Audio volume. Values above 48 mean control via volume slider. Range -128 (muted) to -20 (100%). Avoid the range -19 to 48.
* Default: `127`
@ -132,7 +148,7 @@ Note that button mappings can cause input lag of up to 1 frame depending on when
`L` - Button map for the L button.
* Default: `none`
Example:
Example which maps the D-Pad and Circle-Pad to the GBA D-Pad:
```
[input]
RIGHT=RIGHT,CP_RIGHT
@ -144,29 +160,38 @@ DOWN=DOWN,CP_DOWN
### Game
Game-specific settings. Only intended to be used in the per-game settings (romName.ini in `/3ds/open_agb_firm/saves`).
`u8 saveSlot` - Savegame slot (0-9)
`u8 saveSlot` - Savegame slot (0-9).
* Default: `0`
`u8 saveType` - Override to use a specific save type, see values for `defaultSave` (0-15, 255)
* Default: `255` (disabled)
`string saveType` - Override to use a specific save type.
* Default: `auto`
* Options starting with `rom_256m` are intended for 32 MiB games. Options ending with `rtc` enable the hardware real-time clock:
* `eeprom_8k`
* `rom_256m_eeprom_8k`
* `eeprom_64k`
* `rom_256m_eeprom_64k`
* `flash_512k_atmel_rtc`
* `flash_512k_atmel`
* `flash_512k_sst_rtc`
* `flash_512k_sst`
* `flash_512k_panasonic_rtc`
* `flash_512k_panasonic`
* `flash_1m_macronix_rtc`
* `flash_1m_macronix`
* `flash_1m_sanyo_rtc`
* `flash_1m_sanyo`
* `sram_256k`
* `none`
* `auto`
### Advanced
Options for advanced users. No pun intended.
`bool saveOverride` - Open save type override menu after selecting a game
`bool saveOverride` - Open save type override menu after selecting a game.
* Default: `false`
`u16 defaultSave` - Change save type default when save type is not in `gba_db.bin` and cannot be autodetected
* Default: `14` (SRAM 256k)
* Possible values:
* `0`, `1`: EEPROM 8k
* `2`, `3`: EEPROM 64k
* `4`, `6`, `8`: Flash 512k RTC
* `5`, `7`, `9`: Flash 512k
* `10`, `12`: Flash 1m RTC
* `11`, `13`: Flash 1m
* `14`: SRAM 256k
* `15`: None
`string defaultSave` - Save type default when save type is not in `gba_db.bin` and cannot be autodetected. Same options as for `saveType` above except `auto` is not supported.
* Default: `sram_256k`
## Patches
open_agb_firm supports automatically applying IPS and UPS patches. To use a patch, rename the patch file to match the ROM file name (without the extension).

View File

@ -39,14 +39,14 @@ typedef struct
u8 backlightSteps;
bool directBoot;
bool useGbaDb;
bool useSavesFolder;
// [video]
u8 scaler; // 0 = 1:1, 1 = bilinear (GPU) x1.5, 2 = matrix (hardware) x1.5.
float gbaGamma;
float lcdGamma;
float contrast;
float brightness;
u8 colorProfile; // 0 = none, 1 = GBA, 2 = DS phat, 3 = DS phat white.
u8 scaler; // 0 = 1:1/none, 1 = bilinear (GPU) x1.5, 2 = matrix (hardware) x1.5.
u8 colorProfile; // 0 = none, 1 = GBA, 2 = GB micro, 3 = GBA SP (AGS-101), 4 = DS phat, 5 = DS lite, 6 = Nintendo Switch Online, 7 = Visual Boy Advance/No$GBA, 8 = identity.
float contrast; // Range 0.0-1.0.
float brightness; // Range 0.0-1.0.
float saturation; // Range 0.0-1.0.
// [audio]
u8 audioOut; // 0 = auto, 1 = speakers, 2 = headphones.
@ -62,11 +62,10 @@ typedef struct
// [advanced]
bool saveOverride;
u16 defaultSave;
u16 defaultSave; // TODO: Should be u8. Investigate if u8 has any downsides.
} OafConfig;
//static_assert(sizeof(OafConfig) == 76, "nope");
extern OafConfig g_oafConfig;
extern OafConfig g_oafConfig; // Global config in config.c.

View File

@ -0,0 +1,34 @@
#pragma once
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "types.h"
#ifdef __cplusplus
extern "C"
{
#endif
void makeOpenBusPaddingFast(u32 *romEnd);
#ifdef __cplusplus
} // extern "C"
#endif

@ -1 +1 @@
Subproject commit 6259b6b8ffe4bf82481dc93aeadbcc96738c2b9f
Subproject commit f6717f66858634b677ed695ee346a89db7684b43

View File

@ -30,20 +30,23 @@
"backlight=64\n" \
"backlightSteps=5\n" \
"directBoot=false\n" \
"useGbaDb=true\n\n" \
"useGbaDb=true\n" \
"useSavesFolder=true\n\n" \
\
"[video]\n" \
"scaler=2\n" \
"gbaGamma=2.2\n" \
"lcdGamma=1.54\n" \
"scaler=matrix\n" \
"colorProfile=none\n" \
"contrast=1.0\n" \
"brightness=0.0\n" \
"colorProfile=none\n\n" \
"saturation=1.0\n\n" \
\
"[audio]\n" \
"audioOut=0\n" \
"audioOut=auto\n" \
"volume=127\n\n" \
\
"[advanced]\n" \
"saveOverride=false\n" \
"defaultSave=14"
"defaultSave=sram_256k"
@ -55,14 +58,14 @@ OafConfig g_oafConfig =
5, // backlightSteps
false, // directBoot
true, // useGbaDb
true, // useSavesFolder
// [video]
2, // scaler
2.2f, // gbaGamma
1.54f, // lcdGamma
0, // colorProfile
1.f, // contrast
0.f, // brightness
0, // colorProfile
1.f, // saturation
// [audio]
0, // Automatic audio output.
@ -84,7 +87,7 @@ OafConfig g_oafConfig =
// [game]
0, // saveSlot
0xFF, // saveType
255, // saveType
// [advanced]
false, // saveOverride
@ -129,7 +132,7 @@ static u32 parseButtons(const char *str)
return map & ~(1u<<12);
}
static int cfgIniCallback(void* user, const char* section, const char* name, const char* value)
static int cfgIniCallback(void *user, const char *section, const char *name, const char *value)
{
OafConfig *const config = (OafConfig*)user;
@ -143,37 +146,61 @@ static int cfgIniCallback(void* user, const char* section, const char* name, con
config->directBoot = (strcmp(value, "false") == 0 ? false : true);
else if(strcmp(name, "useGbaDb") == 0)
config->useGbaDb = (strcmp(value, "true") == 0 ? true : false);
else if(strcmp(name, "useSavesFolder") == 0)
config->useSavesFolder = (strcmp(value, "true") == 0 ? true : false);
}
else if(strcmp(section, "video") == 0)
{
if(strcmp(name, "scaler") == 0)
config->scaler = (u8)strtoul(value, NULL, 10);
else if(strcmp(name, "gbaGamma") == 0)
config->gbaGamma = str2float(value);
else if(strcmp(name, "lcdGamma") == 0)
config->lcdGamma = str2float(value);
else if(strcmp(name, "contrast") == 0)
config->contrast = str2float(value);
else if(strcmp(name, "brightness") == 0)
config->brightness = str2float(value);
{
if(strcmp(value, "none") == 0)
config->scaler = 0;
else if(strcmp(value, "bilinear") == 0)
config->scaler = 1;
else if(strcmp(value, "matrix") == 0)
config->scaler = 2;
}
else if(strcmp(name, "colorProfile") == 0)
{
if(strcmp(value, "none") == 0)
config->colorProfile = 0;
else if(strcmp(value, "gba") == 0)
config->colorProfile = 1;
else if(strcmp(value, "nds") == 0)
else if(strcmp(value, "gb_micro") == 0)
config->colorProfile = 2;
else if(strcmp(value, "nds_white") == 0)
else if(strcmp(value, "gba_sp101") == 0)
config->colorProfile = 3;
else if(strcmp(value, "nds") == 0)
config->colorProfile = 4;
else if(strcmp(value, "ds_lite") == 0)
config->colorProfile = 5;
else if(strcmp(value, "nso") == 0)
config->colorProfile = 6;
else if(strcmp(value, "vba") == 0)
config->colorProfile = 7;
else if(strcmp(value, "identity") == 0)
config->colorProfile = 8;
//else if(strcmp(value, "custom") == 0) // TODO: Implement user provided profile.
// config->colorProfile = 4;
// config->colorProfile = 9;
}
else if(strcmp(name, "contrast") == 0)
config->contrast = str2float(value);
else if(strcmp(name, "brightness") == 0)
config->brightness = str2float(value);
else if(strcmp(name, "saturation") == 0)
config->saturation = str2float(value);
}
else if(strcmp(section, "audio") == 0)
{
if(strcmp(name, "audioOut") == 0)
config->audioOut = (u8)strtoul(value, NULL, 10);
{
if(strcmp(value, "auto") == 0)
config->audioOut = 0;
else if(strcmp(value, "speakers") == 0)
config->audioOut = 1;
else if(strcmp(value, "headphones") == 0)
config->audioOut = 2;
}
else if(strcmp(name, "volume") == 0)
config->volume = (s8)strtol(value, NULL, 10);
}
@ -193,14 +220,82 @@ static int cfgIniCallback(void* user, const char* section, const char* name, con
if(strcmp(name, "saveSlot") == 0)
config->saveSlot = (u8)strtoul(value, NULL, 10);
if(strcmp(name, "saveType") == 0)
config->saveType = (u8)strtoul(value, NULL, 10);
{
if(strcmp(value, "eeprom_8k") == 0)
config->saveType = 0;
if(strcmp(value, "rom_256m_eeprom_8k") == 0)
config->saveType = 1;
if(strcmp(value, "eeprom_64k") == 0)
config->saveType = 2;
if(strcmp(value, "rom_256m_eeprom_64k") == 0)
config->saveType = 3;
if(strcmp(value, "flash_512k_atmel_rtc") == 0)
config->saveType = 4;
if(strcmp(value, "flash_512k_atmel") == 0)
config->saveType = 5;
if(strcmp(value, "flash_512k_sst_rtc") == 0)
config->saveType = 6;
if(strcmp(value, "flash_512k_sst") == 0)
config->saveType = 7;
if(strcmp(value, "flash_512k_panasonic_rtc") == 0)
config->saveType = 8;
if(strcmp(value, "flash_512k_panasonic") == 0)
config->saveType = 9;
if(strcmp(value, "flash_1m_macronix_rtc") == 0)
config->saveType = 10;
if(strcmp(value, "flash_1m_macronix") == 0)
config->saveType = 11;
if(strcmp(value, "flash_1m_sanyo_rtc") == 0)
config->saveType = 12;
if(strcmp(value, "flash_1m_sanyo") == 0)
config->saveType = 13;
if(strcmp(value, "sram_256k") == 0)
config->saveType = 14;
if(strcmp(value, "none") == 0)
config->saveType = 15;
if(strcmp(value, "auto") == 0)
config->saveType = 255;
}
}
else if(strcmp(section, "advanced") == 0)
{
if(strcmp(name, "saveOverride") == 0)
config->saveOverride = (strcmp(value, "false") == 0 ? false : true);
if(strcmp(name, "defaultSave") == 0)
config->defaultSave = (u16)strtoul(value, NULL, 10);
{
if(strcmp(value, "eeprom_8k") == 0)
config->defaultSave = 0;
if(strcmp(value, "rom_256m_eeprom_8k") == 0)
config->defaultSave = 1;
if(strcmp(value, "eeprom_64k") == 0)
config->defaultSave = 2;
if(strcmp(value, "rom_256m_eeprom_64k") == 0)
config->defaultSave = 3;
if(strcmp(value, "flash_512k_atmel_rtc") == 0)
config->defaultSave = 4;
if(strcmp(value, "flash_512k_atmel") == 0)
config->defaultSave = 5;
if(strcmp(value, "flash_512k_sst_rtc") == 0)
config->defaultSave = 6;
if(strcmp(value, "flash_512k_sst") == 0)
config->defaultSave = 7;
if(strcmp(value, "flash_512k_panasonic_rtc") == 0)
config->defaultSave = 8;
if(strcmp(value, "flash_512k_panasonic") == 0)
config->defaultSave = 9;
if(strcmp(value, "flash_1m_macronix_rtc") == 0)
config->defaultSave = 10;
if(strcmp(value, "flash_1m_macronix") == 0)
config->defaultSave = 11;
if(strcmp(value, "flash_1m_sanyo_rtc") == 0)
config->defaultSave = 12;
if(strcmp(value, "flash_1m_sanyo") == 0)
config->defaultSave = 13;
if(strcmp(value, "sram_256k") == 0)
config->defaultSave = 14;
if(strcmp(value, "none") == 0)
config->defaultSave = 15;
}
}
else return 0; // Error.

View File

@ -0,0 +1,60 @@
@ 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 <http://www.gnu.org/licenses/>.
#include "asm_macros.h"
.syntax unified
.cpu mpcore
.fpu vfpv2
@ void makeOpenBusPaddingFast(u32 *romEnd);
BEGIN_ASM_FUNC makeOpenBusPaddingFast
@ Save registers and calculate size from start and highest ROM address.
stmfd sp!, {r4, lr} @ Save registers.
rsb r1, r0, #0x22000000 @ r1 = 0x22000000 - r0;
@ Generate pattern halves from address.
lsr r2, r0, #1 @ r2 = r0>>1;
add r3, r2, #1 @ r3 = r2 + 1;
@ Generate constant for incrementing the pattern halves.
mov r12, #2 @ r12 = 2;
add r12, r12, #0x20000 @ r12 += 0x20000;
@ Join pattern halves and precalculate the next 3 patterns.
pkhbt r2, r2, r3, lsl #16 @ r2 = (r2 & 0xFFFF) | r3<<16;
uadd16 r3, r2, r12 @ r3 = ((r2 + 0x20000) & 0xFFFF0000) | ((r2 + 2) & 0xFFFF); // r12 is 0x20002.
uadd16 r4, r3, r12 @ r4 = ((r3 + 0x20000) & 0xFFFF0000) | ((r3 + 2) & 0xFFFF); // r12 is 0x20002.
uadd16 lr, r4, r12 @ lr = ((r4 + 0x20000) & 0xFFFF0000) | ((r4 + 2) & 0xFFFF); // r12 is 0x20002.
@ Adjust constant for unrolled loop. 0x20002 --> 0x80008.
lsl r12, r12, #2 @ r12 <<= 2;
makeOpenBusPaddingFast_blk_lp:
@ Store 16 pattern bytes at a time and decrement size.
stmia r0!, {r2-r4, lr} @ *((_16BytesBlock*)r0) = r2_to_r4_lr; r0 += 16;
subs r1, r1, #16 @ r1 -= 16; // Updates flags.
@ Increment patterns and jump back if we are not done yet.
uadd16 r2, r2, r12 @ r2 = ((r2 + 0x80000) & 0xFFFF0000) | ((r2 + 8) & 0xFFFF); // r12 is 0x80008.
uadd16 r3, r3, r12 @ r3 = ((r3 + 0x80000) & 0xFFFF0000) | ((r3 + 8) & 0xFFFF); // r12 is 0x80008.
uadd16 r4, r4, r12 @ r3 = ((r4 + 0x80000) & 0xFFFF0000) | ((r4 + 8) & 0xFFFF); // r12 is 0x80008.
uadd16 lr, lr, r12 @ lr = ((lr + 0x80000) & 0xFFFF0000) | ((lr + 8) & 0xFFFF); // r12 is 0x80008.
bne makeOpenBusPaddingFast_blk_lp @ if(r1 != 0) goto makeOpenBusPaddingFast_blk_lp;
ldmfd sp!, {r4, pc} @ Restore registers and return.
END_ASM_FUNC

View File

@ -92,7 +92,8 @@ static Result scanDir(const char *const path, DirList *const dList, const char *
const u32 nameLen = strlen(fis[i].fname);
if(entType == ENT_TYPE_FILE)
{
if(nameLen <= filterLen || strcmp(filter, fis[i].fname + nameLen - filterLen) != 0)
if(nameLen <= filterLen || strcmp(filter, fis[i].fname + nameLen - filterLen) != 0
|| fis[i].fname[0] == '.')
continue;
}

View File

@ -43,13 +43,39 @@
static KHandle g_convFinishedEvent = 0;
static const u32 g_topLcdCurveCorrect[73] =
{
// Curve correction from 3DS top LCD gamma to 2.2 gamma for all channels.
// Unfortunately this doesn't fix color temperature but that varies wildly
// for all 2/3DS consoles on the market anyway.
0x01000000, 0x03020102, 0x00060406, 0x01060507,
0x03080608, 0x020B090C, 0x010E0B0F, 0x010F0D10,
0x01110E12, 0x01121014, 0x00141116, 0x00151216,
0x01151317, 0x01171419, 0x0118161B, 0x011A171C,
0x021B191E, 0x001E1B21, 0x011E1C22, 0x01201E23,
0x03211F25, 0x01242329, 0x0226242B, 0x0028272E,
0x0229282E, 0x002B2B31, 0x042C2B32, 0x04303037,
0x0034353C, 0x0535353D, 0x073A3B43, 0x0A41434B,
0x034B4E56, 0x084F525B, 0x0D575B64, 0x03656973,
0x12686D77, 0x017B818A, 0x167C838C, 0x03939AA4,
0x0E979FA8, 0x01A6AFB7, 0x06A9B1B9, 0x01B0B9C0,
0x00B2BBC3, 0x04B4BCC4, 0x00B9C2C9, 0x04BBC3CA,
0x00C1C8CF, 0x00C2CAD0, 0x02C3CBD2, 0x01C7CED5,
0x00C9D1D7, 0x03CBD2D8, 0x00D0D7DC, 0x01D1D8DE,
0x01D4DAE0, 0x00D6DDE2, 0x02D8DEE3, 0x00DCE1E6,
0x01DDE3E7, 0x02E0E5EA, 0x01E4E9ED, 0x02E7EBEF,
0x01EBEFF2, 0x01EEF1F4, 0x00F1F3F6, 0x01F2F5F7,
0x01F5F7F9, 0x01F8F9FB, 0x00FAFCFD, 0x01FCFDFD,
0x00FFFFFF
};
// TODO: Reimplement contrast and brightness in color lut below.
static void adjustGammaTableForGba(void)
{
// Credits for this algo go to Extrems.
const float targetGamma = g_oafConfig.gbaGamma;
/*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;
@ -65,7 +91,29 @@ static void adjustGammaTableForGba(void)
// Same adjustment for red/green/blue.
*color_lut_data = res<<16 | res<<8 | res;
}
}*/
// Very simple gamma table expansion code.
// Code + hardcoded tables are way smaller than hardcoding the uncompressed tables.
const u32 *encTable = g_topLcdCurveCorrect;
vu32 *const color_lut_data = &getGxRegs()->pdc0.color_lut_data;
u32 decoded = 0;
do
{
// Get table entry and extract the number of linearly increasing entries.
u32 entry = *encTable++;
u32 steps = (entry>>24) + 1;
// Keep track of how many table entries we generated.
decoded += steps;
do
{
// Set gamma table entry and increment.
// Note: Bits 24-31 don't matter so we don't need to mask.
*color_lut_data = entry;
entry += 0x010101;
} while(--steps != 0);
} while(decoded < 256);
}
typedef struct
@ -78,31 +126,73 @@ typedef struct
float displayGamma;
} ColorProfile;
static const ColorProfile g_colorProfiles[3] =
// libretro shader values. Credits: hunterk and Pokefan531.
// Last updated 2014-12-03.
static const ColorProfile g_colorProfiles[8] =
{
{ // libretro GBA color (sRGB). Credits: hunterk and Pokefan531.
2.f + 0.5f,
0.93f,
0.8f, 0.275f, -0.075f,
0.135f, 0.64f, 0.225f,
0.195f, 0.155f, 0.65f,
1.f / 2.f
{ // libretro GBA color (sRGB).
2.2f + (0.3f * 1.6f), // Darken screen. Default 0. Modified to 0.3.
0.91f,
0.905f, 0.195f, -0.1f,
0.1f, 0.65f, 0.25f,
0.1575f, 0.1425f, 0.7f,
1.f / 2.2f
},
{ // libretro DS phat (sRGB). Credits: hunterk and Pokefan531.
2.f,
{ // libretro GB micro color (sRGB).
2.2f,
0.9f,
0.8025f, 0.31f, -0.1125f,
0.1f, 0.6875f, 0.2125f,
0.1225f, 0.1125f, 0.765f,
1.f / 2.2f
},
{ // libretro GBA SP (AGS-101) color (sRGB).
2.2f,
0.935f,
0.96f, 0.11f, -0.07f,
0.0325f, 0.89f, 0.0775f,
0.001f, -0.03f, 1.029f,
1.f / 2.2f
},
{ // libretro NDS color (sRGB).
2.2f,
0.905f,
0.835f, 0.27f, -0.105f,
0.1f, 0.6375f, 0.2625f,
0.105f, 0.175f, 0.72f,
1.f / 2.2f
},
{ // libretro NDS lite color (sRGB).
2.2f,
0.935f,
0.93f, 0.14f, -0.07f,
0.025f, 0.9f, 0.075f,
0.008f, -0.03f, 1.022f,
1.f / 2.2f
},
{ // libretro Nintendo Switch Online color (sRGB).
2.2f + 0.8f, // Darken screen. Default 0.8.
1.f,
0.705f, 0.235f, -0.075f,
0.09f, 0.585f, 0.24f,
0.1075f, 0.1725f, 0.72f,
1.f / 2.f
0.865f, 0.1225f, 0.0125f,
0.0575f, 0.925f, 0.0125f,
0.0575f, 0.1225f, 0.82f,
1.f / 2.2f
},
{ // libretro DS phat white (sRGB). Credits: hunterk and Pokefan531.
2.f,
0.915f,
0.815f, 0.275f, -0.09f,
0.1f, 0.64f, 0.26f,
0.1075f, 0.1725f, 0.72f,
1.f / 2.f
{ // libretro Visual Boy Advance/No$GBA full color.
1.45f + 1.f, // Darken screen. Default 1.
1.f,
0.73f, 0.27f, 0.f,
0.0825f, 0.6775f, 0.24f,
0.0825f, 0.24f, 0.6775f,
1.f / 1.45f
},
{ // Identity.
1.f,
1.f,
1.f, 0.f, 0.f,
0.f, 1.f, 0.f,
0.f, 0.f, 1.f,
1.f / 1.f
}
};
@ -111,9 +201,21 @@ ALWAYS_INLINE float clamp_float(const float x, const float min, const float max)
return (x < min ? min : (x > max ? max : x));
}
static void makeColorLut(const ColorProfile *const p)
void makeColorLut(const ColorProfile *const p)
{
u32 *colorLut = (u32*)COLOR_LUT_ADDR;
const float targetGamma = p->targetGamma;
const float contrast = g_oafConfig.contrast;
const float brightness = g_oafConfig.brightness / contrast;
const float targetContrast = powf(contrast, targetGamma);
// Calculate saturation weights.
// Note: We are using the Rec. 709 luminance vector here.
const float sat = g_oafConfig.saturation;
const float rwgt = (1.f - sat) * 0.2126f;
const float gwgt = (1.f - sat) * 0.7152f;
const float bwgt = (1.f - sat) * 0.0722f;
u32 *const colorLut = (u32*)COLOR_LUT_ADDR;
for(u32 i = 0; i < 32768; i++)
{
// Convert to 8-bit and normalize.
@ -122,10 +224,9 @@ static void makeColorLut(const ColorProfile *const p)
float r = (float)rgbFive2Eight(i>>10) / 255;
// Convert to linear gamma.
const float targetGamma = p->targetGamma;
b = powf(b, targetGamma);
g = powf(g, targetGamma);
r = powf(r, targetGamma);
b = powf(b + brightness, targetGamma);
g = powf(g + brightness, targetGamma);
r = powf(r + brightness, targetGamma);
// Apply luminance.
const float lum = p->lum;
@ -145,39 +246,47 @@ static void makeColorLut(const ColorProfile *const p)
* [rb][gb][ b] [b]
*/
// Assuming no alpha channel in original calculation.
float newB = p->rb * r + p->gb * g + p->b * b;
float newG = p->rg * r + p->g * g + p->bg * b;
float newR = p->r * r + p->gr * g + p->br * b;
float tmpB = p->rb * r + p->gb * g + p->b * b;
float tmpG = p->rg * r + p->g * g + p->bg * b;
float tmpR = p->r * r + p->gr * g + p->br * b;
newB = (newB < 0.f ? 0.f : newB);
newG = (newG < 0.f ? 0.f : newG);
newR = (newR < 0.f ? 0.f : newR);
// Apply saturation.
// Note: Some duplicated muls here. gcc optimizes them out.
b = rwgt * tmpR + gwgt * tmpG + (bwgt + sat) * tmpB;
g = rwgt * tmpR + (gwgt + sat) * tmpG + bwgt * tmpB;
r = (rwgt + sat) * tmpR + gwgt * tmpG + bwgt * tmpB;
b = (b < 0.f ? 0.f : b);
g = (g < 0.f ? 0.f : g);
r = (r < 0.f ? 0.f : r);
// Convert to display gamma.
const float displayGamma = p->displayGamma;
newB = powf(newB, displayGamma);
newG = powf(newG, displayGamma);
newR = powf(newR, displayGamma);
b = powf(targetContrast * b, displayGamma);
g = powf(targetContrast * g, displayGamma);
r = powf(targetContrast * r, displayGamma);
// Denormalize, clamp, convert to ABGR8 and write lut.
u32 tmp = 0xFF; // Alpha.
tmp |= clamp_s32(lroundf(newB * 255), 0, 255)<<8;
tmp |= clamp_s32(lroundf(newG * 255), 0, 255)<<16;
tmp |= clamp_s32(lroundf(newR * 255), 0, 255)<<24;
*colorLut++ = tmp;
u32 entry = 255; // Alpha.
entry |= clamp_s32(lroundf(b * 255), 0, 255)<<8;
entry |= clamp_s32(lroundf(g * 255), 0, 255)<<16;
entry |= clamp_s32(lroundf(r * 255), 0, 255)<<24;
colorLut[i] = entry;
}
flushDCacheRange((void*)COLOR_LUT_ADDR, 1024u * 128);
flushDCacheRange(colorLut, 4 * 32768);
}
static Result dumpFrameTex(void)
{
// Stop LgyCap before dumping the frame to prevent glitches.
LGYCAP_stop(LGYCAP_DEV_TOP);
// Capture a single frame in native resolution.
// Note: This adds 1 frame of delay after pressing the screenshot buttons.
if(LGYCAP_captureFrameUnscaled(LGYCAP_DEV_TOP) != KRES_OK)
return RES_INVALID_ARG;
// A1BGR5 format (alpha ignored).
constexpr u32 alignment = 0x80; // Make PPF happy.
alignas(4) static BmpV1WithMasks bmpHeaders =
alignas(4) static const BmpV1WithMasks bmpHeaders =
{
{
.magic = 0x4D42,
@ -204,25 +313,12 @@ static Result dumpFrameTex(void)
.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*)GPU_TEXTURE_ADDR, PPF_DIM(512, 240), tmpBuf + (alignment / 4), outDim,
GX_displayTransfer((u32*)GPU_TEXTURE_ADDR, PPF_DIM(512, 160), tmpBuf + (alignment / 4), PPF_DIM(240, 160),
PPF_O_FMT(GX_A1BGR5) | PPF_I_FMT(GX_A1BGR5) | PPF_CROP_EN);
memcpy(tmpBuf, &bmpHeaders, sizeof(bmpHeaders));
GFX_waitForPPF();
@ -234,8 +330,13 @@ static Result dumpFrameTex(void)
// 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);
td.year + 0x2000, td.mon, td.day, td.hour, td.min, td.sec);
const Result res = fsQuickWrite(fn, tmpBuf, bmpHeaders.header.fileSize);
// Clear overwritten texture area in case we overwrote padding (different resolution).
// This is important because padding pixels must be fully transparent to get sharp edges when the GPU renders.
GX_memoryFill((u32*)GPU_TEXTURE_ADDR, PSC_FILL_32_BITS, 512 * 160 * 2, 0, NULL, 0, 0, 0);
GFX_waitForPSC0();
// Restart LgyCap.
LGYCAP_start(LGYCAP_DEV_TOP);

View File

@ -20,7 +20,7 @@
#include <string.h>
#include "types.h"
#include "util.h"
#include "arm_intrinsic.h"
#include "arm11/fast_rom_padding.h"
#include "oaf_error_codes.h"
#include "fs.h"
#include "arm11/fmt.h"
@ -49,9 +49,9 @@ static u32 fixRomPadding(const u32 romFileSize)
// Pad unused ROM area with 0xFFs (trimmed ROMs).
// Smallest retail ROM chip is 8 Mbit (1 MiB).
u32 romSize = nextPow2(romFileSize);
if(romSize < 0x100000) romSize = 0x100000;
romSize = (romSize < 0x100000 ? 0x100000 : romSize);
const uintptr_t romLoc = LGY_ROM_LOC;
memset((void*)(romLoc + romFileSize), 0xFFFFFFFF, romSize - romFileSize);
memset((void*)(romLoc + romFileSize), 0xFF, romSize - romFileSize);
u32 mirroredSize = romSize;
if(romSize == 0x100000) // 1 MiB.
@ -69,13 +69,8 @@ static u32 fixRomPadding(const u32 romFileSize)
}
// Fake "open bus" padding.
u32 padding = (romLoc + mirroredSize) / 2;
padding = __pkhbt(padding, padding + 1, 16); // Copy lower half + 1 to upper half.
for(uintptr_t i = romLoc + mirroredSize; i < romLoc + LGY_MAX_ROM_SIZE; i += 4)
{
*(u32*)i = padding;
padding = __uadd16(padding, 0x20002); // Unsigned parallel halfword-wise addition.
}
if(romSize < LGY_MAX_ROM_SIZE)
makeOpenBusPaddingFast((u32*)(romLoc + mirroredSize));
// We don't return the mirrored size because the db hashes are over unmirrored dumps.
return romSize;
@ -216,15 +211,23 @@ static Result showFileBrowser(char romAndSavePath[512])
static void rom2GameCfgPath(char romPath[512])
{
// Extract the file name and change the extension.
// For cfg2SavePath() we need to reserve 2 extra bytes/chars.
char tmpIniFileName[256];
safeStrcpy(tmpIniFileName, strrchr(romPath, '/') + 1, 256 - 2);
strcpy(tmpIniFileName + strlen(tmpIniFileName) - 4, ".ini");
if (g_oafConfig.useSavesFolder)
{
// Extract the file name and change the extension.
// For cfg2SavePath() we need to reserve 2 extra bytes/chars.
char tmpIniFileName[256];
safeStrcpy(tmpIniFileName, strrchr(romPath, '/') + 1, 256 - 2);
strcpy(tmpIniFileName + strlen(tmpIniFileName) - 4, ".ini");
// Construct the new path.
strcpy(romPath, OAF_SAVE_DIR "/");
strcat(romPath, tmpIniFileName);
// Construct the new path.
strcpy(romPath, OAF_SAVE_DIR "/");
strcat(romPath, tmpIniFileName);
}
else
{
// Change the extension to .ini.
strcpy(romPath + strlen(romPath) - 4, ".ini");
}
}
static void gameCfg2SavePath(char cfgPath[512], const u8 saveSlot)