mirror of
https://github.com/wavemotion-dave/NINTV-DS.git
synced 2025-06-18 13:55:33 -04:00
704 lines
38 KiB
C++
704 lines
38 KiB
C++
// =====================================================================================
|
|
// Copyright (c) 2021-2025 Dave Bernazzani (wavemotion-dave)
|
|
//
|
|
// Copying and distribution of this emulator, its source code and associated
|
|
// readme files, with or without modification, are permitted in any medium without
|
|
// royalty provided the this copyright notice is used and wavemotion-dave (NINTV-DS)
|
|
// and Kyle Davis (BLISS) are thanked profusely.
|
|
//
|
|
// The NINTV-DS emulator is offered as-is, without any warranty.
|
|
// =====================================================================================
|
|
|
|
#include <nds.h>
|
|
#include <stdio.h>
|
|
#include <fat.h>
|
|
#include <dirent.h>
|
|
#include <unistd.h>
|
|
|
|
#include "nintv-ds.h"
|
|
#include "savestate.h"
|
|
#include "CRC32.h"
|
|
#include "bgBottom.h"
|
|
#include "config.h"
|
|
#include "bgMenu-Green.h"
|
|
#include "bgMenu-White.h"
|
|
#include "Emulator.h"
|
|
#include "Rip.h"
|
|
#include "overlay.h"
|
|
#include "loadgame.h"
|
|
#include "printf.h"
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Configuration structures - we keep configuration at two levels:
|
|
// Global - things that apply to all games (or defaults for new games)
|
|
// Game - things that apply only to one game - indexed by game CRC32
|
|
// We use a master config struct to hold one global config and up to 625
|
|
// individual game configs... and we save them out with version number
|
|
// and CRC to the SD card. The version number is critical because we can
|
|
// change that to force a reset to defaults if we change our master
|
|
// configration defaults in code...
|
|
// -------------------------------------------------------------------------
|
|
struct Config_t myConfig __attribute__((section(".dtcm")));
|
|
struct GlobalConfig_t myGlobalConfig;
|
|
struct AllConfig_t allConfigs;
|
|
|
|
extern AudioMixer *audioMixer;
|
|
extern Rip *currentRip;
|
|
|
|
// 0 = Global Options
|
|
// 1,2 = Game Options
|
|
UINT8 options_shown = 0; // Start with Global config... can toggle to game options as needed
|
|
|
|
UINT32 last_crc = 0x00000000;
|
|
|
|
UINT8 bConfigWasFound = FALSE;
|
|
|
|
short int display_options_list(bool);
|
|
|
|
// --------------------------------------------
|
|
// Set the global configuration defaults...
|
|
// --------------------------------------------
|
|
static void SetDefaultGlobalConfig(void)
|
|
{
|
|
memset(myGlobalConfig.last_path, 0x00, 256);
|
|
memset(myGlobalConfig.last_game, 0x00, 256);
|
|
memset(myGlobalConfig.exec_bios_filename, 0x00, 256);
|
|
memset(myGlobalConfig.grom_bios_filename, 0x00, 256);
|
|
memset(myGlobalConfig.ivoice_bios_filename, 0x00, 256);
|
|
|
|
memset(myGlobalConfig.favorites, 0x00, 256);
|
|
|
|
for (int i=0; i<64; i++)
|
|
{
|
|
myGlobalConfig.reserved32[i] = 0;
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
// Look for some common directories and set these as global defaults
|
|
// --------------------------------------------------------------------
|
|
DIR* bdir = opendir("/roms/bios"); // See if directory exists
|
|
if (bdir)
|
|
{
|
|
closedir(bdir);
|
|
myGlobalConfig.bios_dir = 1; // in /roms/bios
|
|
}
|
|
else
|
|
{
|
|
DIR* bdir = opendir("/data/bios"); // See if directory exists
|
|
if (bdir)
|
|
{
|
|
closedir(bdir);
|
|
myGlobalConfig.bios_dir = 3; // in /data/bios
|
|
}
|
|
else
|
|
{
|
|
myGlobalConfig.bios_dir = 0; // Same as ROMS
|
|
}
|
|
}
|
|
|
|
DIR* dir = opendir("/roms/intv"); // See if directory exists
|
|
if (dir)
|
|
{
|
|
closedir(dir);
|
|
myGlobalConfig.save_dir = 2;
|
|
myGlobalConfig.rom_dir = 2;
|
|
myGlobalConfig.man_dir = 2;
|
|
myGlobalConfig.ovl_dir = 2;
|
|
}
|
|
else //Same as ROMS
|
|
{
|
|
DIR* dir = opendir("/roms"); // See if directory exists
|
|
if (dir)
|
|
{
|
|
myGlobalConfig.save_dir = 1;
|
|
myGlobalConfig.rom_dir = 1;
|
|
myGlobalConfig.man_dir = 1;
|
|
myGlobalConfig.ovl_dir = 1;
|
|
}
|
|
else // Set 'Same as ROMs' which is a sensible fall-back default
|
|
{
|
|
myGlobalConfig.save_dir = 0;
|
|
myGlobalConfig.rom_dir = 0;
|
|
myGlobalConfig.man_dir = 0;
|
|
myGlobalConfig.ovl_dir = 0;
|
|
}
|
|
}
|
|
|
|
myGlobalConfig.show_fps = 0;
|
|
myGlobalConfig.erase_saves = 0;
|
|
myGlobalConfig.key_START_map_default = OVL_META_DISC;
|
|
myGlobalConfig.key_SELECT_map_default = OVL_META_MENU;
|
|
myGlobalConfig.def_sound_quality = (isDSiMode() ? 2:0);
|
|
myGlobalConfig.def_palette = 0;
|
|
myGlobalConfig.brightness = 0;
|
|
myGlobalConfig.menu_color = 1;
|
|
myGlobalConfig.spare1 = 0;
|
|
myGlobalConfig.spare2 = 0;
|
|
myGlobalConfig.spare3 = 0;
|
|
myGlobalConfig.spare4 = 0;
|
|
myGlobalConfig.spare5 = 0;
|
|
myGlobalConfig.frame_skip = (isDSiMode() ? 0:1);
|
|
memset(myGlobalConfig.reserved, 0x00, 256);
|
|
}
|
|
|
|
// --------------------------------------------
|
|
// Set one game-specific default. We track
|
|
// this in the myConfig global data struct.
|
|
// --------------------------------------------
|
|
static void SetDefaultGameConfig(UINT32 crc)
|
|
{
|
|
myConfig.game_crc = 0x00000000;
|
|
myConfig.frame_skip = myGlobalConfig.frame_skip;
|
|
myConfig.spare1 = 0;
|
|
myConfig.key_A_map = OVL_BTN_FIRE;
|
|
myConfig.key_B_map = OVL_BTN_FIRE;
|
|
myConfig.key_X_map = OVL_BTN_R_ACT;
|
|
myConfig.key_Y_map = OVL_BTN_L_ACT;
|
|
myConfig.key_L_map = OVL_KEY_1;
|
|
myConfig.key_R_map = OVL_KEY_2;
|
|
myConfig.key_START_map = myGlobalConfig.key_START_map_default;
|
|
myConfig.key_SELECT_map = myGlobalConfig.key_SELECT_map_default;
|
|
myConfig.key_AX_map = OVL_BTN_FIRE;
|
|
myConfig.key_XY_map = OVL_BTN_FIRE;
|
|
myConfig.key_YB_map = OVL_BTN_FIRE;
|
|
myConfig.key_BA_map = OVL_BTN_FIRE;
|
|
myConfig.controller_type = CONTROLLER_P1;
|
|
myConfig.sound_quality = myGlobalConfig.def_sound_quality;
|
|
myConfig.dpad_config = DPAD_NORMAL;
|
|
myConfig.target_fps = 0;
|
|
myConfig.load_options = 0x00;
|
|
myConfig.palette = myGlobalConfig.def_palette;
|
|
myConfig.stretch_x = ((160 / 256) << 8) | (160 % 256);
|
|
myConfig.offset_x = 0;
|
|
myConfig.bLatched = 0;
|
|
myConfig.fudgeTiming = 0;
|
|
myConfig.key_click = 0;
|
|
myConfig.bSkipBlanks = 0;
|
|
myConfig.gramSize = GRAM_512B; // Normal 512 bytes
|
|
myConfig.spare6 = 0;
|
|
myConfig.spare7 = 0;
|
|
myConfig.spare8 = 0;
|
|
myConfig.spare9 = 0;
|
|
myConfig.spare10 = 0;
|
|
myConfig.spare11 = 0;
|
|
myConfig.spare12 = 0;
|
|
myConfig.spare13 = 0;
|
|
myConfig.spare14 = 0;
|
|
myConfig.spare15 = 0;
|
|
myConfig.spare16 = 0;
|
|
myConfig.spare17 = 1;
|
|
myConfig.spare18 = 1;
|
|
myConfig.spare19 = 1;
|
|
myConfig.spare20 = 0x0000;
|
|
|
|
// -----------------------------------------------------------------------------------------
|
|
// Now patch up some of the more common games that work best with non-default settings...
|
|
// -----------------------------------------------------------------------------------------
|
|
if (crc == 0x2DEACD15) myConfig.bLatched = true; // Stampede must have latched backtab access
|
|
if (crc == 0x573B9B6D) myConfig.bLatched = true; // Masters of the Universe must have latched backtab access
|
|
if (crc == 0x5F6E1AF6) myConfig.fudgeTiming = 2; // Motocross needs some fudge timing to run... known race condition...
|
|
if (crc == 0xfab2992c) myConfig.controller_type = CONTROLLER_DUAL_ACTION_B; // Astrosmash is best with Dual Action B
|
|
if (crc == 0xd0f83698) myConfig.controller_type = CONTROLLER_DUAL_ACTION_B; // Astrosmash (competition) is best with Dual Action B
|
|
if (crc == 0x2a1e0c1c) myConfig.controller_type = CONTROLLER_DUAL_ACTION_B; // Buzz Bombers is best with Dual Action B
|
|
if (crc == 0x2a1e0c1c) myConfig.controller_type = CONTROLLER_DUAL_ACTION_B; // Buzz Bombers is best with Dual Action B
|
|
if (crc == 0xc047d487) myConfig.controller_type = CONTROLLER_DUAL_ACTION_B; // Beauty and the Beast is best with Dual Action B
|
|
|
|
if (crc == 0x10D64E48) myConfig.controller_type = CONTROLLER_P2; // MLB Baseball and Super Pro Baseball use the P2 controller
|
|
if (crc == 0xDAB36628) myConfig.controller_type = CONTROLLER_P2; // MLB Baseball and Super Pro Baseball use the P2 controller
|
|
if (crc == 0x650fc1b4) myConfig.controller_type = CONTROLLER_P2; // MLB Baseball and Super Pro Baseball use the P2 controller
|
|
if (crc == 0x275F3512) myConfig.controller_type = CONTROLLER_P2; // Turbo uses the P2 controller
|
|
|
|
if (crc == 0x15E88FCE) myConfig.key_START_map = OVL_META_SWITCH; // Swords and Serpents wants to map the START button to swap controllers
|
|
if (crc == 0xB35C1101) myConfig.key_START_map = OVL_META_SWITCH; // Auto Racing wants to map the START button to swap controllers
|
|
if (crc == 0x515E1D7E) myConfig.key_START_map = OVL_META_SWITCH; // Body Slam - Super Pro Wrestling wants to map the START button to swap controllers
|
|
|
|
if (crc == 0xc047d487) myConfig.dpad_config = DPAD_STRICT_4WAY; // Beauty and the Beast is best with Strict 4-way
|
|
if (crc == 0xD1D352A0) myConfig.dpad_config = DPAD_STRICT_4WAY; // Tower of Doom is best with Strict 4-way
|
|
if (crc == 0xD8C9856A) myConfig.dpad_config = DPAD_DIAGONALS; // Q-Bert is best with diagonal
|
|
if (crc == 0x8AD19AB3) myConfig.bSkipBlanks = true; // B-17 Bomber needs to skip rendering blanks or the screen 'flashes'
|
|
|
|
if (crc == 0xc0c84a13) myConfig.gramSize = GRAM_2K; // Chippi-Plus is best with 2K Expanded GRAM
|
|
if (crc == 0xb8417889) myConfig.gramSize = GRAM_2K; // Studio Vision games are best with 2K Expanded GRAM
|
|
if (crc == 0x51bd4fed) myConfig.gramSize = GRAM_2K; // Studio Vision games are best with 2K Expanded GRAM
|
|
if (crc == 0x1deeee3e) myConfig.gramSize = GRAM_2K; // Studio Vision games are best with 2K Expanded GRAM
|
|
if (crc == 0x3acd33e4) myConfig.gramSize = GRAM_2K; // Studio Vision games are best with 2K Expanded GRAM
|
|
if (crc == 0x39eec5c7) myConfig.gramSize = GRAM_2K; // Studio Vision games are best with 2K Expanded GRAM
|
|
if (crc == 0x788b440d) myConfig.gramSize = GRAM_2K; // Studio Vision games are best with 2K Expanded GRAM
|
|
if (crc == 0x1a6b9100) myConfig.gramSize = GRAM_2K; // Studio Vision games are best with 2K Expanded GRAM
|
|
if (crc == 0x52bb127b) myConfig.gramSize = GRAM_2K; // Studio Vision games are best with 2K Expanded GRAM
|
|
if (crc == 0x2c0edc53) myConfig.gramSize = GRAM_2K; // Studio Vision games are best with 2K Expanded GRAM
|
|
if (crc == 0x490f1348) myConfig.gramSize = GRAM_2K; // Studio Vision games are best with 2K Expanded GRAM
|
|
if (crc == 0x3f490bd2) myConfig.gramSize = GRAM_2K; // Studio Vision games are best with 2K Expanded GRAM
|
|
if (crc == 0xccb7fe6d) myConfig.gramSize = GRAM_2K; // Studio Vision games are best with 2K Expanded GRAM
|
|
if (crc == 0xb91a1636) myConfig.gramSize = GRAM_2K; // Studio Vision games are best with 2K Expanded GRAM
|
|
|
|
if (crc == 0x59f5fa32) myConfig.load_options = LOAD_WITH_TUTORVISION; // Map Mazes is a Tutorvision game
|
|
if (crc == 0x714ecd51) myConfig.load_options = LOAD_WITH_TUTORVISION; // Shapes in Space is a Tutorvision game
|
|
if (crc == 0x63a87259) myConfig.load_options = LOAD_WITH_TUTORVISION |
|
|
LOAD_WITH_JLP; // Little Man Computer is a Tutorvision game with JLP
|
|
last_crc = crc;
|
|
}
|
|
|
|
|
|
static void ToggleXYABasDirections(void)
|
|
{
|
|
if (myConfig.key_A_map == OVL_KEY_6) // If we were AD&D map, go to TARMIN map
|
|
{
|
|
myConfig.key_A_map = OVL_KEY_7;
|
|
myConfig.key_B_map = OVL_KEY_4;
|
|
myConfig.key_X_map = OVL_KEY_6;
|
|
myConfig.key_Y_map = OVL_KEY_1;
|
|
myConfig.key_AX_map = OVL_BTN_FIRE;
|
|
myConfig.key_XY_map = OVL_BTN_FIRE;
|
|
myConfig.key_YB_map = OVL_BTN_FIRE;
|
|
myConfig.key_BA_map = OVL_BTN_FIRE;
|
|
myConfig.controller_type = CONTROLLER_P1;
|
|
myConfig.key_L_map = OVL_KEY_3;
|
|
myConfig.key_R_map = OVL_BTN_FIRE;
|
|
myConfig.key_START_map = OVL_KEY_5;
|
|
dsPrintValue(24,0,0, (char*)"TARMIN");
|
|
WAITVBL;WAITVBL;WAITVBL;WAITVBL;
|
|
dsPrintValue(24,0,0, (char*)" ");
|
|
}
|
|
else if (myConfig.key_A_map == OVL_BTN_FIRE) // If we were normal map, go to AD&D/TRON map
|
|
{
|
|
myConfig.key_A_map = OVL_KEY_6;
|
|
myConfig.key_B_map = OVL_KEY_8;
|
|
myConfig.key_X_map = OVL_KEY_2;
|
|
myConfig.key_Y_map = OVL_KEY_4;
|
|
myConfig.key_AX_map = OVL_KEY_3;
|
|
myConfig.key_XY_map = OVL_KEY_1;
|
|
myConfig.key_YB_map = OVL_KEY_7;
|
|
myConfig.key_BA_map = OVL_KEY_9;
|
|
myConfig.controller_type = CONTROLLER_DUAL_ACTION_A;
|
|
myConfig.key_L_map = OVL_BTN_FIRE;
|
|
myConfig.key_R_map = OVL_BTN_FIRE;
|
|
myConfig.key_START_map = myGlobalConfig.key_START_map_default;
|
|
dsPrintValue(23,0,0, (char*)"AD&D/TRON");
|
|
WAITVBL;WAITVBL;WAITVBL;WAITVBL;
|
|
dsPrintValue(23,0,0, (char*)" ");
|
|
}
|
|
else // Just cycle back to normal key map
|
|
{
|
|
myConfig.key_A_map = OVL_BTN_FIRE;
|
|
myConfig.key_B_map = OVL_BTN_FIRE;
|
|
myConfig.key_X_map = OVL_BTN_R_ACT;
|
|
myConfig.key_Y_map = OVL_BTN_L_ACT;
|
|
myConfig.key_AX_map = OVL_BTN_FIRE;
|
|
myConfig.key_XY_map = OVL_BTN_FIRE;
|
|
myConfig.key_YB_map = OVL_BTN_FIRE;
|
|
myConfig.key_BA_map = OVL_BTN_FIRE;
|
|
myConfig.controller_type = CONTROLLER_P1;
|
|
myConfig.key_L_map = OVL_KEY_1;
|
|
myConfig.key_R_map = OVL_KEY_2;
|
|
myConfig.key_START_map = myGlobalConfig.key_START_map_default;
|
|
dsPrintValue(24,0,0, (char*)"DEFAULT");
|
|
WAITVBL;WAITVBL;WAITVBL;WAITVBL;
|
|
dsPrintValue(24,0,0, (char*)" ");
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Write out the NINTV-DS.DAT configuration file to capture the settings for
|
|
// each game. This one file contains global settings + 300 game settings.
|
|
// ---------------------------------------------------------------------------
|
|
void SaveConfig(UINT32 crc, bool bShow)
|
|
{
|
|
FILE *fp;
|
|
int slot = 0;
|
|
|
|
if (bShow) dsPrintValue(0,23,0, (char*)" SAVING CONFIGURATION ");
|
|
|
|
// Set the global configuration version number...
|
|
allConfigs.config_ver = CONFIG_VER;
|
|
|
|
// Copy in the global configuration
|
|
memcpy(&allConfigs.global_config, &myGlobalConfig, sizeof(struct GlobalConfig_t));
|
|
|
|
// If there is a game loaded, save that into a slot... re-use the same slot if it exists
|
|
if (crc != 0x00000000)
|
|
{
|
|
myConfig.game_crc = crc;
|
|
|
|
// Find the slot we should save into...
|
|
for (slot=0; slot<MAX_CONFIGS; slot++)
|
|
{
|
|
if (allConfigs.game_config[slot].game_crc == myConfig.game_crc) // Got a match?!
|
|
{
|
|
break;
|
|
}
|
|
if (allConfigs.game_config[slot].game_crc == 0x00000000) // Didn't find it... use a blank slot...
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
memcpy(&allConfigs.game_config[slot], &myConfig, sizeof(struct Config_t));
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------------
|
|
// Compute the CRC32 of everything and we can check this as integrity in the future...
|
|
// -------------------------------------------------------------------------------------
|
|
allConfigs.crc32 = CRC32::getCrc((UINT8*)&allConfigs, sizeof(allConfigs) - sizeof(UINT32));
|
|
|
|
DIR* dir = opendir("/data");
|
|
if (dir)
|
|
{
|
|
closedir(dir); // Directory exists.
|
|
}
|
|
else
|
|
{
|
|
mkdir("/data", 0777); // Doesn't exist - make it...
|
|
}
|
|
fp = fopen("/data/NINTV-DS.DAT", "wb+");
|
|
if (fp != NULL)
|
|
{
|
|
fwrite(&allConfigs, sizeof(allConfigs), 1, fp);
|
|
fclose(fp);
|
|
} else dsPrintValue(2,20,0, (char*)" ERROR SAVING CONFIG FILE ");
|
|
|
|
if (bShow)
|
|
{
|
|
WAITVBL;WAITVBL;WAITVBL;WAITVBL;WAITVBL;
|
|
display_options_list(false);
|
|
}
|
|
}
|
|
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Find the NINTV-DS.DAT file and load it... if it doesn't exist, then
|
|
// default values will be used for the entire configuration database...
|
|
// -------------------------------------------------------------------------
|
|
void FindAndLoadConfig(UINT32 crc)
|
|
{
|
|
FILE *fp;
|
|
|
|
last_crc = crc;
|
|
bConfigWasFound = FALSE;
|
|
SetDefaultGameConfig(crc);
|
|
fp = fopen("/data/NINTV-DS.DAT", "rb");
|
|
if (fp != NULL)
|
|
{
|
|
fread(&allConfigs, sizeof(allConfigs), 1, fp);
|
|
fclose(fp);
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// If we were config version 0x0009, we perform a one-time upgrade to version 0x000B
|
|
// and replace the default START key map to the OVL_META_DISC to swap in the disc overlay.
|
|
// ----------------------------------------------------------------------------------------
|
|
if (allConfigs.config_ver == 0x0009) // One time upgrade
|
|
{
|
|
dsPrintValue(0,1,0, (char*)"PLEASE WAIT...");
|
|
allConfigs.global_config.key_START_map_default = OVL_META_DISC;
|
|
for (int slot=0; slot<MAX_CONFIGS; slot++)
|
|
{
|
|
if (allConfigs.game_config[slot].key_START_map == OVL_KEY_ENTER) allConfigs.game_config[slot].key_START_map = OVL_META_DISC;
|
|
allConfigs.game_config[slot].spare1 = 0;
|
|
}
|
|
allConfigs.config_ver = CONFIG_VER;
|
|
memcpy(&myGlobalConfig, &allConfigs.global_config, sizeof(struct GlobalConfig_t));
|
|
SaveConfig(0x00000000, FALSE);
|
|
dsPrintValue(0,1,0, (char*)" ");
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// If we were config version 0x000A, we perform a one-time upgrade to version 0x000B
|
|
// and patch up OVL_META_DISC which moved from meta index 27 to its new home...
|
|
// ----------------------------------------------------------------------------------------
|
|
if (allConfigs.config_ver == 0x000A) // One time upgrade
|
|
{
|
|
dsPrintValue(0,1,0, (char*)"PLEASE WAIT...");
|
|
allConfigs.global_config.key_START_map_default = OVL_META_DISC;
|
|
for (int slot=0; slot<MAX_CONFIGS; slot++)
|
|
{
|
|
if (allConfigs.game_config[slot].key_START_map == 27) allConfigs.game_config[slot].key_START_map = OVL_META_DISC;
|
|
allConfigs.game_config[slot].spare1 = 0;
|
|
}
|
|
allConfigs.config_ver = CONFIG_VER;
|
|
memcpy(&myGlobalConfig, &allConfigs.global_config, sizeof(struct GlobalConfig_t));
|
|
SaveConfig(0x00000000, FALSE);
|
|
dsPrintValue(0,1,0, (char*)" ");
|
|
}
|
|
|
|
if (allConfigs.config_ver != CONFIG_VER)
|
|
{
|
|
dsPrintValue(0,1,0, (char*)"PLEASE WAIT...");
|
|
memset(&allConfigs, 0x00, sizeof(allConfigs));
|
|
allConfigs.config_ver = CONFIG_VER;
|
|
SetDefaultGlobalConfig();
|
|
SetDefaultGameConfig(crc);
|
|
SaveConfig(0x00000000, FALSE);
|
|
dsPrintValue(0,1,0, (char*)" ");
|
|
}
|
|
else
|
|
{
|
|
// Now copy out the global config
|
|
memcpy(&myGlobalConfig, &allConfigs.global_config, sizeof(struct GlobalConfig_t));
|
|
}
|
|
|
|
if (crc != 0xFFFFFFFF)
|
|
{
|
|
for (int slot=0; slot<MAX_CONFIGS; slot++)
|
|
{
|
|
if (allConfigs.game_config[slot].game_crc == crc) // Got a match?!
|
|
{
|
|
bConfigWasFound = TRUE;
|
|
memcpy(&myConfig, &allConfigs.game_config[slot], sizeof(struct Config_t));
|
|
if (crc == 0x8AD19AB3) myConfig.bSkipBlanks = true; // B-17 Bomber needs to skip rendering blanks or the screen 'flashes'
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else // Not found... init the entire database...
|
|
{
|
|
dsPrintValue(0,1,0, (char*)"PLEASE WAIT...");
|
|
memset(&allConfigs, 0x00, sizeof(allConfigs));
|
|
allConfigs.config_ver = CONFIG_VER;
|
|
SetDefaultGlobalConfig();
|
|
SetDefaultGameConfig(crc);
|
|
SaveConfig(0x00000000, FALSE);
|
|
dsPrintValue(0,1,0, (char*)" ");
|
|
}
|
|
|
|
ApplyOptions();
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// Options are handled here... we have a number of things the user can tweak
|
|
// and these options are applied immediately. The user can also save off
|
|
// their option choices for the currently running game into the NINTV-DS.DAT
|
|
// configuration database. When games are loaded back up, NINTV-DS.DAT is read
|
|
// to see if we have a match and the user settings can be restored for the game.
|
|
// ------------------------------------------------------------------------------
|
|
struct options_t
|
|
{
|
|
const char *label;
|
|
const char *option[31];
|
|
UINT8 *option_val;
|
|
UINT8 option_max;
|
|
};
|
|
|
|
#define KEY_MAP_OPTIONS "KEY-1", "KEY-2", "KEY-3", "KEY-4", "KEY-5", "KEY-6", "KEY-7", "KEY-8", "KEY-9", "KEY-CLR", "KEY-0",\
|
|
"KEY-ENT", "FIRE", "L-ACT", "R-ACT", "RESET", "LOAD", "CONFIG", "SCORES", "QUIT", "STATE",\
|
|
"MENU", "SWITCH L/R", "MANUAL", "SHOW DISC", "SHOW KBD", "SWAP OVL", "DISC UP", "DISC DOWN", "SPEEDUP", "FASTLOAD SLOT 1"
|
|
|
|
const struct options_t Option_Table[3][20] =
|
|
{
|
|
// Page 1 options
|
|
{
|
|
{"A BUTTON", {KEY_MAP_OPTIONS}, &myConfig.key_A_map, 31},
|
|
{"B BUTTON", {KEY_MAP_OPTIONS}, &myConfig.key_B_map, 31},
|
|
{"X BUTTON", {KEY_MAP_OPTIONS}, &myConfig.key_X_map, 31},
|
|
{"Y BUTTON", {KEY_MAP_OPTIONS}, &myConfig.key_Y_map, 31},
|
|
{"L BUTTON", {KEY_MAP_OPTIONS}, &myConfig.key_L_map, 31},
|
|
{"R BUTTON", {KEY_MAP_OPTIONS}, &myConfig.key_R_map, 31},
|
|
{"START BTN", {KEY_MAP_OPTIONS}, &myConfig.key_START_map, 31},
|
|
{"SELECT BTN", {KEY_MAP_OPTIONS}, &myConfig.key_SELECT_map, 31},
|
|
{"A+X BUTTON", {KEY_MAP_OPTIONS}, &myConfig.key_AX_map, 26}, // These can't be mapped to DISC UP/DN or later meta keys
|
|
{"X+Y BUTTON", {KEY_MAP_OPTIONS}, &myConfig.key_XY_map, 26},
|
|
{"Y+B BUTTON", {KEY_MAP_OPTIONS}, &myConfig.key_YB_map, 26},
|
|
{"B+A BUTTON", {KEY_MAP_OPTIONS}, &myConfig.key_BA_map, 26},
|
|
{"CONTROLLER", {"LEFT/PLAYER1", "RIGHT/PLAYER2", "DUAL-ACTION A", "DUAL-ACTION B"}, &myConfig.controller_type, 4},
|
|
{"D-PAD", {"NORMAL", "SWAP LEFT/RGT", "SWAP UP/DOWN", "DIAGONALS", "STRICT 4-WAY"}, &myConfig.dpad_config, 5},
|
|
{"FRAMESKIP", {"OFF", "ON (ODD)", "ON (EVEN)"}, &myConfig.frame_skip, 3},
|
|
{"SOUND QUAL", {"LOW", "MEDIUM", "HIGH", "ULTIMATE"}, &myConfig.sound_quality, 4},
|
|
{"TGT SPEED", {"60 FPS (100%)","66 FPS (110%)","72 FPS (120%)","78 FPS (130%)","84 FPS (140%)","90 FPS (150%)","54 FPS (90%)","MAX SPEED"}, &myConfig.target_fps, 8},
|
|
{"PALETTE", {"ORIGINAL", "MUTED", "BRIGHT", "PAL", "CUSTOM"}, &myConfig.palette, 5},
|
|
{"KEYBD CLICK", {"NO" , "YES"}, &myConfig.key_click, 2},
|
|
{NULL, {"", ""}, NULL, 1},
|
|
},
|
|
|
|
// Page 2 options
|
|
{
|
|
{"BACKTAB", {"NOT LATCHED", "LATCHED"}, &myConfig.bLatched, 2},
|
|
{"GRAM SIZE", {"512B (NORMAL)", "2K (EXPANDED)"}, &myConfig.gramSize, 2},
|
|
{"CPU FUDGE", {"NONE", "LOW", "MEDIUM", "HIGH", "MAX"}, &myConfig.fudgeTiming, 5},
|
|
{"SKIP BLANKS", {"NO" , "YES"}, &myConfig.bSkipBlanks, 2},
|
|
{NULL, {"", ""}, NULL, 1},
|
|
},
|
|
|
|
// Global Options
|
|
{
|
|
{"FPS", {"OFF", "ON", "ON WITH DEBUG"}, &myGlobalConfig.show_fps, 3},
|
|
{"SAVE STATE", {"KEEP ON LOAD", "ERASE ON LOAD"}, &myGlobalConfig.erase_saves, 2},
|
|
{"BIOS DIR", {"SAME AS ROMS", "/ROMS/BIOS", "/ROMS/INTV/BIOS", "/DATA/BIOS"}, &myGlobalConfig.bios_dir, 4},
|
|
{"SAVE DIR", {"SAME AS ROMS", "/ROMS/SAV", "/ROMS/INTV/SAV", "/DATA/SAV"}, &myGlobalConfig.save_dir, 4},
|
|
{"OVL DIR", {"SAME AS ROMS", "/ROMS/OVL", "/ROMS/INTV/OVL", "/DATA/OVL"}, &myGlobalConfig.ovl_dir, 4},
|
|
{"ROM DIR", {"SAME AS EMU", "/ROMS", "/ROMS/INTV"}, &myGlobalConfig.rom_dir, 3},
|
|
{"MAN DIR", {"SAME AS ROMS", "/ROMS/MAN", "/ROMS/INTV/MAN", "/DATA/MAN"}, &myGlobalConfig.man_dir, 4},
|
|
{"START DEF", {KEY_MAP_OPTIONS}, &myGlobalConfig.key_START_map_default, 30},
|
|
{"SELECT DEF", {KEY_MAP_OPTIONS}, &myGlobalConfig.key_SELECT_map_default, 30},
|
|
{"DEF SOUND", {"LOW", "MEDIUM", "HIGH"}, &myGlobalConfig.def_sound_quality, 3},
|
|
{"DEF PALETTE", {"ORIGINAL", "MUTED", "BRIGHT", "PAL", "CUSTOM"}, &myGlobalConfig.def_palette, 5},
|
|
{"DEF FRAMSKP", {"OFF", "ON (ODD)", "ON (EVEN)"}, &myGlobalConfig.frame_skip, 3},
|
|
{"BRIGTNESS", {"MAX", "DIM", "DIMMER", "DIMEST"}, &myGlobalConfig.brightness, 4},
|
|
{"MENU COLOR", {"WHITE", "GREEN"}, &myGlobalConfig.menu_color, 2},
|
|
{NULL, {"", ""}, NULL, 1},
|
|
}
|
|
};
|
|
|
|
struct options_t *Current_Option_Table;
|
|
static char strBuf[36];
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// After settings hae changed, we call this to apply the new options to the game being played.
|
|
// This is also called when loading a game and after the configuration if read from NINTV-DS.DAT
|
|
// -------------------------------------------------------------------------------------------------
|
|
void ApplyOptions(void)
|
|
{
|
|
// Change the sound div if needed... affects sound quality and speed
|
|
extern INT32 clockDivisor, clocksPerSample;
|
|
static UINT32 sound_divs[] = {15, 12, 8, 4};
|
|
clockDivisor = sound_divs[myConfig.sound_quality];
|
|
|
|
if ((last_crc == 0xC2063C08) && (!isDSiMode())) // World Series Major League Baseball on DS-Lite/Phat needs a bit more help... lower sound quality a bit
|
|
clockDivisor = 24;
|
|
|
|
clocksPerSample = clockDivisor<<4;
|
|
|
|
// In case the sound changed... restart the audio processor
|
|
extern void audioRampDown(void);
|
|
audioRampDown();
|
|
bStartSoundFifo=true;
|
|
|
|
// clears the emulator side of the audio mixer
|
|
audioMixer->resetProcessor();
|
|
|
|
// Set the screen scaling options for the game selected
|
|
REG_BG3PA = myConfig.stretch_x;
|
|
REG_BG3X = (myConfig.offset_x) << 8;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// Display the current list of options: either the global list
|
|
// or the individual game list of options...
|
|
// ------------------------------------------------------------------
|
|
short int display_options_list(bool bFullDisplay)
|
|
{
|
|
short int len=0;
|
|
|
|
if (bFullDisplay)
|
|
{
|
|
Current_Option_Table = (options_t *)Option_Table[options_shown];
|
|
while (true)
|
|
{
|
|
sprintf(strBuf, " %-11s : %-15s", Current_Option_Table[len].label, Current_Option_Table[len].option[*(Current_Option_Table[len].option_val)]);
|
|
dsPrintValue(1,2+len, (len==0 ? 1:0), strBuf); len++;
|
|
if (Current_Option_Table[len].label == NULL) break;
|
|
}
|
|
|
|
// Blank out rest of the screen... option menus are of different lengths...
|
|
for (int i=len; i<22; i++)
|
|
{
|
|
dsPrintValue(1,2+i, 0, (char *)" ");
|
|
}
|
|
}
|
|
|
|
dsPrintValue(0,22, 0, (char *)"D-PAD TOGGLE. B=EXIT, START=SAVE");
|
|
dsPrintValue(0,23, 0, (char *)"X=SWAP KEYS. SELECT=MORE OPTIONS");
|
|
return len;
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Allows the user to move the cursor up and down through the various table
|
|
// enties above to select options for the game they wish to play.
|
|
// -----------------------------------------------------------------------------
|
|
void dsChooseOptions(int global)
|
|
{
|
|
short int optionHighlighted;
|
|
short int idx;
|
|
UINT8 bDone=false;
|
|
int keys_pressed;
|
|
short int last_keys_pressed = 999;
|
|
|
|
options_shown = global; // Always start with GAME options unless told otherwise
|
|
|
|
// Show the Options background...
|
|
dsShowBannerScreen();
|
|
|
|
idx=display_options_list(true);
|
|
optionHighlighted = 0;
|
|
while (!bDone)
|
|
{
|
|
keys_pressed = keysCurrent();
|
|
if (keys_pressed != last_keys_pressed)
|
|
{
|
|
last_keys_pressed = keys_pressed;
|
|
if (keysCurrent() & KEY_UP) // Previous option
|
|
{
|
|
sprintf(strBuf, " %-11s : %-15s", Current_Option_Table[optionHighlighted].label, Current_Option_Table[optionHighlighted].option[*(Current_Option_Table[optionHighlighted].option_val)]);
|
|
dsPrintValue(1,2+optionHighlighted,0, strBuf);
|
|
if (optionHighlighted > 0) optionHighlighted--; else optionHighlighted=(idx-1);
|
|
sprintf(strBuf, " %-11s : %-15s", Current_Option_Table[optionHighlighted].label, Current_Option_Table[optionHighlighted].option[*(Current_Option_Table[optionHighlighted].option_val)]);
|
|
dsPrintValue(1,2+optionHighlighted,1, strBuf);
|
|
}
|
|
if (keysCurrent() & KEY_DOWN) // Next option
|
|
{
|
|
sprintf(strBuf, " %-11s : %-15s", Current_Option_Table[optionHighlighted].label, Current_Option_Table[optionHighlighted].option[*(Current_Option_Table[optionHighlighted].option_val)]);
|
|
dsPrintValue(1,2+optionHighlighted,0, strBuf);
|
|
if (optionHighlighted < (idx-1)) optionHighlighted++; else optionHighlighted=0;
|
|
sprintf(strBuf, " %-11s : %-15s", Current_Option_Table[optionHighlighted].label, Current_Option_Table[optionHighlighted].option[*(Current_Option_Table[optionHighlighted].option_val)]);
|
|
dsPrintValue(1,2+optionHighlighted,1, strBuf);
|
|
}
|
|
|
|
if (keysCurrent() & KEY_RIGHT) // Toggle option clockwise
|
|
{
|
|
*(Current_Option_Table[optionHighlighted].option_val) = (*(Current_Option_Table[optionHighlighted].option_val) + 1) % Current_Option_Table[optionHighlighted].option_max;
|
|
sprintf(strBuf, " %-11s : %-15s", Current_Option_Table[optionHighlighted].label, Current_Option_Table[optionHighlighted].option[*(Current_Option_Table[optionHighlighted].option_val)]);
|
|
dsPrintValue(1,2+optionHighlighted,1, strBuf);
|
|
}
|
|
if (keysCurrent() & KEY_LEFT) // Toggle option counterclockwise
|
|
{
|
|
if ((*(Current_Option_Table[optionHighlighted].option_val)) == 0)
|
|
*(Current_Option_Table[optionHighlighted].option_val) = Current_Option_Table[optionHighlighted].option_max -1;
|
|
else
|
|
*(Current_Option_Table[optionHighlighted].option_val) = (*(Current_Option_Table[optionHighlighted].option_val) - 1) % Current_Option_Table[optionHighlighted].option_max;
|
|
sprintf(strBuf, " %-11s : %-15s", Current_Option_Table[optionHighlighted].label, Current_Option_Table[optionHighlighted].option[*(Current_Option_Table[optionHighlighted].option_val)]);
|
|
dsPrintValue(1,2+optionHighlighted,1, strBuf);
|
|
}
|
|
if (keysCurrent() & KEY_START) // Save Options
|
|
{
|
|
SaveConfig(currentRip ? currentRip->GetCRC():0x00000000, TRUE);
|
|
}
|
|
if (keysCurrent() & (KEY_SELECT | KEY_L | KEY_R)) // Toggle Between Option Pages
|
|
{
|
|
options_shown = (options_shown + 1) % 3;
|
|
idx=display_options_list(true);
|
|
optionHighlighted = 0;
|
|
}
|
|
if ((keysCurrent() & KEY_B) || (keysCurrent() & KEY_A)) // Exit options
|
|
{
|
|
break;
|
|
}
|
|
if (keysCurrent() & KEY_X) // Toggle the various ABXY button options (so we can quickly map into AD&D or Tron Deadly Discs, etc)
|
|
{
|
|
ToggleXYABasDirections();
|
|
idx=display_options_list(true);
|
|
optionHighlighted = 0;
|
|
WAITVBL;WAITVBL;WAITVBL;
|
|
}
|
|
}
|
|
swiWaitForVBlank();
|
|
}
|
|
|
|
ApplyOptions();
|
|
|
|
// Restore original bottom graphic
|
|
dsShowScreenMain(false, false);
|
|
|
|
// Give a third of a second time delay...
|
|
for (int i=0; i<20; i++)
|
|
{
|
|
swiWaitForVBlank();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// End of Line
|
|
|