Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 170 additions & 21 deletions src/platform/libretro/libretro.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ static unsigned targetSampleRate = GBA_RESAMPLED_RATE;
#define VIDEO_WIDTH_MAX 256
#define VIDEO_HEIGHT_MAX 224
#define VIDEO_BUFF_SIZE (VIDEO_WIDTH_MAX * VIDEO_HEIGHT_MAX * sizeof(mColor))
#define LIBRETRO_MAX_PORTS 8

static retro_environment_t environCallback;
static retro_video_refresh_t videoCallback;
Expand All @@ -75,6 +76,9 @@ static void _audioRateChanged(struct mAVStream*, unsigned rate);
static void _setRumble(struct mRumbleIntegrator*, float level);
static uint8_t _readLux(struct GBALuminanceSource* lux);
static void _updateLux(struct GBALuminanceSource* lux);
static int _boktai1StepToBar(int step);
static int _boktai1BarToStep(int bar);
static int _analogStickSolarLevel(unsigned port);
static void _updateCamera(const uint32_t* buffer, unsigned width, unsigned height, size_t pitch);
static void _startImage(struct mImageSource*, unsigned w, unsigned h, int colorFormats);
static void _stopImage(struct mImageSource*);
Expand Down Expand Up @@ -106,6 +110,8 @@ static int luxLevelIndex;
static uint8_t luxLevel;
static bool luxSensorEnabled;
static bool luxSensorUsed;
static bool luxAnalogMode;
static bool boktai1Game;
static struct mLogger logger;
static struct retro_camera_callback cam;
static struct mImageSource imageSource;
Expand Down Expand Up @@ -1436,6 +1442,7 @@ void retro_init(void) {
envVarsUpdated = true;
luxSensorUsed = false;
luxSensorEnabled = false;
boktai1Game = false;
luxLevelIndex = 0;
luxLevel = 0;
lux.readLuminance = _readLux;
Expand Down Expand Up @@ -1607,23 +1614,60 @@ void retro_run(void) {
core->setKeys(core, keys);

if (!luxSensorUsed) {
static bool wasAdjustingLux = false;
if (wasAdjustingLux) {
wasAdjustingLux = inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3) ||
inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3);
} else {
if (inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3)) {
++luxLevelIndex;
if (luxLevelIndex > 10) {
luxLevelIndex = 10;
if (luxAnalogMode) {
int level = -1;
unsigned port;
for (port = 0; port < LIBRETRO_MAX_PORTS; ++port) {
level = _analogStickSolarLevel(port);
if (level >= 0) {
break;
}
}
if (level >= 0) {
if (boktai1Game) {
if (level > 8) {
level = 8;
}
luxLevelIndex = _boktai1BarToStep(level);
} else {
luxLevelIndex = level;
}
wasAdjustingLux = true;
} else if (inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3)) {
--luxLevelIndex;
if (luxLevelIndex < 0) {
luxLevelIndex = 0;
}
} else {
static bool wasAdjustingLux = false;
if (wasAdjustingLux) {
wasAdjustingLux = inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3) ||
inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3);
} else {
if (inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3)) {
if (boktai1Game) {
int bar = _boktai1StepToBar(luxLevelIndex);
if (bar < 8) {
++bar;
}
luxLevelIndex = _boktai1BarToStep(bar);
} else {
++luxLevelIndex;
if (luxLevelIndex > 10) {
luxLevelIndex = 10;
}
}
wasAdjustingLux = true;
} else if (inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3)) {
if (boktai1Game) {
int bar = _boktai1StepToBar(luxLevelIndex);
if (bar > 0) {
--bar;
}
luxLevelIndex = _boktai1BarToStep(bar);
} else {
--luxLevelIndex;
if (luxLevelIndex < 0) {
luxLevelIndex = 0;
}
}
wasAdjustingLux = true;
}
wasAdjustingLux = true;
}
}
}
Expand Down Expand Up @@ -2088,6 +2132,22 @@ bool retro_load_game(const struct retro_game_info* game) {
core->loadROM(core, rom);
deferredSetup = true;

/* Boktai 1 has an 8-bar solar gauge. Detect it once per loaded game so
* manual adjustments (core option level + L3/R3) operate in 0-8 bars. */
boktai1Game = false;
#ifdef M_CORE_GBA
if (core->platform(core) == mPLATFORM_GBA) {
struct mGameInfo info;
core->getGameInfo(core, &info);
boktai1Game = strcmp(info.code, "U3IJ") == 0 ||
strcmp(info.code, "U3IE") == 0 ||
strcmp(info.code, "U3IP") == 0;
}
#endif
/* Re-apply solar settings with the correct mapping for this title. */
envVarsUpdated = true;
_updateLux(&lux);

const char* sysDir = 0;
const char* biosName = 0;
char biosPath[PATH_MAX];
Expand Down Expand Up @@ -2154,6 +2214,7 @@ void retro_unload_game(void) {
if (!core) {
return;
}
boktai1Game = false;
mCoreConfigDeinit(&core->config);
core->deinit(core);
mappedMemoryFree(data, dataSize);
Expand Down Expand Up @@ -2466,6 +2527,81 @@ static void _setRumble(struct mRumbleIntegrator* rumble, float level) {
rumbleCallback(0, RETRO_RUMBLE_WEAK, level * 0xFFFF);
}

static int _boktai1StepToBar(int step) {
static const uint8_t map[11] = { 0, 1, 2, 3, 3, 4, 5, 6, 7, 7, 8 };
if (step < 0) {
step = 0;
} else if (step > 10) {
step = 10;
}
return map[step];
}

static int _boktai1BarToStep(int bar) {
static const uint8_t map[9] = { 0, 1, 2, 3, 5, 6, 7, 8, 10 };
if (bar < 0) {
bar = 0;
} else if (bar > 8) {
bar = 8;
}
return map[bar];
}

static int _analogStickSolarLevel(unsigned port) {
const int16_t dead = 0x4000;
int16_t lx = inputCallback(port, RETRO_DEVICE_ANALOG,
RETRO_DEVICE_INDEX_ANALOG_LEFT,
RETRO_DEVICE_ID_ANALOG_X);
int16_t ly = inputCallback(port, RETRO_DEVICE_ANALOG,
RETRO_DEVICE_INDEX_ANALOG_LEFT,
RETRO_DEVICE_ID_ANALOG_Y);
int16_t rx = inputCallback(port, RETRO_DEVICE_ANALOG,
RETRO_DEVICE_INDEX_ANALOG_RIGHT,
RETRO_DEVICE_ID_ANALOG_X);
int16_t ry = inputCallback(port, RETRO_DEVICE_ANALOG,
RETRO_DEVICE_INDEX_ANALOG_RIGHT,
RETRO_DEVICE_ID_ANALOG_Y);

int group = -1;
if (ly < -dead) {
group = 0; /* 0-3 */
} else if (ly > dead) {
group = 1; /* 4-7 */
} else if (lx < -dead) {
group = 2; /* 8-10 & 0 */
}

int select = -1;
if (ry < -dead) {
select = 0; /* 0/4/8 */
} else if (ry > dead) {
select = 1; /* 1/5/9 */
} else if (rx < -dead) {
select = 2; /* 2/6/10 */
} else if (rx > dead) {
select = 3; /* 3/7/0 */
}

if (group < 0 || select < 0) {
return -1;
}

if (group == 0) {
return select;
} else if (group == 1) {
return 4 + select;
}

if (select == 0) {
return 8;
} else if (select == 1) {
return 9;
} else if (select == 2) {
return 10;
}
return 0;
}

static void _updateLux(struct GBALuminanceSource* lux) {
UNUSED(lux);
struct retro_variable var = {
Expand All @@ -2480,24 +2616,37 @@ static void _updateLux(struct GBALuminanceSource* lux) {

if (luxVarUpdated) {
luxSensorUsed = strcmp(var.value, "sensor") == 0;
luxAnalogMode = strcmp(var.value, "analog") == 0;
}

if (luxSensorUsed) {
_initSensors();
float fLux = luxSensorEnabled ? sensorGetCallback(0, RETRO_SENSOR_ILLUMINANCE) : 0.0f;
luxLevel = cbrtf(fLux) * 8;
} else {
if (luxVarUpdated) {
if (luxVarUpdated && !luxAnalogMode) {
char* end;
int newLuxLevelIndex = strtol(var.value, &end, 10);

if (!*end) {
if (newLuxLevelIndex > 10) {
luxLevelIndex = 10;
} else if (newLuxLevelIndex < 0) {
luxLevelIndex = 0;
if (boktai1Game) {
/* Boktai 1 shows an 8-bar gauge (0-8). Clamp any user-provided
* values outside that range, then map into the internal 10-step
* lux level indices. */
if (newLuxLevelIndex > 8) {
newLuxLevelIndex = 8;
} else if (newLuxLevelIndex < 0) {
newLuxLevelIndex = 0;
}
luxLevelIndex = _boktai1BarToStep(newLuxLevelIndex);
} else {
luxLevelIndex = newLuxLevelIndex;
if (newLuxLevelIndex > 10) {
luxLevelIndex = 10;
} else if (newLuxLevelIndex < 0) {
luxLevelIndex = 0;
} else {
luxLevelIndex = newLuxLevelIndex;
}
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/platform/libretro/libretro_core_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,11 +271,12 @@ struct retro_core_option_v2_definition option_defs_us[] = {
"mgba_solar_sensor_level",
"Solar Sensor Level",
NULL,
"Sets ambient sunlight intensity. Can be used by games that included a solar sensor in their cartridges, e.g: the Boktai series.",
"Sets sunlight intensity for games that include a solar sensor in their cartridge (e.g. Boktai). 'Analog Stick Control': left stick selects group (Up=0-3, Down=4-7, Left=8-10/0); right stick selects value (Up=0/4/8, Down=1/5/9, Left=2/6/10, Right=3/7/0). ASC disables Analog to Digital until restart.",
NULL,
"input",
{
{ "sensor", "Use device sensor if available" },
{ "sensor", "Use Device Sensor If Available" },
{ "analog", "Analog Stick Control" },
{ "0", NULL },
{ "1", NULL },
{ "2", NULL },
Expand Down