// ===================================================================================== // Copyright (c) 2021 Dave Bernazzani (wavemotion-dave) // // Copying and distribution of this emulator, it's 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 #include #include #include #include #include "ds_tools.h" #include "savestate.h" #include "CRC32.h" #include "bgBottom.h" #include "config.h" #include "bgOptions.h" #include "Emulator.h" #include "Rip.h" struct Config_t myConfig; struct GlobalConfig_t myGlobalConfig; struct AllConfig_t allConfigs; extern AudioMixer *audioMixer; extern bool bStartSoundFifo; extern Rip *currentRip; UINT8 options_shown = 0; // Start with GAME config... can toggle to '1' for Global Options... int display_options_list(bool); 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.ecs_bios_filename, 0x00, 256); memset(myGlobalConfig.ivoice_bios_filename, 0x00, 256); myGlobalConfig.bios_dir = 0; myGlobalConfig.save_dir = 0; myGlobalConfig.show_fps = 0; myGlobalConfig.erase_saves = 0; myGlobalConfig.key_START_map_default = 11; myGlobalConfig.key_SELECT_map_default = 21; myGlobalConfig.rom_dir = 0; myGlobalConfig.def_sound_quality = (isDSiMode() ? 1:3); myGlobalConfig.man_dir = 0; myGlobalConfig.def_palette = 0; myGlobalConfig.brightness = 0; myGlobalConfig.spare3 = 0; myGlobalConfig.spare4 = 0; myGlobalConfig.spare5 = 1; myGlobalConfig.spare6 = 1; memset(myGlobalConfig.reserved, 0x00, 256); } static void SetDefaultGameConfig(void) { myConfig.game_crc = 0x00000000; myConfig.frame_skip_opt = 1; myConfig.overlay_selected = 0; myConfig.key_A_map = 12; myConfig.key_B_map = 12; myConfig.key_X_map = 13; myConfig.key_Y_map = 14; myConfig.key_L_map = 0; myConfig.key_R_map = 1; myConfig.key_START_map = myGlobalConfig.key_START_map_default; myConfig.key_SELECT_map = myGlobalConfig.key_SELECT_map_default; myConfig.controller_type = 0; myConfig.sound_clock_div = myGlobalConfig.def_sound_quality; myConfig.dpad_config = 0; myConfig.target_fps = 0; myConfig.spare0 = 0; myConfig.palette = myGlobalConfig.def_palette; myConfig.stretch_x = ((160 / 256) << 8) | (160 % 256); myConfig.offset_x = 0; myConfig.spare3 = 0; myConfig.spare4 = 0; myConfig.spare5 = 0; myConfig.key_AX_map = 0; myConfig.key_XY_map = 0; myConfig.key_YB_map = 0; myConfig.key_BA_map = 0; } // --------------------------------------------------------------------------- // Write out the XEGS.DAT configuration file to capture the settings for // each game. // --------------------------------------------------------------------------- void SaveConfig(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 (currentRip != NULL) { myConfig.game_crc = currentRip->GetCRC(); // Find the slot we should save into... for (slot=0; slotGetCRC()) // Got a match?! { memcpy(&myConfig, &allConfigs.game_config[slot], sizeof(struct Config_t)); if (myConfig.stretch_x < 0x50) myConfig.stretch_x = ((160 / 256) << 8) | (160 % 256); // If we haven't saved a stretch... set it to normal 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(); SaveConfig(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[25]; UINT8 *option_val; UINT8 option_max; }; const struct options_t Game_Option_Table[] = { {"OVERLAY", {"GENERIC", "CUSTOM", "MINOTAUR", "ADVENTURE", "ASTROSMASH", "SPACE SPARTANS", "B-17 BOMBER", "ATLANTIS", "BOMB SQUAD", "UTOPIA", "SWORDS & SERPT"},&myConfig.overlay_selected, 11}, {"A BUTTON", {"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", "R-ACT", "L-ACT", "RESET", "LOAD", "CONFIG", "SCORES", "QUIT", "STATE", "MENU", "SWITCH", "MANUAL"}, &myConfig.key_A_map, 24}, {"B BUTTON", {"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", "R-ACT", "L-ACT", "RESET", "LOAD", "CONFIG", "SCORES", "QUIT", "STATE", "MENU", "SWITCH", "MANUAL"}, &myConfig.key_B_map, 24}, {"X BUTTON", {"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", "R-ACT", "L-ACT", "RESET", "LOAD", "CONFIG", "SCORES", "QUIT", "STATE", "MENU", "SWITCH", "MANUAL"}, &myConfig.key_X_map, 24}, {"Y BUTTON", {"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", "R-ACT", "L-ACT", "RESET", "LOAD", "CONFIG", "SCORES", "QUIT", "STATE", "MENU", "SWITCH", "MANUAL"}, &myConfig.key_Y_map, 24}, {"L BUTTON", {"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", "R-ACT", "L-ACT", "RESET", "LOAD", "CONFIG", "SCORES", "QUIT", "STATE", "MENU", "SWITCH", "MANUAL"}, &myConfig.key_L_map, 24}, {"R BUTTON", {"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", "R-ACT", "L-ACT", "RESET", "LOAD", "CONFIG", "SCORES", "QUIT", "STATE", "MENU", "SWITCH", "MANUAL"}, &myConfig.key_R_map, 24}, {"START BTN", {"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", "R-ACT", "L-ACT", "RESET", "LOAD", "CONFIG", "SCORES", "QUIT", "STATE", "MENU", "SWITCH", "MANUAL"}, &myConfig.key_START_map, 24}, {"SELECT BTN", {"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", "R-ACT", "L-ACT", "RESET", "LOAD", "CONFIG", "SCORES", "QUIT", "STATE", "MENU", "SWITCH", "MANUAL"}, &myConfig.key_SELECT_map, 24}, {"A+X BUTTON", {"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", "R-ACT", "L-ACT", "RESET", "LOAD", "CONFIG", "SCORES", "QUIT", "STATE", "MENU", "SWITCH", "MANUAL"}, &myConfig.key_AX_map, 24}, {"X+Y BUTTON", {"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", "R-ACT", "L-ACT", "RESET", "LOAD", "CONFIG", "SCORES", "QUIT", "STATE", "MENU", "SWITCH", "MANUAL"}, &myConfig.key_XY_map, 24}, {"Y+B BUTTON", {"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", "R-ACT", "L-ACT", "RESET", "LOAD", "CONFIG", "SCORES", "QUIT", "STATE", "MENU", "SWITCH", "MANUAL"}, &myConfig.key_YB_map, 24}, {"B+A BUTTON", {"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", "R-ACT", "L-ACT", "RESET", "LOAD", "CONFIG", "SCORES", "QUIT", "STATE", "MENU", "SWITCH", "MANUAL"}, &myConfig.key_BA_map, 24}, {"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)", "AGRESSIVE"}, &myConfig.frame_skip_opt, 4}, {"SOUND DIV", {"12 (BEST)", "14 (EXELLENT)", "16 (GOOD)", "20 (AVERAGE)", "24 (FAIR)", "DISABLED"}, &myConfig.sound_clock_div, 6}, {"TGT SPEED", {"60 FPS (100%)", "66 FPS (110%)", "72 FPS (120%)", "78 FPS (130%)", "84 FPS (140%)", "90 FPS (150%)", "MAX SPEED"}, &myConfig.target_fps, 7}, {"PALETTE", {"ORIGINAL", "MUTED", "BRIGHT", "PAL", "CUSTOM"}, &myConfig.palette, 5}, {NULL, {"", ""}, NULL, 1}, }; const struct options_t Global_Option_Table[] = { {"FPS", {"OFF", "ON"}, &myGlobalConfig.show_fps, 2}, {"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-1", "KEY-2", "KEY-3", "KEY-4", "KEY-5", "KEY-6", "KEY-7", "KEY-8", "KEY-9", "KEY-CLR", "KEY-0", "KEY-ENT", "FIRE", "R-ACT", "L-ACT", "RESET", "LOAD", "CONFIG", "SCORES", "QUIT", "STATE", "MENU"}, &myGlobalConfig.key_START_map_default, 22}, {"SELECT DEF", {"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", "R-ACT", "L-ACT", "RESET", "LOAD", "CONFIG", "SCORES", "QUIT", "STATE", "MENU"}, &myGlobalConfig.key_SELECT_map_default, 22}, {"SOUND DEF", {"12 (BEST)", "14 (EXELLENT)", "16 (GOOD)", "20 (FAIR)", "24 (POOR)", "DISABLED"}, &myGlobalConfig.def_sound_quality, 6}, {"PALETTE DEF", {"ORIGINAL", "MUTED", "BRIGHT", "PAL", "CUSTOM"}, &myGlobalConfig.def_palette, 5}, {"BRIGTNESS", {"MAX", "DIM", "DIMMER", "DIMEST"}, &myGlobalConfig.brightness, 4}, {NULL, {"", ""}, NULL, 1}, }; struct options_t *Current_Option_Table; void ApplyOptions(void) { // Change the sound div if needed... affects sound quality and speed extern INT32 clockDivisor, clocksPerSample; static UINT32 sound_divs[] = {12,14,16,20,24,SOUND_DIV_DISABLE}; clockDivisor = sound_divs[myConfig.sound_clock_div]; clocksPerSample = clockDivisor<<4; // Check if the sound changed... fifoSendValue32(FIFO_USER_01,(1<<16) | SOUND_KILL); 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; } int display_options_list(bool bFullDisplay) { char strBuf[35]; int len=0; if (bFullDisplay) { Current_Option_Table = (options_shown==0 ? (options_t *)Game_Option_Table : (options_t *)Global_Option_Table); 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. A=EXIT, START=SAVE"); if (options_shown == 0) { dsPrintValue(0,23, 0, (char *)"PRESS SELECT FOR GLOBAL OPTIONS "); } else { dsPrintValue(0,23, 0, (char *)" PRESS SELECT FOR GAME 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(void) { int optionHighlighted; int idx; bool bDone=false; int keys_pressed; int last_keys_pressed = 999; char strBuf[35]; options_shown = 0; // Always start with GAME options // Show the Options background... decompress(bgOptionsTiles, bgGetGfxPtr(bg0b), LZ77Vram); decompress(bgOptionsMap, (void*) bgGetMapPtr(bg0b), LZ77Vram); dmaCopy((void *) bgOptionsPal,(u16*) BG_PALETTE_SUB,256*2); unsigned short dmaVal = *(bgGetMapPtr(bg1b) +31*32); dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2); 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(TRUE); } if (keysCurrent() & KEY_SELECT) // Toggle Between Game and Global Options { options_shown = (1-options_shown); idx=display_options_list(true); optionHighlighted = 0; } if ((keysCurrent() & KEY_B) || (keysCurrent() & KEY_A)) // Exit options { break; } } swiWaitForVBlank(); } ApplyOptions(); // Restore original bottom graphic dsShowScreenMain(false); // Give a third of a second time delay... for (int i=0; i<20; i++) { swiWaitForVBlank(); } return; } // End of Line